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
, BOOL
);
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
, FALSE
);
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
, FALSE
);
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
3223 * [I] space : VK_SPACE code sent
3226 * SUCCESS : TRUE (needs to be repainted)
3227 * FAILURE : FALSE (nothing has changed)
3229 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL space
)
3231 /* FIXME: pass in the state */
3232 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
3233 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
3234 BOOL bResult
= FALSE
;
3236 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem
, wShift
, wCtrl
);
3237 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3239 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
3242 LISTVIEW_SetSelection(infoPtr
, nItem
);
3249 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3254 lvItem
.state
= ~LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3255 lvItem
.stateMask
= LVIS_SELECTED
;
3258 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3259 if (lvItem
.state
& LVIS_SELECTED
)
3260 infoPtr
->nSelectionMark
= nItem
;
3262 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3267 LISTVIEW_SetSelection(infoPtr
, nItem
);
3270 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3273 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3277 static BOOL
LISTVIEW_GetItemAtPt(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, POINT pt
)
3279 LVHITTESTINFO lvHitTestInfo
;
3281 ZeroMemory(&lvHitTestInfo
, sizeof(lvHitTestInfo
));
3282 lvHitTestInfo
.pt
.x
= pt
.x
;
3283 lvHitTestInfo
.pt
.y
= pt
.y
;
3285 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
3287 lpLVItem
->mask
= LVIF_PARAM
;
3288 lpLVItem
->iItem
= lvHitTestInfo
.iItem
;
3289 lpLVItem
->iSubItem
= 0;
3291 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
3294 static inline BOOL
LISTVIEW_isHotTracking(const LISTVIEW_INFO
*infoPtr
)
3296 return ((infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) ||
3297 (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
) ||
3298 (infoPtr
->dwLvExStyle
& LVS_EX_TWOCLICKACTIVATE
));
3303 * Called when the mouse is being actively tracked and has hovered for a specified
3307 * [I] infoPtr : valid pointer to the listview structure
3308 * [I] fwKeys : key indicator
3309 * [I] x,y : mouse position
3312 * 0 if the message was processed, non-zero if there was an error
3315 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3316 * over the item for a certain period of time.
3319 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3321 if (LISTVIEW_isHotTracking(infoPtr
))
3329 if (LISTVIEW_GetItemAtPt(infoPtr
, &item
, pt
))
3330 LISTVIEW_SetSelection(infoPtr
, item
.iItem
);
3338 * Called whenever WM_MOUSEMOVE is received.
3341 * [I] infoPtr : valid pointer to the listview structure
3342 * [I] fwKeys : key indicator
3343 * [I] x,y : mouse position
3346 * 0 if the message is processed, non-zero if there was an error
3348 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3350 TRACKMOUSEEVENT trackinfo
;
3352 if (!(fwKeys
& MK_LBUTTON
))
3353 infoPtr
->bLButtonDown
= FALSE
;
3355 if (infoPtr
->bLButtonDown
)
3359 WORD wDragWidth
= GetSystemMetrics(SM_CXDRAG
);
3360 WORD wDragHeight
= GetSystemMetrics(SM_CYDRAG
);
3362 rect
.left
= infoPtr
->ptClickPos
.x
- wDragWidth
;
3363 rect
.right
= infoPtr
->ptClickPos
.x
+ wDragWidth
;
3364 rect
.top
= infoPtr
->ptClickPos
.y
- wDragHeight
;
3365 rect
.bottom
= infoPtr
->ptClickPos
.y
+ wDragHeight
;
3370 if (!PtInRect(&rect
, tmp
))
3372 LVHITTESTINFO lvHitTestInfo
;
3375 lvHitTestInfo
.pt
= infoPtr
->ptClickPos
;
3376 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
3378 ZeroMemory(&nmlv
, sizeof(nmlv
));
3379 nmlv
.iItem
= lvHitTestInfo
.iItem
;
3380 nmlv
.ptAction
= infoPtr
->ptClickPos
;
3382 if (!infoPtr
->bDragging
)
3384 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
3385 infoPtr
->bDragging
= TRUE
;
3392 infoPtr
->bLButtonDown
= FALSE
;
3394 /* see if we are supposed to be tracking mouse hovering */
3395 if (LISTVIEW_isHotTracking(infoPtr
)) {
3396 /* fill in the trackinfo struct */
3397 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3398 trackinfo
.dwFlags
= TME_QUERY
;
3399 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
3400 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
3402 /* see if we are already tracking this hwnd */
3403 _TrackMouseEvent(&trackinfo
);
3405 if(!(trackinfo
.dwFlags
& TME_HOVER
)) {
3406 trackinfo
.dwFlags
= TME_HOVER
;
3408 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3409 _TrackMouseEvent(&trackinfo
);
3418 * Tests whether the item is assignable to a list with style lStyle
3420 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
3422 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
3423 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
3424 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
3432 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3435 * [I] infoPtr : valid pointer to the listview structure
3436 * [I] lpLVItem : valid pointer to new item attributes
3437 * [I] isNew : the item being set is being inserted
3438 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3439 * [O] bChanged : will be set to TRUE if the item really changed
3445 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
3447 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3455 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
3457 if (lpLVItem
->mask
== 0) return TRUE
;
3459 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
3461 /* a virtual listview only stores selection and focus */
3462 if (lpLVItem
->mask
& ~LVIF_STATE
)
3468 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3469 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
3473 /* we need to get the lParam and state of the item */
3474 item
.iItem
= lpLVItem
->iItem
;
3475 item
.iSubItem
= lpLVItem
->iSubItem
;
3476 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
3477 item
.stateMask
= ~0;
3480 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
3482 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
3483 /* determine what fields will change */
3484 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
))
3485 uChanged
|= LVIF_STATE
;
3487 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
3488 uChanged
|= LVIF_IMAGE
;
3490 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
3491 uChanged
|= LVIF_PARAM
;
3493 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
3494 uChanged
|= LVIF_INDENT
;
3496 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
3497 uChanged
|= LVIF_TEXT
;
3499 TRACE("uChanged=0x%x\n", uChanged
);
3500 if (!uChanged
) return TRUE
;
3503 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
3504 nmlv
.iItem
= lpLVItem
->iItem
;
3505 nmlv
.uNewState
= (item
.state
& ~lpLVItem
->stateMask
) | (lpLVItem
->state
& lpLVItem
->stateMask
);
3506 nmlv
.uOldState
= item
.state
;
3507 nmlv
.uChanged
= uChanged
;
3508 nmlv
.lParam
= item
.lParam
;
3510 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3511 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3513 if(lpItem
&& !isNew
&& infoPtr
->bDoChangeNotify
)
3515 HWND hwndSelf
= infoPtr
->hwndSelf
;
3517 if (notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
3519 if (!IsWindow(hwndSelf
))
3523 /* copy information */
3524 if (lpLVItem
->mask
& LVIF_TEXT
)
3525 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3527 if (lpLVItem
->mask
& LVIF_IMAGE
)
3528 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
3530 if (lpLVItem
->mask
& LVIF_PARAM
)
3531 lpItem
->lParam
= lpLVItem
->lParam
;
3533 if (lpLVItem
->mask
& LVIF_INDENT
)
3534 lpItem
->iIndent
= lpLVItem
->iIndent
;
3536 if (uChanged
& LVIF_STATE
)
3538 if (lpItem
&& (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& ~(LVIS_FOCUSED
| LVIS_SELECTED
)))
3540 lpItem
->state
&= ~lpLVItem
->stateMask
;
3541 lpItem
->state
|= (lpLVItem
->state
& lpLVItem
->stateMask
);
3543 if (lpLVItem
->state
& lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3545 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
3546 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3548 else if (lpLVItem
->stateMask
& LVIS_SELECTED
)
3549 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3551 /* if we are asked to change focus, and we manage it, do it */
3552 if (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
3554 if (lpLVItem
->state
& LVIS_FOCUSED
)
3556 LISTVIEW_SetItemFocus(infoPtr
, -1);
3557 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
3558 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, uView
== LVS_LIST
);
3560 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
3561 infoPtr
->nFocusedItem
= -1;
3565 /* if we're inserting the item, we're done */
3566 if (isNew
) return TRUE
;
3568 /* send LVN_ITEMCHANGED notification */
3569 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
3570 if (infoPtr
->bDoChangeNotify
) notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
3577 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3580 * [I] infoPtr : valid pointer to the listview structure
3581 * [I] lpLVItem : valid pointer to new subitem attributes
3582 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3583 * [O] bChanged : will be set to TRUE if the item really changed
3589 static BOOL
set_sub_item(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
3592 SUBITEM_INFO
*lpSubItem
;
3594 /* we do not support subitems for virtual listviews */
3595 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
3597 /* set subitem only if column is present */
3598 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
3600 /* First do some sanity checks */
3601 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3602 particularly useful. We currently do not actually do anything with
3603 the flag on subitems.
3605 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
)) return FALSE
;
3606 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
))) return TRUE
;
3608 /* get the subitem structure, and create it if not there */
3609 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3610 assert (hdpaSubItems
);
3612 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
3615 SUBITEM_INFO
*tmpSubItem
;
3618 lpSubItem
= Alloc(sizeof(SUBITEM_INFO
));
3619 if (!lpSubItem
) return FALSE
;
3620 /* we could binary search here, if need be...*/
3621 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
3623 tmpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
3624 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
3626 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
3631 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
3632 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
3636 if (lpLVItem
->mask
& LVIF_IMAGE
)
3637 if (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
)
3639 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
3643 if (lpLVItem
->mask
& LVIF_TEXT
)
3644 if (lpSubItem
->hdr
.pszText
!= lpLVItem
->pszText
)
3646 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3655 * Sets item attributes.
3658 * [I] infoPtr : valid pointer to the listview structure
3659 * [I] lpLVItem : new item attributes
3660 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3666 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, LVITEMW
*lpLVItem
, BOOL isW
)
3668 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3669 HWND hwndSelf
= infoPtr
->hwndSelf
;
3670 LPWSTR pszText
= NULL
;
3671 BOOL bResult
, bChanged
= FALSE
;
3673 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
3675 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
3678 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3679 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_textW(lpLVItem
->pszText
))
3681 pszText
= lpLVItem
->pszText
;
3682 lpLVItem
->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
3685 /* actually set the fields */
3686 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
3688 if (lpLVItem
->iSubItem
)
3689 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
3691 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
3692 if (!IsWindow(hwndSelf
))
3695 /* redraw item, if necessary */
3696 if (bChanged
&& !infoPtr
->bIsDrawing
)
3698 /* this little optimization eliminates some nasty flicker */
3699 if ( uView
== LVS_REPORT
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
3700 !(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) &&
3701 lpLVItem
->iSubItem
> 0 && lpLVItem
->iSubItem
<= DPA_GetPtrCount(infoPtr
->hdpaColumns
) )
3702 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
3704 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
3709 textfreeT(lpLVItem
->pszText
, isW
);
3710 lpLVItem
->pszText
= pszText
;
3718 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3721 * [I] infoPtr : valid pointer to the listview structure
3726 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*infoPtr
)
3728 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3730 SCROLLINFO scrollInfo
;
3732 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3733 scrollInfo
.fMask
= SIF_POS
;
3735 if (uView
== LVS_LIST
)
3737 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
3738 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
3740 else if (uView
== LVS_REPORT
)
3742 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3743 nItem
= scrollInfo
.nPos
;
3747 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3748 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
3751 TRACE("nItem=%d\n", nItem
);
3759 * Erases the background of the given rectangle
3762 * [I] infoPtr : valid pointer to the listview structure
3763 * [I] hdc : device context handle
3764 * [I] lprcBox : clipping rectangle
3770 static inline BOOL
LISTVIEW_FillBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
3772 if (!infoPtr
->hBkBrush
) return FALSE
;
3774 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, wine_dbgstr_rect(lprcBox
), infoPtr
->hBkBrush
);
3776 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
3784 * [I] infoPtr : valid pointer to the listview structure
3785 * [I] hdc : device context handle
3786 * [I] nItem : item index
3787 * [I] nSubItem : subitem index
3788 * [I] pos : item position in client coordinates
3789 * [I] cdmode : custom draw mode
3795 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, INT nSubItem
, POINT pos
, DWORD cdmode
)
3797 UINT uFormat
, uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3798 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
3799 static WCHAR szCallback
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3800 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
3802 RECT rcSelect
, rcBox
, rcIcon
, rcLabel
, rcStateIcon
;
3803 NMLVCUSTOMDRAW nmlvcd
;
3808 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc
, nItem
, nSubItem
, wine_dbgstr_point(&pos
));
3810 /* get information needed for drawing the item */
3811 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_PARAM
;
3812 if (nSubItem
== 0) lvItem
.mask
|= LVIF_STATE
;
3813 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
3814 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
;
3815 lvItem
.iItem
= nItem
;
3816 lvItem
.iSubItem
= nSubItem
;
3819 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3820 lvItem
.pszText
= szDispText
;
3821 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
3822 if (nSubItem
> 0 && (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3823 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3824 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= szCallback
;
3825 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3827 /* now check if we need to update the focus rectangle */
3828 lprcFocus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
3830 if (!lprcFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
3831 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcSelect
, &rcIcon
, &rcStateIcon
, &rcLabel
);
3832 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
3833 OffsetRect(&rcSelect
, pos
.x
, pos
.y
);
3834 OffsetRect(&rcIcon
, pos
.x
, pos
.y
);
3835 OffsetRect(&rcStateIcon
, pos
.x
, pos
.y
);
3836 OffsetRect(&rcLabel
, pos
.x
, pos
.y
);
3837 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3838 wine_dbgstr_rect(&rcBox
), wine_dbgstr_rect(&rcSelect
),
3839 wine_dbgstr_rect(&rcIcon
), wine_dbgstr_rect(&rcLabel
));
3841 /* fill in the custom draw structure */
3842 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
3844 hOldFont
= GetCurrentObject(hdc
, OBJ_FONT
);
3845 if (nSubItem
> 0) cdmode
= infoPtr
->cditemmode
;
3846 if (cdmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3847 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3848 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3849 if (nSubItem
== 0) infoPtr
->cditemmode
= cdsubitemmode
;
3850 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3851 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3852 if (nSubItem
== 0 && cdsubitemmode
== CDRF_NOTIFYITEMDRAW
)
3854 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
3855 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3857 if (nSubItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
3858 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
3859 else if ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) == FALSE
)
3860 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, TRUE
);
3862 /* in full row select, subitems, will just use main item's colors */
3863 if (nSubItem
&& uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3864 nmlvcd
.clrTextBk
= CLR_NONE
;
3867 if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && (nSubItem
== 0))
3869 UINT uStateImage
= STATEIMAGEINDEX(lvItem
.state
);
3872 TRACE("uStateImage=%d\n", uStateImage
);
3873 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
,
3874 rcStateIcon
.left
, rcStateIcon
.top
, ILD_NORMAL
);
3879 himl
= (uView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
3880 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
))
3882 TRACE("iImage=%d\n", lvItem
.iImage
);
3883 ImageList_DrawEx(himl
, lvItem
.iImage
, hdc
, rcIcon
.left
, rcIcon
.top
,
3884 rcIcon
.right
- rcIcon
.left
, rcIcon
.bottom
- rcIcon
.top
, infoPtr
->clrBk
, CLR_DEFAULT
,
3885 (lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
) ? ILD_SELECTED
: ILD_NORMAL
);
3888 /* Don't bother painting item being edited */
3889 if (infoPtr
->hwndEdit
&& nItem
== infoPtr
->nEditLabelItem
&& nSubItem
== 0) goto postpaint
;
3891 /* FIXME: temporary hack */
3892 rcSelect
.left
= rcLabel
.left
;
3894 /* draw the selection background, if we're drawing the main item */
3897 /* in icon mode, the label rect is really what we want to draw the
3899 if (uView
== LVS_ICON
)
3902 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3903 rcSelect
.right
= rcBox
.right
;
3905 if (nmlvcd
.clrTextBk
!= CLR_NONE
)
3906 ExtTextOutW(hdc
, rcSelect
.left
, rcSelect
.top
, ETO_OPAQUE
, &rcSelect
, 0, 0, 0);
3907 if(lprcFocus
) *lprcFocus
= rcSelect
;
3910 /* figure out the text drawing flags */
3911 uFormat
= (uView
== LVS_ICON
? (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
3912 if (uView
== LVS_ICON
)
3913 uFormat
= (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
3916 switch (LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
3918 case LVCFMT_RIGHT
: uFormat
|= DT_RIGHT
; break;
3919 case LVCFMT_CENTER
: uFormat
|= DT_CENTER
; break;
3920 default: uFormat
|= DT_LEFT
;
3923 if (!(uFormat
& (DT_RIGHT
| DT_CENTER
)))
3925 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
3926 else rcLabel
.left
+= LABEL_HOR_PADDING
;
3928 else if (uFormat
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
3930 /* for GRIDLINES reduce the bottom so the text formats correctly */
3931 if (uView
== LVS_REPORT
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
3934 DrawTextW(hdc
, lvItem
.pszText
, -1, &rcLabel
, uFormat
);
3937 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
3938 notify_postpaint(infoPtr
, &nmlvcd
);
3939 if (cdsubitemmode
& CDRF_NEWFONT
)
3940 SelectObject(hdc
, hOldFont
);
3946 * Draws listview items when in owner draw mode.
3949 * [I] infoPtr : valid pointer to the listview structure
3950 * [I] hdc : device context handle
3955 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3957 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
3958 DWORD cditemmode
= CDRF_DODEFAULT
;
3959 NMLVCUSTOMDRAW nmlvcd
;
3960 POINT Origin
, Position
;
3966 ZeroMemory(&dis
, sizeof(dis
));
3968 /* Get scroll info once before loop */
3969 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3971 /* iterate through the invalidated rows */
3972 while(iterator_next(i
))
3974 item
.iItem
= i
->nItem
;
3976 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
3977 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
3978 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
3980 dis
.CtlType
= ODT_LISTVIEW
;
3982 dis
.itemID
= item
.iItem
;
3983 dis
.itemAction
= ODA_DRAWENTIRE
;
3985 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
3986 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
3987 dis
.hwndItem
= infoPtr
->hwndSelf
;
3989 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
3990 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
3991 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
3992 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
3993 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
3994 dis
.itemData
= item
.lParam
;
3996 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), wine_dbgstr_rect(&dis
.rcItem
));
3999 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4000 * structure for the rest. of the paint cycle
4002 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
4003 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
4004 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4006 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
4008 prepaint_setup (infoPtr
, hdc
, &nmlvcd
, FALSE
);
4009 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
4012 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
4013 notify_postpaint(infoPtr
, &nmlvcd
);
4019 * Draws listview items when in report display mode.
4022 * [I] infoPtr : valid pointer to the listview structure
4023 * [I] hdc : device context handle
4024 * [I] cdmode : custom draw mode
4029 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4032 RECT rcClip
, rcItem
;
4033 POINT Origin
, Position
;
4039 /* figure out what to draw */
4040 rgntype
= GetClipBox(hdc
, &rcClip
);
4041 if (rgntype
== NULLREGION
) return;
4043 /* Get scroll info once before loop */
4044 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4046 /* narrow down the columns we need to paint */
4047 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
4049 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
4050 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
4052 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
4054 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
4055 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
4057 iterator_rangeitems(&j
, colRange
);
4059 /* in full row select, we _have_ to draw the main item */
4060 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4063 /* iterate through the invalidated rows */
4064 while(iterator_next(i
))
4066 /* iterate through the invalidated columns */
4067 while(iterator_next(&j
))
4069 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4070 Position
.x
+= Origin
.x
;
4071 Position
.y
+= Origin
.y
;
4073 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
4075 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4077 rcItem
.bottom
= infoPtr
->nItemHeight
;
4078 OffsetRect(&rcItem
, Position
.x
, Position
.y
);
4079 if (!RectVisible(hdc
, &rcItem
)) continue;
4082 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, j
.nItem
, Position
, cdmode
);
4085 iterator_destroy(&j
);
4090 * Draws the gridlines if necessary when in report display mode.
4093 * [I] infoPtr : valid pointer to the listview structure
4094 * [I] hdc : device context handle
4099 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
4104 RECT rcClip
, rcItem
;
4111 /* figure out what to draw */
4112 rgntype
= GetClipBox(hdc
, &rcClip
);
4113 if (rgntype
== NULLREGION
) return;
4115 /* Get scroll info once before loop */
4116 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4118 /* narrow down the columns we need to paint */
4119 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
4121 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
4122 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
4124 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
4126 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
4127 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
4129 iterator_rangeitems(&j
, colRange
);
4131 if ((hPen
= CreatePen( PS_SOLID
, 1, comctl32_color
.clr3dFace
)))
4133 hOldPen
= SelectObject ( hdc
, hPen
);
4135 /* draw the vertical lines for the columns */
4136 iterator_rangeitems(&j
, colRange
);
4137 while(iterator_next(&j
))
4139 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4140 if (rcItem
.left
== 0) continue; /* skip first column */
4141 rcItem
.left
+= Origin
.x
;
4142 rcItem
.right
+= Origin
.x
;
4143 rcItem
.top
= infoPtr
->rcList
.top
;
4144 rcItem
.bottom
= infoPtr
->rcList
.bottom
;
4145 TRACE("vert col=%d, rcItem=%s\n", j
.nItem
, wine_dbgstr_rect(&rcItem
));
4146 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4147 LineTo (hdc
, rcItem
.left
, rcItem
.bottom
);
4149 iterator_destroy(&j
);
4151 /* draw the horizontial lines for the rows */
4152 itemheight
= LISTVIEW_CalculateItemHeight(infoPtr
);
4153 rcItem
.left
= infoPtr
->rcList
.left
+ Origin
.x
;
4154 rcItem
.right
= infoPtr
->rcList
.right
+ Origin
.x
;
4155 rcItem
.bottom
= rcItem
.top
= Origin
.y
- 1;
4156 MoveToEx(hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4157 LineTo(hdc
, rcItem
.right
, rcItem
.top
);
4158 for(y
=itemheight
-1+Origin
.y
; y
<=infoPtr
->rcList
.bottom
; y
+=itemheight
)
4160 rcItem
.bottom
= rcItem
.top
= y
;
4161 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem
));
4162 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4163 LineTo (hdc
, rcItem
.right
, rcItem
.top
);
4166 SelectObject( hdc
, hOldPen
);
4167 DeleteObject( hPen
);
4173 * Draws listview items when in list display mode.
4176 * [I] infoPtr : valid pointer to the listview structure
4177 * [I] hdc : device context handle
4178 * [I] cdmode : custom draw mode
4183 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4185 POINT Origin
, Position
;
4187 /* Get scroll info once before loop */
4188 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4190 while(iterator_prev(i
))
4192 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4193 Position
.x
+= Origin
.x
;
4194 Position
.y
+= Origin
.y
;
4196 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, 0, Position
, cdmode
);
4203 * Draws listview items.
4206 * [I] infoPtr : valid pointer to the listview structure
4207 * [I] hdc : device context handle
4208 * [I] prcErase : rect to be erased before refresh (may be NULL)
4213 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*prcErase
)
4215 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4216 COLORREF oldTextColor
= 0, oldBkColor
= 0, oldClrTextBk
, oldClrText
;
4217 NMLVCUSTOMDRAW nmlvcd
;
4224 HBITMAP hbmp
= NULL
;
4226 LISTVIEW_DUMP(infoPtr
);
4228 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
4229 TRACE("double buffering\n");
4231 hdc
= CreateCompatibleDC(hdcOrig
);
4233 ERR("Failed to create DC for backbuffer\n");
4236 hbmp
= CreateCompatibleBitmap(hdcOrig
, infoPtr
->rcList
.right
,
4237 infoPtr
->rcList
.bottom
);
4239 ERR("Failed to create bitmap for backbuffer\n");
4244 SelectObject(hdc
, hbmp
);
4245 SelectObject(hdc
, infoPtr
->hFont
);
4247 /* Save dc values we're gonna trash while drawing
4248 * FIXME: Should be done in LISTVIEW_DrawItem() */
4249 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
4250 oldBkMode
= GetBkMode(hdc
);
4251 oldBkColor
= GetBkColor(hdc
);
4252 oldTextColor
= GetTextColor(hdc
);
4255 infoPtr
->bIsDrawing
= TRUE
;
4258 LISTVIEW_FillBkgnd(infoPtr
, hdc
, prcErase
);
4259 } else if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
4260 /* If no erasing was done (usually because RedrawWindow was called
4261 * with RDW_INVALIDATE only) we need to copy the old contents into
4262 * the backbuffer before continuing. */
4263 BitBlt(hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
4264 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
4265 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
4266 hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
4269 /* FIXME: Shouldn't need to do this */
4270 oldClrTextBk
= infoPtr
->clrTextBk
;
4271 oldClrText
= infoPtr
->clrText
;
4273 infoPtr
->cditemmode
= CDRF_DODEFAULT
;
4275 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
4276 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
4277 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4278 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
4279 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
4281 /* Use these colors to draw the items */
4282 infoPtr
->clrTextBk
= nmlvcd
.clrTextBk
;
4283 infoPtr
->clrText
= nmlvcd
.clrText
;
4285 /* nothing to draw */
4286 if(infoPtr
->nItemCount
== 0) goto enddraw
;
4288 /* figure out what we need to draw */
4289 iterator_visibleitems(&i
, infoPtr
, hdc
);
4291 /* send cache hint notification */
4292 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4294 RANGE range
= iterator_range(&i
);
4297 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
4298 nmlv
.iFrom
= range
.lower
;
4299 nmlv
.iTo
= range
.upper
- 1;
4300 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
4303 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
4304 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
4307 if (uView
== LVS_REPORT
)
4308 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
4309 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4310 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
4312 /* if we have a focus rect, draw it */
4313 if (infoPtr
->bFocus
)
4314 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
4316 iterator_destroy(&i
);
4319 /* For LVS_EX_GRIDLINES go and draw lines */
4320 /* This includes the case where there were *no* items */
4321 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
&&
4322 infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
4323 LISTVIEW_RefreshReportGrid(infoPtr
, hdc
);
4325 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
4326 notify_postpaint(infoPtr
, &nmlvcd
);
4328 infoPtr
->clrTextBk
= oldClrTextBk
;
4329 infoPtr
->clrText
= oldClrText
;
4332 BitBlt(hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
4333 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
4334 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
4335 hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
4340 SelectObject(hdc
, hOldFont
);
4341 SetBkMode(hdc
, oldBkMode
);
4342 SetBkColor(hdc
, oldBkColor
);
4343 SetTextColor(hdc
, oldTextColor
);
4346 infoPtr
->bIsDrawing
= FALSE
;
4352 * Calculates the approximate width and height of a given number of items.
4355 * [I] infoPtr : valid pointer to the listview structure
4356 * [I] nItemCount : number of items
4357 * [I] wWidth : width
4358 * [I] wHeight : height
4361 * Returns a DWORD. The width in the low word and the height in high word.
4363 static DWORD
LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
4364 WORD wWidth
, WORD wHeight
)
4366 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4367 INT nItemCountPerColumn
= 1;
4368 INT nColumnCount
= 0;
4369 DWORD dwViewRect
= 0;
4371 if (nItemCount
== -1)
4372 nItemCount
= infoPtr
->nItemCount
;
4374 if (uView
== LVS_LIST
)
4376 if (wHeight
== 0xFFFF)
4378 /* use current height */
4379 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4382 if (wHeight
< infoPtr
->nItemHeight
)
4383 wHeight
= infoPtr
->nItemHeight
;
4387 if (infoPtr
->nItemHeight
> 0)
4389 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
4390 if (nItemCountPerColumn
== 0)
4391 nItemCountPerColumn
= 1;
4393 if (nItemCount
% nItemCountPerColumn
!= 0)
4394 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
4396 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
4400 /* Microsoft padding magic */
4401 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
4402 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
4404 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4406 else if (uView
== LVS_REPORT
)
4410 if (infoPtr
->nItemCount
> 0)
4412 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
4413 wWidth
= rcBox
.right
- rcBox
.left
;
4414 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
4418 /* use current height and width */
4419 if (wHeight
== 0xffff)
4420 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4421 if (wWidth
== 0xffff)
4422 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
4425 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4427 else if (uView
== LVS_SMALLICON
)
4428 FIXME("uView == LVS_SMALLICON: not implemented\n");
4429 else if (uView
== LVS_ICON
)
4430 FIXME("uView == LVS_ICON: not implemented\n");
4438 * Create a drag image list for the specified item.
4441 * [I] infoPtr : valid pointer to the listview structure
4442 * [I] iItem : index of item
4443 * [O] lppt : Upperr-left corner of the image
4446 * Returns a handle to the image list if successful, NULL otherwise.
4448 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
4454 HBITMAP hbmp
, hOldbmp
;
4455 HIMAGELIST dragList
= 0;
4456 TRACE("iItem=%d Count=%d\n", iItem
, infoPtr
->nItemCount
);
4458 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
)
4461 rcItem
.left
= LVIR_BOUNDS
;
4462 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
4465 lppt
->x
= rcItem
.left
;
4466 lppt
->y
= rcItem
.top
;
4468 size
.cx
= rcItem
.right
- rcItem
.left
;
4469 size
.cy
= rcItem
.bottom
- rcItem
.top
;
4471 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
4472 hdc
= CreateCompatibleDC(hdcOrig
);
4473 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
4474 hOldbmp
= SelectObject(hdc
, hbmp
);
4476 rcItem
.left
= rcItem
.top
= 0;
4477 rcItem
.right
= size
.cx
;
4478 rcItem
.bottom
= size
.cy
;
4479 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
4482 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, 0, pos
, infoPtr
->cditemmode
))
4484 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
4485 SelectObject(hdc
, hOldbmp
);
4486 ImageList_Add(dragList
, hbmp
, 0);
4489 SelectObject(hdc
, hOldbmp
);
4493 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
4495 TRACE("ret=%p\n", dragList
);
4503 * Removes all listview items and subitems.
4506 * [I] infoPtr : valid pointer to the listview structure
4512 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
, BOOL destroy
)
4515 HDPA hdpaSubItems
= NULL
;
4522 /* we do it directly, to avoid notifications */
4523 ranges_clear(infoPtr
->selectionRanges
);
4524 infoPtr
->nSelectionMark
= -1;
4525 infoPtr
->nFocusedItem
= -1;
4526 SetRectEmpty(&infoPtr
->rcFocus
);
4527 /* But we are supposed to leave nHotItem as is! */
4530 /* send LVN_DELETEALLITEMS notification */
4531 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4533 bSuppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
4535 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
4537 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4539 /* send LVN_DELETEITEM notification, if not suppressed
4540 and if it is not a virtual listview */
4541 if (!bSuppress
) notify_deleteitem(infoPtr
, i
);
4542 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
4543 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
4545 hdrItem
= DPA_GetPtr(hdpaSubItems
, j
);
4546 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4549 DPA_Destroy(hdpaSubItems
);
4550 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
4552 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
4553 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
4554 infoPtr
->nItemCount
--;
4559 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4560 LISTVIEW_UpdateScroll(infoPtr
);
4562 LISTVIEW_InvalidateList(infoPtr
);
4569 * Scrolls, and updates the columns, when a column is changing width.
4572 * [I] infoPtr : valid pointer to the listview structure
4573 * [I] nColumn : column to scroll
4574 * [I] dx : amount of scroll, in pixels
4579 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
4581 COLUMN_INFO
*lpColumnInfo
;
4586 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
4587 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
4588 rcCol
= lpColumnInfo
->rcHeader
;
4589 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
4590 rcCol
.left
= rcCol
.right
;
4592 /* adjust the other columns */
4593 for (nCol
= nColumn
; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
4595 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
4596 lpColumnInfo
->rcHeader
.left
+= dx
;
4597 lpColumnInfo
->rcHeader
.right
+= dx
;
4600 /* do not update screen if not in report mode */
4601 if (!is_redrawing(infoPtr
) || (infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return;
4603 /* if we have a focus, we must first erase the focus rect */
4604 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
4606 /* Need to reset the item width when inserting a new column */
4607 infoPtr
->nItemWidth
+= dx
;
4609 LISTVIEW_UpdateScroll(infoPtr
);
4610 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
4612 /* scroll to cover the deleted column, and invalidate for redraw */
4613 rcOld
= infoPtr
->rcList
;
4614 rcOld
.left
= ptOrigin
.x
+ rcCol
.left
+ dx
;
4615 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4617 /* we can restore focus now */
4618 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
4623 * Removes a column from the listview control.
4626 * [I] infoPtr : valid pointer to the listview structure
4627 * [I] nColumn : column index
4633 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4637 TRACE("nColumn=%d\n", nColumn
);
4639 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0
4640 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4642 /* While the MSDN specifically says that column zero should not be deleted,
4643 what actually happens is that the column itself is deleted but no items or subitems
4647 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
4649 if (!Header_DeleteItem(infoPtr
->hwndHeader
, nColumn
))
4652 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
4653 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
4655 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
4657 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
4659 INT nItem
, nSubItem
, i
;
4661 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
4663 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
4666 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4668 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
4669 if (lpSubItem
->iSubItem
== nColumn
)
4672 lpDelItem
= lpSubItem
;
4674 else if (lpSubItem
->iSubItem
> nColumn
)
4676 lpSubItem
->iSubItem
--;
4680 /* if we found our subitem, zapp it */
4684 if (is_textW(lpDelItem
->hdr
.pszText
))
4685 Free(lpDelItem
->hdr
.pszText
);
4690 /* free dpa memory */
4691 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
4696 /* update the other column info */
4697 LISTVIEW_UpdateItemSize(infoPtr
);
4698 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
4699 LISTVIEW_InvalidateList(infoPtr
);
4701 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
4708 * Invalidates the listview after an item's insertion or deletion.
4711 * [I] infoPtr : valid pointer to the listview structure
4712 * [I] nItem : item index
4713 * [I] dir : -1 if deleting, 1 if inserting
4718 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
4720 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4721 INT nPerCol
, nItemCol
, nItemRow
;
4725 /* if we don't refresh, what's the point of scrolling? */
4726 if (!is_redrawing(infoPtr
)) return;
4728 assert (abs(dir
) == 1);
4730 /* arrange icons if autoarrange is on */
4731 if (is_autoarrange(infoPtr
))
4733 BOOL arrange
= TRUE
;
4734 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
4735 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
4736 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4739 /* scrollbars need updating */
4740 LISTVIEW_UpdateScroll(infoPtr
);
4742 /* figure out the item's position */
4743 if (uView
== LVS_REPORT
)
4744 nPerCol
= infoPtr
->nItemCount
+ 1;
4745 else if (uView
== LVS_LIST
)
4746 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
4747 else /* LVS_ICON, or LVS_SMALLICON */
4750 nItemCol
= nItem
/ nPerCol
;
4751 nItemRow
= nItem
% nPerCol
;
4752 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4754 /* move the items below up a slot */
4755 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
4756 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
4757 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
4758 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4759 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4760 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
4761 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4763 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll
), wine_dbgstr_rect(&infoPtr
->rcList
));
4764 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4765 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4768 /* report has only that column, so we're done */
4769 if (uView
== LVS_REPORT
) return;
4771 /* now for LISTs, we have to deal with the columns to the right */
4772 rcScroll
.left
= (nItemCol
+ 1) * infoPtr
->nItemWidth
;
4774 rcScroll
.right
= (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
4775 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4776 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4777 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4778 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4779 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4784 * Removes an item from the listview control.
4787 * [I] infoPtr : valid pointer to the listview structure
4788 * [I] nItem : item index
4794 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
4797 const UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4798 const BOOL is_icon
= (uView
== LVS_SMALLICON
|| uView
== LVS_ICON
);
4800 TRACE("(nItem=%d)\n", nItem
);
4802 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
4804 /* remove selection, and focus */
4806 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4807 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
4809 /* send LVN_DELETEITEM notification. */
4810 if (!notify_deleteitem(infoPtr
, nItem
)) return FALSE
;
4812 /* we need to do this here, because we'll be deleting stuff */
4814 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4816 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4822 hdpaSubItems
= DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
4823 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4825 hdrItem
= DPA_GetPtr(hdpaSubItems
, i
);
4826 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4829 DPA_Destroy(hdpaSubItems
);
4834 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
4835 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
4838 infoPtr
->nItemCount
--;
4839 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
4841 /* now is the invalidation fun */
4843 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
4850 * Callback implementation for editlabel control
4853 * [I] infoPtr : valid pointer to the listview structure
4854 * [I] pszText : modified text
4855 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4861 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, LPWSTR pszText
, BOOL isW
)
4863 HWND hwndSelf
= infoPtr
->hwndSelf
;
4864 NMLVDISPINFOW dispInfo
;
4865 INT editedItem
= infoPtr
->nEditLabelItem
;
4868 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
4870 infoPtr
->nEditLabelItem
= -1;
4872 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4873 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4874 dispInfo
.item
.iItem
= editedItem
;
4875 dispInfo
.item
.iSubItem
= 0;
4876 dispInfo
.item
.stateMask
= ~0;
4877 if (!LISTVIEW_GetItemW(infoPtr
, &dispInfo
.item
)) return FALSE
;
4880 bSame
= (lstrcmpW(dispInfo
.item
.pszText
, pszText
) == 0);
4883 LPWSTR tmp
= textdupTtoW(pszText
, FALSE
);
4884 bSame
= (lstrcmpW(dispInfo
.item
.pszText
, tmp
) == 0);
4885 textfreeT(tmp
, FALSE
);
4887 if (bSame
) return TRUE
;
4889 /* add the text from the edit in */
4890 dispInfo
.item
.mask
|= LVIF_TEXT
;
4891 dispInfo
.item
.pszText
= pszText
;
4892 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4894 /* Do we need to update the Item Text */
4895 if (!notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
)) return FALSE
;
4896 if (!IsWindow(hwndSelf
))
4898 if (!pszText
) return TRUE
;
4900 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4902 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, editedItem
);
4903 ITEM_INFO
* lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
4904 if (lpItem
&& lpItem
->hdr
.pszText
== LPSTR_TEXTCALLBACKW
)
4906 LISTVIEW_InvalidateItem(infoPtr
, editedItem
);
4911 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4912 dispInfo
.item
.mask
= LVIF_TEXT
;
4913 dispInfo
.item
.iItem
= editedItem
;
4914 dispInfo
.item
.iSubItem
= 0;
4915 dispInfo
.item
.pszText
= pszText
;
4916 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4917 return LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
4922 * Begin in place editing of specified list view item
4925 * [I] infoPtr : valid pointer to the listview structure
4926 * [I] nItem : item index
4927 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4933 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
4935 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
4936 NMLVDISPINFOW dispInfo
;
4938 HWND hwndSelf
= infoPtr
->hwndSelf
;
4940 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
4942 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
4943 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
4945 infoPtr
->nEditLabelItem
= nItem
;
4947 /* Is the EditBox still there, if so remove it */
4948 if(infoPtr
->hwndEdit
!= 0)
4950 SetFocus(infoPtr
->hwndSelf
);
4951 infoPtr
->hwndEdit
= 0;
4954 LISTVIEW_SetSelection(infoPtr
, nItem
);
4955 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
4956 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4958 rect
.left
= LVIR_LABEL
;
4959 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
4961 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4962 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4963 dispInfo
.item
.iItem
= nItem
;
4964 dispInfo
.item
.iSubItem
= 0;
4965 dispInfo
.item
.stateMask
= ~0;
4966 dispInfo
.item
.pszText
= szDispText
;
4967 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
4968 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
4970 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, WS_VISIBLE
,
4971 rect
.left
-2, rect
.top
-1, 0, rect
.bottom
- rect
.top
+2, isW
);
4972 if (!infoPtr
->hwndEdit
) return 0;
4974 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
4976 if (!IsWindow(hwndSelf
))
4978 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
4979 infoPtr
->hwndEdit
= 0;
4983 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
4984 SetFocus(infoPtr
->hwndEdit
);
4985 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
4986 return infoPtr
->hwndEdit
;
4992 * Ensures the specified item is visible, scrolling into view if necessary.
4995 * [I] infoPtr : valid pointer to the listview structure
4996 * [I] nItem : item index
4997 * [I] bPartial : partially or entirely visible
5003 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
5005 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5006 INT nScrollPosHeight
= 0;
5007 INT nScrollPosWidth
= 0;
5008 INT nHorzAdjust
= 0;
5009 INT nVertAdjust
= 0;
5012 RECT rcItem
, rcTemp
;
5014 rcItem
.left
= LVIR_BOUNDS
;
5015 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
5017 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
5019 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
5021 /* scroll left/right, but in LVS_REPORT mode */
5022 if (uView
== LVS_LIST
)
5023 nScrollPosWidth
= infoPtr
->nItemWidth
;
5024 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5025 nScrollPosWidth
= 1;
5027 if (rcItem
.left
< infoPtr
->rcList
.left
)
5030 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
5035 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
5039 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
5041 /* scroll up/down, but not in LVS_LIST mode */
5042 if (uView
== LVS_REPORT
)
5043 nScrollPosHeight
= infoPtr
->nItemHeight
;
5044 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
5045 nScrollPosHeight
= 1;
5047 if (rcItem
.top
< infoPtr
->rcList
.top
)
5050 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
5055 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
5059 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
5061 if (nScrollPosWidth
)
5063 INT diff
= nHorzDiff
/ nScrollPosWidth
;
5064 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
5065 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
5068 if (nScrollPosHeight
)
5070 INT diff
= nVertDiff
/ nScrollPosHeight
;
5071 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
5072 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
5080 * Searches for an item with specific characteristics.
5083 * [I] hwnd : window handle
5084 * [I] nStart : base item index
5085 * [I] lpFindInfo : item information to look for
5088 * SUCCESS : index of item
5091 static INT
LISTVIEW_FindItemW(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
5092 const LVFINDINFOW
*lpFindInfo
)
5094 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5095 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5096 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
5097 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
5098 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
5099 POINT Position
, Destination
;
5102 /* Search in virtual listviews should be done by application, not by
5103 listview control, so we just send LVN_ODFINDITEMW and return the result */
5104 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5108 nmlv
.iStart
= nStart
;
5109 nmlv
.lvfi
= *lpFindInfo
;
5110 return notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
5113 if (!lpFindInfo
|| nItem
< 0) return -1;
5116 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
))
5118 lvItem
.mask
|= LVIF_TEXT
;
5119 lvItem
.pszText
= szDispText
;
5120 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5123 if (lpFindInfo
->flags
& LVFI_WRAP
)
5126 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
5127 (uView
== LVS_ICON
|| uView
==LVS_SMALLICON
))
5132 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5133 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
5134 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
5135 switch(lpFindInfo
->vkDirection
)
5137 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
5138 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
5139 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
5140 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
5141 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
5142 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
5143 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
5145 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
5146 Destination
.x
= rcArea
.right
;
5147 Destination
.y
= rcArea
.bottom
;
5149 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
5153 else Destination
.x
= Destination
.y
= 0;
5155 /* if LVFI_PARAM is specified, all other flags are ignored */
5156 if (lpFindInfo
->flags
& LVFI_PARAM
)
5158 lvItem
.mask
|= LVIF_PARAM
;
5160 lvItem
.mask
&= ~LVIF_TEXT
;
5164 for (; nItem
< nLast
; nItem
++)
5166 lvItem
.iItem
= nItem
;
5167 lvItem
.iSubItem
= 0;
5168 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
5170 if (lvItem
.mask
& LVIF_PARAM
)
5172 if (lpFindInfo
->lParam
== lvItem
.lParam
)
5178 if (lvItem
.mask
& LVIF_TEXT
)
5180 if (lpFindInfo
->flags
& LVFI_PARTIAL
)
5182 if (strstrW(lvItem
.pszText
, lpFindInfo
->psz
) == NULL
) continue;
5186 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
5190 if (!bNearest
) return nItem
;
5192 /* This is very inefficient. To do a good job here,
5193 * we need a sorted array of (x,y) item positions */
5194 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5196 /* compute the distance^2 to the destination */
5197 xdist
= Destination
.x
- Position
.x
;
5198 ydist
= Destination
.y
- Position
.y
;
5199 dist
= xdist
* xdist
+ ydist
* ydist
;
5201 /* remember the distance, and item if it's closer */
5205 nNearestItem
= nItem
;
5212 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
5217 return nNearestItem
;
5222 * Searches for an item with specific characteristics.
5225 * [I] hwnd : window handle
5226 * [I] nStart : base item index
5227 * [I] lpFindInfo : item information to look for
5230 * SUCCESS : index of item
5233 static INT
LISTVIEW_FindItemA(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
5234 const LVFINDINFOA
*lpFindInfo
)
5236 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
);
5241 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
5242 if (hasText
) fiw
.psz
= strW
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
5243 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
5244 textfreeT(strW
, FALSE
);
5250 * Retrieves the background image of the listview control.
5253 * [I] infoPtr : valid pointer to the listview structure
5254 * [O] lpBkImage : background image attributes
5260 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5262 /* FIXME (listview, "empty stub!\n"); */
5268 * Retrieves column attributes.
5271 * [I] infoPtr : valid pointer to the listview structure
5272 * [I] nColumn : column index
5273 * [IO] lpColumn : column information
5274 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5275 * otherwise it is in fact a LPLVCOLUMNA
5281 static BOOL
LISTVIEW_GetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
5283 COLUMN_INFO
*lpColumnInfo
;
5286 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5287 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
5289 /* initialize memory */
5290 ZeroMemory(&hdi
, sizeof(hdi
));
5292 if (lpColumn
->mask
& LVCF_TEXT
)
5294 hdi
.mask
|= HDI_TEXT
;
5295 hdi
.pszText
= lpColumn
->pszText
;
5296 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
5299 if (lpColumn
->mask
& LVCF_IMAGE
)
5300 hdi
.mask
|= HDI_IMAGE
;
5302 if (lpColumn
->mask
& LVCF_ORDER
)
5303 hdi
.mask
|= HDI_ORDER
;
5305 if (lpColumn
->mask
& LVCF_SUBITEM
)
5306 hdi
.mask
|= HDI_LPARAM
;
5308 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
5310 if (lpColumn
->mask
& LVCF_FMT
)
5311 lpColumn
->fmt
= lpColumnInfo
->fmt
;
5313 if (lpColumn
->mask
& LVCF_WIDTH
)
5314 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
5316 if (lpColumn
->mask
& LVCF_IMAGE
)
5317 lpColumn
->iImage
= hdi
.iImage
;
5319 if (lpColumn
->mask
& LVCF_ORDER
)
5320 lpColumn
->iOrder
= hdi
.iOrder
;
5322 if (lpColumn
->mask
& LVCF_SUBITEM
)
5323 lpColumn
->iSubItem
= hdi
.lParam
;
5329 static BOOL
LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
5336 /* FIXME: little hack */
5337 for (i
= 0; i
< iCount
; i
++)
5345 * Retrieves the column width.
5348 * [I] infoPtr : valid pointer to the listview structure
5349 * [I] int : column index
5352 * SUCCESS : column width
5355 static INT
LISTVIEW_GetColumnWidth(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
5357 INT nColumnWidth
= 0;
5360 TRACE("nColumn=%d\n", nColumn
);
5362 /* we have a 'column' in LIST and REPORT mode only */
5363 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
)
5366 nColumnWidth
= infoPtr
->nItemWidth
;
5369 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5370 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5371 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5373 * TODO: should we do the same in LVM_GETCOLUMN?
5375 hdItem
.mask
= HDI_WIDTH
;
5376 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdItem
))
5378 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr
->hwndSelf
, nColumn
);
5381 nColumnWidth
= hdItem
.cxy
;
5385 TRACE("nColumnWidth=%d\n", nColumnWidth
);
5386 return nColumnWidth
;
5391 * In list or report display mode, retrieves the number of items that can fit
5392 * vertically in the visible area. In icon or small icon display mode,
5393 * retrieves the total number of visible items.
5396 * [I] infoPtr : valid pointer to the listview structure
5399 * Number of fully visible items.
5401 static INT
LISTVIEW_GetCountPerPage(const LISTVIEW_INFO
*infoPtr
)
5403 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
5407 return infoPtr
->nItemCount
;
5409 return LISTVIEW_GetCountPerColumn(infoPtr
);
5411 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
5419 * Retrieves an image list handle.
5422 * [I] infoPtr : valid pointer to the listview structure
5423 * [I] nImageList : image list identifier
5426 * SUCCESS : image list handle
5429 static HIMAGELIST
LISTVIEW_GetImageList(const LISTVIEW_INFO
*infoPtr
, INT nImageList
)
5433 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
5434 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
5435 case LVSIL_STATE
: return infoPtr
->himlState
;
5440 /* LISTVIEW_GetISearchString */
5444 * Retrieves item attributes.
5447 * [I] hwnd : window handle
5448 * [IO] lpLVItem : item info
5449 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5450 * if FALSE, then lpLVItem is a LPLVITEMA.
5453 * This is the internal 'GetItem' interface -- it tries to
5454 * be smart and avoid text copies, if possible, by modifying
5455 * lpLVItem->pszText to point to the text string. Please note
5456 * that this is not always possible (e.g. OWNERDATA), so on
5457 * entry you *must* supply valid values for pszText, and cchTextMax.
5458 * The only difference to the documented interface is that upon
5459 * return, you should use *only* the lpLVItem->pszText, rather than
5460 * the buffer pointer you provided on input. Most code already does
5461 * that, so it's not a problem.
5462 * For the two cases when the text must be copied (that is,
5463 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5469 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5471 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
5472 NMLVDISPINFOW dispInfo
;
5478 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
5480 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5483 if (lpLVItem
->mask
== 0) return TRUE
;
5485 /* make a local copy */
5486 isubitem
= lpLVItem
->iSubItem
;
5488 /* a quick optimization if all we're asked is the focus state
5489 * these queries are worth optimising since they are common,
5490 * and can be answered in constant time, without the heavy accesses */
5491 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
5492 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
5494 lpLVItem
->state
= 0;
5495 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5496 lpLVItem
->state
|= LVIS_FOCUSED
;
5500 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5502 /* if the app stores all the data, handle it separately */
5503 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5505 dispInfo
.item
.state
= 0;
5507 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5508 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) || infoPtr
->uCallbackMask
)
5510 /* NOTE: copy only fields which we _know_ are initialized, some apps
5511 * depend on the uninitialized fields being 0 */
5512 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
5513 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5514 dispInfo
.item
.iSubItem
= isubitem
;
5515 if (lpLVItem
->mask
& LVIF_TEXT
)
5517 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5518 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5520 if (lpLVItem
->mask
& LVIF_STATE
)
5521 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
5522 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5523 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
5524 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
5526 /* full size structure expected - _WIN32IE >= 0x560 */
5527 *lpLVItem
= dispInfo
.item
;
5529 else if (lpLVItem
->mask
& LVIF_INDENT
)
5531 /* indent member expected - _WIN32IE >= 0x300 */
5532 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
5536 /* minimal structure expected */
5537 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
5539 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
5542 /* make sure lParam is zeroed out */
5543 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
5545 /* we store only a little state, so if we're not asked, we're done */
5546 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
5548 /* if focus is handled by us, report it */
5549 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5551 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5552 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5553 lpLVItem
->state
|= LVIS_FOCUSED
;
5556 /* and do the same for selection, if we handle it */
5557 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5559 lpLVItem
->state
&= ~LVIS_SELECTED
;
5560 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5561 lpLVItem
->state
|= LVIS_SELECTED
;
5567 /* find the item and subitem structures before we proceed */
5568 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
5569 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5574 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
5575 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
5578 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
5583 pItemHdr
= &lpItem
->hdr
;
5585 /* Do we need to query the state from the app? */
5586 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
5588 dispInfo
.item
.mask
|= LVIF_STATE
;
5589 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
5592 /* Do we need to enquire about the image? */
5593 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
5594 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
5596 dispInfo
.item
.mask
|= LVIF_IMAGE
;
5597 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
5600 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5601 if ((lpLVItem
->mask
& LVIF_TEXT
) && !is_textW(pItemHdr
->pszText
))
5603 dispInfo
.item
.mask
|= LVIF_TEXT
;
5604 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5605 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5606 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
5607 *dispInfo
.item
.pszText
= '\0';
5610 /* If we don't have all the requested info, query the application */
5611 if (dispInfo
.item
.mask
!= 0)
5613 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5614 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
5615 dispInfo
.item
.lParam
= lpItem
->lParam
;
5616 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5617 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
5620 /* we should not store values for subitems */
5621 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
5623 /* Now, handle the iImage field */
5624 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
5626 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
5627 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
5628 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
5630 else if (lpLVItem
->mask
& LVIF_IMAGE
)
5632 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
5633 lpLVItem
->iImage
= pItemHdr
->iImage
;
5635 lpLVItem
->iImage
= 0;
5638 /* The pszText field */
5639 if (dispInfo
.item
.mask
& LVIF_TEXT
)
5641 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
5642 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
5644 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
5646 else if (lpLVItem
->mask
& LVIF_TEXT
)
5648 if (isW
) lpLVItem
->pszText
= pItemHdr
->pszText
;
5649 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
5652 /* Next is the lParam field */
5653 if (dispInfo
.item
.mask
& LVIF_PARAM
)
5655 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
5656 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
5657 lpItem
->lParam
= dispInfo
.item
.lParam
;
5659 else if (lpLVItem
->mask
& LVIF_PARAM
)
5660 lpLVItem
->lParam
= lpItem
->lParam
;
5662 /* if this is a subitem, we're done */
5663 if (isubitem
) return TRUE
;
5665 /* ... the state field (this one is different due to uCallbackmask) */
5666 if (lpLVItem
->mask
& LVIF_STATE
)
5668 lpLVItem
->state
= lpItem
->state
& lpLVItem
->stateMask
;
5669 if (dispInfo
.item
.mask
& LVIF_STATE
)
5671 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
5672 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
5674 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5676 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5677 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5678 lpLVItem
->state
|= LVIS_FOCUSED
;
5680 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5682 lpLVItem
->state
&= ~LVIS_SELECTED
;
5683 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5684 lpLVItem
->state
|= LVIS_SELECTED
;
5688 /* and last, but not least, the indent field */
5689 if (lpLVItem
->mask
& LVIF_INDENT
)
5690 lpLVItem
->iIndent
= lpItem
->iIndent
;
5697 * Retrieves item attributes.
5700 * [I] hwnd : window handle
5701 * [IO] lpLVItem : item info
5702 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5703 * if FALSE, then lpLVItem is a LPLVITEMA.
5706 * This is the external 'GetItem' interface -- it properly copies
5707 * the text in the provided buffer.
5713 static BOOL
LISTVIEW_GetItemExtT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5718 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5721 pszText
= lpLVItem
->pszText
;
5722 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
5723 if (bResult
&& lpLVItem
->pszText
!= pszText
)
5724 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
5725 lpLVItem
->pszText
= pszText
;
5733 * Retrieves the position (upper-left) of the listview control item.
5734 * Note that for LVS_ICON style, the upper-left is that of the icon
5735 * and not the bounding box.
5738 * [I] infoPtr : valid pointer to the listview structure
5739 * [I] nItem : item index
5740 * [O] lpptPosition : coordinate information
5746 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
5748 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5751 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
5753 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5755 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5756 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
5758 if (uView
== LVS_ICON
)
5760 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
5761 lpptPosition
->y
+= ICON_TOP_PADDING
;
5763 lpptPosition
->x
+= Origin
.x
;
5764 lpptPosition
->y
+= Origin
.y
;
5766 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition
));
5773 * Retrieves the bounding rectangle for a listview control item.
5776 * [I] infoPtr : valid pointer to the listview structure
5777 * [I] nItem : item index
5778 * [IO] lprc : bounding rectangle coordinates
5779 * lprc->left specifies the portion of the item for which the bounding
5780 * rectangle will be retrieved.
5782 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5783 * including the icon and label.
5786 * * Experiment shows that native control returns:
5787 * * width = min (48, length of text line)
5788 * * .left = position.x - (width - iconsize.cx)/2
5789 * * .right = .left + width
5790 * * height = #lines of text * ntmHeight + icon height + 8
5791 * * .top = position.y - 2
5792 * * .bottom = .top + height
5793 * * separation between items .y = itemSpacing.cy - height
5794 * * .x = itemSpacing.cx - width
5795 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5798 * * Experiment shows that native control returns:
5799 * * width = iconSize.cx + 16
5800 * * .left = position.x - (width - iconsize.cx)/2
5801 * * .right = .left + width
5802 * * height = iconSize.cy + 4
5803 * * .top = position.y - 2
5804 * * .bottom = .top + height
5805 * * separation between items .y = itemSpacing.cy - height
5806 * * .x = itemSpacing.cx - width
5807 * LVIR_LABEL Returns the bounding rectangle of the item text.
5810 * * Experiment shows that native control returns:
5811 * * width = text length
5812 * * .left = position.x - width/2
5813 * * .right = .left + width
5814 * * height = ntmH * linecount + 2
5815 * * .top = position.y + iconSize.cy + 6
5816 * * .bottom = .top + height
5817 * * separation between items .y = itemSpacing.cy - height
5818 * * .x = itemSpacing.cx - width
5819 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5820 * rectangles, but excludes columns in report view.
5827 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5828 * upon whether the window has the focus currently and on whether the item
5829 * is the one with the focus. Ensure that the control's record of which
5830 * item has the focus agrees with the items' records.
5832 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5834 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5835 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5836 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
5837 POINT Position
, Origin
;
5840 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
5842 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5844 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5845 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5847 /* Be smart and try to figure out the minimum we have to do */
5848 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
5849 if (uView
== LVS_REPORT
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
5850 if (uView
== LVS_ICON
&& lprc
->left
!= LVIR_ICON
&&
5851 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
5852 oversizedBox
= TRUE
;
5854 /* get what we need from the item before hand, so we make
5855 * only one request. This can speed up things, if data
5856 * is stored on the app side */
5858 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
5859 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
5860 lvItem
.iItem
= nItem
;
5861 lvItem
.iSubItem
= 0;
5862 lvItem
.pszText
= szDispText
;
5863 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5864 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5865 /* we got the state already up, simulate it here, to avoid a reget */
5866 if (uView
== LVS_ICON
&& (lprc
->left
!= LVIR_ICON
))
5868 lvItem
.mask
|= LVIF_STATE
;
5869 lvItem
.stateMask
= LVIS_FOCUSED
;
5870 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
5873 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
5874 lprc
->left
= LVIR_BOUNDS
;
5878 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
5882 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, NULL
, lprc
);
5886 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
5889 case LVIR_SELECTBOUNDS
:
5890 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, lprc
, NULL
, NULL
, NULL
);
5894 WARN("Unknown value: %d\n", lprc
->left
);
5898 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
5900 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc
));
5907 * Retrieves the spacing between listview control items.
5910 * [I] infoPtr : valid pointer to the listview structure
5911 * [IO] lprc : rectangle to receive the output
5912 * on input, lprc->top = nSubItem
5913 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5915 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5916 * not only those of the first column.
5917 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5923 static BOOL
LISTVIEW_GetSubItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5929 if (!lprc
) return FALSE
;
5931 nColumn
= lprc
->top
;
5933 TRACE("(nItem=%d, nSubItem=%d)\n", nItem
, lprc
->top
);
5934 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5936 return LISTVIEW_GetItemRect(infoPtr
, nItem
, lprc
);
5938 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return FALSE
;
5940 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &Position
)) return FALSE
;
5942 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5945 lvItem
.iItem
= nItem
;
5946 lvItem
.iSubItem
= nColumn
;
5948 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5952 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
5957 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
5961 ERR("Unknown bounds=%d\n", lprc
->left
);
5965 OffsetRect(lprc
, Position
.x
, Position
.y
);
5972 * Retrieves the width of a label.
5975 * [I] infoPtr : valid pointer to the listview structure
5978 * SUCCESS : string width (in pixels)
5981 static INT
LISTVIEW_GetLabelWidth(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
5983 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5986 TRACE("(nItem=%d)\n", nItem
);
5988 lvItem
.mask
= LVIF_TEXT
;
5989 lvItem
.iItem
= nItem
;
5990 lvItem
.iSubItem
= 0;
5991 lvItem
.pszText
= szDispText
;
5992 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5993 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5995 return LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
6000 * Retrieves the spacing between listview control items.
6003 * [I] infoPtr : valid pointer to the listview structure
6004 * [I] bSmall : flag for small or large icon
6007 * Horizontal + vertical spacing
6009 static LONG
LISTVIEW_GetItemSpacing(const LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
6015 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
6019 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
6020 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
6022 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
6029 * Retrieves the state of a listview control item.
6032 * [I] infoPtr : valid pointer to the listview structure
6033 * [I] nItem : item index
6034 * [I] uMask : state mask
6037 * State specified by the mask.
6039 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
6043 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6045 lvItem
.iItem
= nItem
;
6046 lvItem
.iSubItem
= 0;
6047 lvItem
.mask
= LVIF_STATE
;
6048 lvItem
.stateMask
= uMask
;
6049 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
6051 return lvItem
.state
& uMask
;
6056 * Retrieves the text of a listview control item or subitem.
6059 * [I] hwnd : window handle
6060 * [I] nItem : item index
6061 * [IO] lpLVItem : item information
6062 * [I] isW : TRUE if lpLVItem is Unicode
6065 * SUCCESS : string length
6068 static INT
LISTVIEW_GetItemTextT(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
6070 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6072 lpLVItem
->mask
= LVIF_TEXT
;
6073 lpLVItem
->iItem
= nItem
;
6074 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
6076 return textlenT(lpLVItem
->pszText
, isW
);
6081 * Searches for an item based on properties + relationships.
6084 * [I] infoPtr : valid pointer to the listview structure
6085 * [I] nItem : item index
6086 * [I] uFlags : relationship flag
6089 * SUCCESS : item index
6092 static INT
LISTVIEW_GetNextItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
6094 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6096 LVFINDINFOW lvFindInfo
;
6097 INT nCountPerColumn
;
6101 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
6102 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
6104 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
6106 if (uFlags
& LVNI_CUT
)
6109 if (uFlags
& LVNI_DROPHILITED
)
6110 uMask
|= LVIS_DROPHILITED
;
6112 if (uFlags
& LVNI_FOCUSED
)
6113 uMask
|= LVIS_FOCUSED
;
6115 if (uFlags
& LVNI_SELECTED
)
6116 uMask
|= LVIS_SELECTED
;
6118 /* if we're asked for the focused item, that's only one,
6119 * so it's worth optimizing */
6120 if (uFlags
& LVNI_FOCUSED
)
6122 if ((LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) != uMask
) return -1;
6123 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
6126 if (uFlags
& LVNI_ABOVE
)
6128 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
6133 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6139 /* Special case for autoarrange - move 'til the top of a list */
6140 if (is_autoarrange(infoPtr
))
6142 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6143 while (nItem
- nCountPerRow
>= 0)
6145 nItem
-= nCountPerRow
;
6146 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6151 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6152 lvFindInfo
.vkDirection
= VK_UP
;
6153 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6154 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6156 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6161 else if (uFlags
& LVNI_BELOW
)
6163 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
6165 while (nItem
< infoPtr
->nItemCount
)
6168 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6174 /* Special case for autoarrange - move 'til the bottom of a list */
6175 if (is_autoarrange(infoPtr
))
6177 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6178 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
6180 nItem
+= nCountPerRow
;
6181 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6186 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6187 lvFindInfo
.vkDirection
= VK_DOWN
;
6188 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6189 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6191 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6196 else if (uFlags
& LVNI_TOLEFT
)
6198 if (uView
== LVS_LIST
)
6200 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
6201 while (nItem
- nCountPerColumn
>= 0)
6203 nItem
-= nCountPerColumn
;
6204 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6208 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6210 /* Special case for autoarrange - move 'ti the beginning of a row */
6211 if (is_autoarrange(infoPtr
))
6213 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6214 while (nItem
% nCountPerRow
> 0)
6217 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6222 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6223 lvFindInfo
.vkDirection
= VK_LEFT
;
6224 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6225 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6227 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6232 else if (uFlags
& LVNI_TORIGHT
)
6234 if (uView
== LVS_LIST
)
6236 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
6237 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
6239 nItem
+= nCountPerColumn
;
6240 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6244 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6246 /* Special case for autoarrange - move 'til the end of a row */
6247 if (is_autoarrange(infoPtr
))
6249 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6250 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
6253 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6258 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6259 lvFindInfo
.vkDirection
= VK_RIGHT
;
6260 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6261 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6263 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6272 /* search by index */
6273 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
6275 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
6283 /* LISTVIEW_GetNumberOfWorkAreas */
6287 * Retrieves the origin coordinates when in icon or small icon display mode.
6290 * [I] infoPtr : valid pointer to the listview structure
6291 * [O] lpptOrigin : coordinate information
6296 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
6298 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6299 INT nHorzPos
= 0, nVertPos
= 0;
6300 SCROLLINFO scrollInfo
;
6302 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
6303 scrollInfo
.fMask
= SIF_POS
;
6305 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
6306 nHorzPos
= scrollInfo
.nPos
;
6307 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
6308 nVertPos
= scrollInfo
.nPos
;
6310 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
6312 lpptOrigin
->x
= infoPtr
->rcList
.left
;
6313 lpptOrigin
->y
= infoPtr
->rcList
.top
;
6314 if (uView
== LVS_LIST
)
6315 nHorzPos
*= infoPtr
->nItemWidth
;
6316 else if (uView
== LVS_REPORT
)
6317 nVertPos
*= infoPtr
->nItemHeight
;
6319 lpptOrigin
->x
-= nHorzPos
;
6320 lpptOrigin
->y
-= nVertPos
;
6322 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin
));
6327 * Retrieves the width of a string.
6330 * [I] hwnd : window handle
6331 * [I] lpszText : text string to process
6332 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6335 * SUCCESS : string width (in pixels)
6338 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
6343 if (is_textT(lpszText
, isW
))
6345 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
6346 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
6347 HFONT hOldFont
= SelectObject(hdc
, hFont
);
6350 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
6352 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
6353 SelectObject(hdc
, hOldFont
);
6354 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6356 return stringSize
.cx
;
6361 * Determines which listview item is located at the specified position.
6364 * [I] infoPtr : valid pointer to the listview structure
6365 * [IO] lpht : hit test information
6366 * [I] subitem : fill out iSubItem.
6367 * [I] select : return the index only if the hit selects the item
6370 * (mm 20001022): We must not allow iSubItem to be touched, for
6371 * an app might pass only a structure with space up to iItem!
6372 * (MS Office 97 does that for instance in the file open dialog)
6375 * SUCCESS : item index
6378 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
6380 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6381 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6382 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
6383 POINT Origin
, Position
, opt
;
6388 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht
->pt
), subitem
, select
);
6392 if (subitem
) lpht
->iSubItem
= 0;
6394 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
6395 lpht
->flags
|= LVHT_TOLEFT
;
6396 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
6397 lpht
->flags
|= LVHT_TORIGHT
;
6399 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
6400 lpht
->flags
|= LVHT_ABOVE
;
6401 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
6402 lpht
->flags
|= LVHT_BELOW
;
6404 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6405 if (lpht
->flags
) return -1;
6407 lpht
->flags
|= LVHT_NOWHERE
;
6409 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6411 /* first deal with the large items */
6412 rcSearch
.left
= lpht
->pt
.x
;
6413 rcSearch
.top
= lpht
->pt
.y
;
6414 rcSearch
.right
= rcSearch
.left
+ 1;
6415 rcSearch
.bottom
= rcSearch
.top
+ 1;
6417 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
6418 iterator_next(&i
); /* go to first item in the sequence */
6420 iterator_destroy(&i
);
6422 TRACE("lpht->iItem=%d\n", iItem
);
6423 if (iItem
== -1) return -1;
6425 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
6426 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
6427 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
6428 if (uView
== LVS_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
6429 lvItem
.iItem
= iItem
;
6430 lvItem
.iSubItem
= 0;
6431 lvItem
.pszText
= szDispText
;
6432 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6433 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
6434 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
6436 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
6437 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
6438 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
6439 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
6441 if (uView
== LVS_REPORT
)
6445 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6446 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6448 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds
));
6449 if (!PtInRect(&rcBounds
, opt
)) return -1;
6451 if (PtInRect(&rcIcon
, opt
))
6452 lpht
->flags
|= LVHT_ONITEMICON
;
6453 else if (PtInRect(&rcLabel
, opt
))
6454 lpht
->flags
|= LVHT_ONITEMLABEL
;
6455 else if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && PtInRect(&rcState
, opt
))
6456 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
6457 if (lpht
->flags
& LVHT_ONITEM
)
6458 lpht
->flags
&= ~LVHT_NOWHERE
;
6460 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6461 if (uView
== LVS_REPORT
&& subitem
)
6465 rcBounds
.right
= rcBounds
.left
;
6466 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
6468 rcBounds
.left
= rcBounds
.right
;
6469 rcBounds
.right
+= LISTVIEW_GetColumnWidth(infoPtr
, j
);
6470 if (PtInRect(&rcBounds
, opt
))
6478 if (select
&& !(uView
== LVS_REPORT
&&
6479 ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) ||
6480 (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))))
6482 if (uView
== LVS_REPORT
)
6484 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6485 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6487 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
6489 return lpht
->iItem
= iItem
;
6493 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6494 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6495 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6496 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6497 their own sort proc. when sending LVM_SORTITEMS.
6500 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6502 LVS_SORTXXX must be specified,
6503 LVS_OWNERDRAW is not set,
6504 <item>.pszText is not LPSTR_TEXTCALLBACK.
6506 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6507 are sorted based on item text..."
6509 static INT WINAPI
LISTVIEW_InsertCompare( LPVOID first
, LPVOID second
, LPARAM lParam
)
6511 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
6512 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
6513 INT cmpv
= textcmpWT(lv_first
->hdr
.pszText
, lv_second
->hdr
.pszText
, TRUE
);
6515 /* if we're sorting descending, negate the return value */
6516 return (((const LISTVIEW_INFO
*)lParam
)->dwStyle
& LVS_SORTDESCENDING
) ? -cmpv
: cmpv
;
6521 * Inserts a new item in the listview control.
6524 * [I] infoPtr : valid pointer to the listview structure
6525 * [I] lpLVItem : item information
6526 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6529 * SUCCESS : new item index
6532 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
6534 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6539 BOOL is_sorted
, has_changed
;
6541 HWND hwndSelf
= infoPtr
->hwndSelf
;
6543 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6545 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
6547 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6548 if (!lpLVItem
|| lpLVItem
->iSubItem
) return -1;
6550 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
6552 if (!(lpItem
= Alloc(sizeof(ITEM_INFO
)))) return -1;
6554 /* insert item in listview control data structure */
6555 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
6556 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
6558 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
6559 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
6561 if (lpLVItem
->iItem
< 0 && !is_sorted
) return -1;
6563 nItem
= is_sorted
? infoPtr
->nItemCount
: min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
6564 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
6565 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
6566 if (nItem
== -1) goto fail
;
6567 infoPtr
->nItemCount
++;
6569 /* shift indices first so they don't get tangled */
6570 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
6572 /* set the item attributes */
6573 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6575 /* full size structure expected - _WIN32IE >= 0x560 */
6578 else if (lpLVItem
->mask
& LVIF_INDENT
)
6580 /* indent member expected - _WIN32IE >= 0x300 */
6581 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
6585 /* minimal structure expected */
6586 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
6589 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
6591 item
.mask
|= LVIF_STATE
;
6592 item
.stateMask
|= LVIS_STATEIMAGEMASK
;
6593 item
.state
&= ~LVIS_STATEIMAGEMASK
;
6594 item
.state
|= INDEXTOSTATEIMAGEMASK(1);
6596 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
6598 /* if we're sorted, sort the list, and update the index */
6601 DPA_Sort( infoPtr
->hdpaItems
, LISTVIEW_InsertCompare
, (LPARAM
)infoPtr
);
6602 nItem
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, hdpaSubItems
);
6603 assert(nItem
!= -1);
6606 /* make room for the position, if we are in the right mode */
6607 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6609 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
6611 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
6613 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
6618 /* send LVN_INSERTITEM notification */
6619 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
6621 nmlv
.lParam
= lpItem
->lParam
;
6622 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
6623 if (!IsWindow(hwndSelf
))
6626 /* align items (set position of each item) */
6627 if ((uView
== LVS_SMALLICON
|| uView
== LVS_ICON
))
6631 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
6632 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
6634 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
6636 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
6639 /* now is the invalidation fun */
6640 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
6644 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
6645 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
6646 infoPtr
->nItemCount
--;
6648 DPA_DeletePtr(hdpaSubItems
, 0);
6649 DPA_Destroy (hdpaSubItems
);
6656 * Redraws a range of items.
6659 * [I] infoPtr : valid pointer to the listview structure
6660 * [I] nFirst : first item
6661 * [I] nLast : last item
6667 static BOOL
LISTVIEW_RedrawItems(const LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
6671 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
6672 max(nFirst
, nLast
) >= infoPtr
->nItemCount
)
6675 for (i
= nFirst
; i
<= nLast
; i
++)
6676 LISTVIEW_InvalidateItem(infoPtr
, i
);
6683 * Scroll the content of a listview.
6686 * [I] infoPtr : valid pointer to the listview structure
6687 * [I] dx : horizontal scroll amount in pixels
6688 * [I] dy : vertical scroll amount in pixels
6695 * If the control is in report mode (LVS_REPORT) the control can
6696 * be scrolled only in line increments. "dy" will be rounded to the
6697 * nearest number of pixels that are a whole line. Ex: if line height
6698 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6699 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6701 * For: (per experimentation with native control and CSpy ListView)
6702 * LVS_ICON dy=1 = 1 pixel (vertical only)
6704 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6706 * LVS_LIST dx=1 = 1 column (horizontal only)
6707 * but will only scroll 1 column per message
6708 * no matter what the value.
6709 * dy must be 0 or FALSE returned.
6710 * LVS_REPORT dx=1 = 1 pixel
6714 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
6716 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
) {
6718 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
6719 dy
/= infoPtr
->nItemHeight
;
6722 if (dy
!= 0) return FALSE
;
6729 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
, 0);
6730 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
, 0);
6737 * Sets the background color.
6740 * [I] infoPtr : valid pointer to the listview structure
6741 * [I] clrBk : background color
6747 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrBk
)
6749 TRACE("(clrBk=%x)\n", clrBk
);
6751 if(infoPtr
->clrBk
!= clrBk
) {
6752 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
6753 infoPtr
->clrBk
= clrBk
;
6754 if (clrBk
== CLR_NONE
)
6755 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
6757 infoPtr
->hBkBrush
= CreateSolidBrush(clrBk
);
6758 LISTVIEW_InvalidateList(infoPtr
);
6764 /* LISTVIEW_SetBkImage */
6766 /*** Helper for {Insert,Set}ColumnT *only* */
6767 static void column_fill_hditem(const LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
,
6768 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6770 if (lpColumn
->mask
& LVCF_FMT
)
6772 /* format member is valid */
6773 lphdi
->mask
|= HDI_FORMAT
;
6775 /* set text alignment (leftmost column must be left-aligned) */
6776 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
6777 lphdi
->fmt
|= HDF_LEFT
;
6778 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
6779 lphdi
->fmt
|= HDF_RIGHT
;
6780 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
6781 lphdi
->fmt
|= HDF_CENTER
;
6783 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6784 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
6786 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6788 lphdi
->fmt
|= HDF_IMAGE
;
6789 lphdi
->iImage
= I_IMAGECALLBACK
;
6793 if (lpColumn
->mask
& LVCF_WIDTH
)
6795 lphdi
->mask
|= HDI_WIDTH
;
6796 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
6798 /* make it fill the remainder of the controls width */
6802 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
6804 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
6805 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
6808 /* retrieve the layout of the header */
6809 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
6810 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, wine_dbgstr_rect(&rcHeader
));
6812 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
6815 lphdi
->cxy
= lpColumn
->cx
;
6818 if (lpColumn
->mask
& LVCF_TEXT
)
6820 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
6821 lphdi
->fmt
|= HDF_STRING
;
6822 lphdi
->pszText
= lpColumn
->pszText
;
6823 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
6826 if (lpColumn
->mask
& LVCF_IMAGE
)
6828 lphdi
->mask
|= HDI_IMAGE
;
6829 lphdi
->iImage
= lpColumn
->iImage
;
6832 if (lpColumn
->mask
& LVCF_ORDER
)
6834 lphdi
->mask
|= HDI_ORDER
;
6835 lphdi
->iOrder
= lpColumn
->iOrder
;
6842 * Inserts a new column.
6845 * [I] infoPtr : valid pointer to the listview structure
6846 * [I] nColumn : column index
6847 * [I] lpColumn : column information
6848 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6851 * SUCCESS : new column index
6854 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6855 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6857 COLUMN_INFO
*lpColumnInfo
;
6861 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6863 if (!lpColumn
|| nColumn
< 0) return -1;
6864 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
6866 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6867 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6870 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6871 * (can be seen in SPY) otherwise column never gets added.
6873 if (!(lpColumn
->mask
& LVCF_WIDTH
)) {
6874 hdi
.mask
|= HDI_WIDTH
;
6879 * when the iSubItem is available Windows copies it to the header lParam. It seems
6880 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6882 if (lpColumn
->mask
& LVCF_SUBITEM
)
6884 hdi
.mask
|= HDI_LPARAM
;
6885 hdi
.lParam
= lpColumn
->iSubItem
;
6888 /* insert item in header control */
6889 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
6890 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
6891 (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6892 if (nNewColumn
== -1) return -1;
6893 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
6895 /* create our own column info */
6896 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
6897 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
6899 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
6900 if (!Header_GetItemRect(infoPtr
->hwndHeader
, nNewColumn
, &lpColumnInfo
->rcHeader
)) goto fail
;
6902 /* now we have to actually adjust the data */
6903 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
6905 SUBITEM_INFO
*lpSubItem
;
6909 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
6911 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
6912 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
6914 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
6915 if (lpSubItem
->iSubItem
>= nNewColumn
)
6916 lpSubItem
->iSubItem
++;
6921 /* make space for the new column */
6922 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
6923 LISTVIEW_UpdateItemSize(infoPtr
);
6928 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
6931 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
6939 * Sets the attributes of a header item.
6942 * [I] infoPtr : valid pointer to the listview structure
6943 * [I] nColumn : column index
6944 * [I] lpColumn : column attributes
6945 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6951 static BOOL
LISTVIEW_SetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6952 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6954 HDITEMW hdi
, hdiget
;
6957 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6959 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6961 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6962 if (lpColumn
->mask
& LVCF_FMT
)
6964 hdi
.mask
|= HDI_FORMAT
;
6965 hdiget
.mask
= HDI_FORMAT
;
6966 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdiget
))
6967 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
6969 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6971 /* set header item attributes */
6972 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6973 if (!bResult
) return FALSE
;
6975 if (lpColumn
->mask
& LVCF_FMT
)
6977 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
6978 int oldFmt
= lpColumnInfo
->fmt
;
6980 lpColumnInfo
->fmt
= lpColumn
->fmt
;
6981 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
6983 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6984 if (uView
== LVS_REPORT
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
6993 * Sets the column order array
6996 * [I] infoPtr : valid pointer to the listview structure
6997 * [I] iCount : number of elements in column order array
6998 * [I] lpiArray : pointer to column order array
7004 static BOOL
LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
7006 FIXME("iCount %d lpiArray %p\n", iCount
, lpiArray
);
7017 * Sets the width of a column
7020 * [I] infoPtr : valid pointer to the listview structure
7021 * [I] nColumn : column index
7022 * [I] cx : column width
7028 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
7030 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7031 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
7035 TRACE("(nColumn=%d, cx=%d\n", nColumn
, cx
);
7037 /* set column width only if in report or list mode */
7038 if (uView
!= LVS_REPORT
&& uView
!= LVS_LIST
) return FALSE
;
7040 /* take care of invalid cx values */
7041 if(uView
== LVS_REPORT
&& cx
< -2) cx
= LVSCW_AUTOSIZE
;
7042 else if (uView
== LVS_LIST
&& cx
< 1) return FALSE
;
7044 /* resize all columns if in LVS_LIST mode */
7045 if(uView
== LVS_LIST
)
7047 infoPtr
->nItemWidth
= cx
;
7048 LISTVIEW_InvalidateList(infoPtr
);
7052 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
7054 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
7059 lvItem
.mask
= LVIF_TEXT
;
7061 lvItem
.iSubItem
= nColumn
;
7062 lvItem
.pszText
= szDispText
;
7063 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7064 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7066 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
7067 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
7068 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
7070 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
7071 max_cx
+= infoPtr
->iconSize
.cx
;
7072 max_cx
+= TRAILING_LABEL_PADDING
;
7075 /* autosize based on listview items width */
7076 if(cx
== LVSCW_AUTOSIZE
)
7078 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
7080 /* if iCol is the last column make it fill the remainder of the controls width */
7081 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
7086 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7087 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
7089 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
7093 /* Despite what the MS docs say, if this is not the last
7094 column, then MS resizes the column to the width of the
7095 largest text string in the column, including headers
7096 and items. This is different from LVSCW_AUTOSIZE in that
7097 LVSCW_AUTOSIZE ignores the header string length. */
7100 /* retrieve header text */
7101 hdi
.mask
= HDI_TEXT
;
7102 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
7103 hdi
.pszText
= szDispText
;
7104 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
))
7106 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
7107 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
7110 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
7111 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
7112 /* FIXME: Take into account the header image, if one is present */
7113 SelectObject(hdc
, old_font
);
7114 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7116 cx
= max (cx
, max_cx
);
7120 if (cx
< 0) return FALSE
;
7122 /* call header to update the column change */
7123 hdi
.mask
= HDI_WIDTH
;
7125 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
7126 return Header_SetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
);
7130 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7133 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO
*infoPtr
)
7136 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
7138 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
7139 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
7142 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
7143 ILC_COLOR
| ILC_MASK
, 2, 2);
7144 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
7145 hdc
= CreateCompatibleDC(hdc_wnd
);
7146 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
7147 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
7148 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
7150 rc
.left
= rc
.top
= 0;
7151 rc
.right
= GetSystemMetrics(SM_CXSMICON
);
7152 rc
.bottom
= GetSystemMetrics(SM_CYSMICON
);
7154 hbm_orig
= SelectObject(hdc
, hbm_mask
);
7155 FillRect(hdc
, &rc
, hbr_white
);
7156 InflateRect(&rc
, -3, -3);
7157 FillRect(hdc
, &rc
, hbr_black
);
7159 SelectObject(hdc
, hbm_im
);
7160 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
7161 SelectObject(hdc
, hbm_orig
);
7162 ImageList_Add(himl
, hbm_im
, hbm_mask
);
7164 SelectObject(hdc
, hbm_im
);
7165 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
7166 SelectObject(hdc
, hbm_orig
);
7167 ImageList_Add(himl
, hbm_im
, hbm_mask
);
7169 DeleteObject(hbm_mask
);
7170 DeleteObject(hbm_im
);
7178 * Sets the extended listview style.
7181 * [I] infoPtr : valid pointer to the listview structure
7183 * [I] dwStyle : style
7186 * SUCCESS : previous style
7189 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD dwMask
, DWORD dwExStyle
)
7191 DWORD dwOldExStyle
= infoPtr
->dwLvExStyle
;
7195 infoPtr
->dwLvExStyle
= (dwOldExStyle
& ~dwMask
) | (dwExStyle
& dwMask
);
7197 infoPtr
->dwLvExStyle
= dwExStyle
;
7199 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_CHECKBOXES
)
7201 HIMAGELIST himl
= 0;
7202 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
7205 item
.mask
= LVIF_STATE
;
7206 item
.stateMask
= LVIS_STATEIMAGEMASK
;
7207 item
.state
= INDEXTOSTATEIMAGEMASK(1);
7208 LISTVIEW_SetItemState(infoPtr
, -1, &item
);
7210 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
7212 LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
7215 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_HEADERDRAGDROP
)
7217 DWORD dwStyle
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
7218 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
)
7219 dwStyle
|= HDS_DRAGDROP
;
7221 dwStyle
&= ~HDS_DRAGDROP
;
7222 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, dwStyle
);
7225 /* GRIDLINES adds decoration at top so changes sizes */
7226 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_GRIDLINES
)
7228 LISTVIEW_UpdateSize(infoPtr
);
7232 LISTVIEW_InvalidateList(infoPtr
);
7233 return dwOldExStyle
;
7238 * Sets the new hot cursor used during hot tracking and hover selection.
7241 * [I] infoPtr : valid pointer to the listview structure
7242 * [I] hCursor : the new hot cursor handle
7245 * Returns the previous hot cursor
7247 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
7249 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
7251 infoPtr
->hHotCursor
= hCursor
;
7259 * Sets the hot item index.
7262 * [I] infoPtr : valid pointer to the listview structure
7263 * [I] iIndex : index
7266 * SUCCESS : previous hot item index
7267 * FAILURE : -1 (no hot item)
7269 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
7271 INT iOldIndex
= infoPtr
->nHotItem
;
7273 infoPtr
->nHotItem
= iIndex
;
7281 * Sets the amount of time the cursor must hover over an item before it is selected.
7284 * [I] infoPtr : valid pointer to the listview structure
7285 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7288 * Returns the previous hover time
7290 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
7292 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
7294 infoPtr
->dwHoverTime
= dwHoverTime
;
7296 return oldHoverTime
;
7301 * Sets spacing for icons of LVS_ICON style.
7304 * [I] infoPtr : valid pointer to the listview structure
7305 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7306 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7309 * MAKELONG(oldcx, oldcy)
7311 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
7313 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
7314 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7316 TRACE("requested=(%d,%d)\n", cx
, cy
);
7318 /* this is supported only for LVS_ICON style */
7319 if (uView
!= LVS_ICON
) return oldspacing
;
7321 /* set to defaults, if instructed to */
7322 if (cx
== -1) cx
= GetSystemMetrics(SM_CXICONSPACING
);
7323 if (cy
== -1) cy
= GetSystemMetrics(SM_CYICONSPACING
);
7325 /* if 0 then compute width
7326 * FIXME: Should scan each item and determine max width of
7327 * icon or label, then make that the width */
7329 cx
= infoPtr
->iconSpacing
.cx
;
7331 /* if 0 then compute height */
7333 cy
= infoPtr
->iconSize
.cy
+ 2 * infoPtr
->ntmHeight
+
7334 ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_PADDING
;
7337 infoPtr
->iconSpacing
.cx
= cx
;
7338 infoPtr
->iconSpacing
.cy
= cy
;
7340 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7341 LOWORD(oldspacing
), HIWORD(oldspacing
), cx
, cy
,
7342 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
7343 infoPtr
->ntmHeight
);
7345 /* these depend on the iconSpacing */
7346 LISTVIEW_UpdateItemSize(infoPtr
);
7351 static inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL small
)
7355 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
7362 size
->cx
= GetSystemMetrics(small
? SM_CXSMICON
: SM_CXICON
);
7363 size
->cy
= GetSystemMetrics(small
? SM_CYSMICON
: SM_CYICON
);
7372 * [I] infoPtr : valid pointer to the listview structure
7373 * [I] nType : image list type
7374 * [I] himl : image list handle
7377 * SUCCESS : old image list
7380 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
7382 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7383 INT oldHeight
= infoPtr
->nItemHeight
;
7384 HIMAGELIST himlOld
= 0;
7386 TRACE("(nType=%d, himl=%p\n", nType
, himl
);
7391 himlOld
= infoPtr
->himlNormal
;
7392 infoPtr
->himlNormal
= himl
;
7393 if (uView
== LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
7394 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
7398 himlOld
= infoPtr
->himlSmall
;
7399 infoPtr
->himlSmall
= himl
;
7400 if (uView
!= LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
7404 himlOld
= infoPtr
->himlState
;
7405 infoPtr
->himlState
= himl
;
7406 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
7407 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
7411 ERR("Unknown icon type=%d\n", nType
);
7415 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
7416 if (infoPtr
->nItemHeight
!= oldHeight
)
7417 LISTVIEW_UpdateScroll(infoPtr
);
7424 * Preallocates memory (does *not* set the actual count of items !)
7427 * [I] infoPtr : valid pointer to the listview structure
7428 * [I] nItems : item count (projected number of items to allocate)
7429 * [I] dwFlags : update flags
7435 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
7437 TRACE("(nItems=%d, dwFlags=%x)\n", nItems
, dwFlags
);
7439 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
7441 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7442 INT nOldCount
= infoPtr
->nItemCount
;
7444 if (nItems
< nOldCount
)
7446 RANGE range
= { nItems
, nOldCount
};
7447 ranges_del(infoPtr
->selectionRanges
, range
);
7448 if (infoPtr
->nFocusedItem
>= nItems
)
7450 infoPtr
->nFocusedItem
= -1;
7451 SetRectEmpty(&infoPtr
->rcFocus
);
7455 infoPtr
->nItemCount
= nItems
;
7456 LISTVIEW_UpdateScroll(infoPtr
);
7458 /* the flags are valid only in ownerdata report and list modes */
7459 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
) dwFlags
= 0;
7461 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
7462 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
7464 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
7465 LISTVIEW_InvalidateList(infoPtr
);
7472 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7473 nFrom
= min(nOldCount
, nItems
);
7474 nTo
= max(nOldCount
, nItems
);
7476 if (uView
== LVS_REPORT
)
7479 rcErase
.top
= nFrom
* infoPtr
->nItemHeight
;
7480 rcErase
.right
= infoPtr
->nItemWidth
;
7481 rcErase
.bottom
= nTo
* infoPtr
->nItemHeight
;
7482 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7483 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7484 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7488 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
7490 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
7491 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
7492 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
7493 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7494 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7495 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7496 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7498 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7500 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7501 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7502 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7503 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7504 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7510 /* According to MSDN for non-LVS_OWNERDATA this is just
7511 * a performance issue. The control allocates its internal
7512 * data structures for the number of items specified. It
7513 * cuts down on the number of memory allocations. Therefore
7514 * we will just issue a WARN here
7516 WARN("for non-ownerdata performance option not implemented.\n");
7524 * Sets the position of an item.
7527 * [I] infoPtr : valid pointer to the listview structure
7528 * [I] nItem : item index
7529 * [I] pt : coordinate
7535 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, POINT pt
)
7537 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7540 TRACE("(nItem=%d, &pt=%s\n", nItem
, wine_dbgstr_point(&pt
));
7542 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
7543 !(uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)) return FALSE
;
7545 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7547 /* This point value seems to be an undocumented feature.
7548 * The best guess is that it means either at the origin,
7549 * or at true beginning of the list. I will assume the origin. */
7550 if ((pt
.x
== -1) && (pt
.y
== -1))
7553 if (uView
== LVS_ICON
)
7555 pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
7556 pt
.y
-= ICON_TOP_PADDING
;
7561 infoPtr
->bAutoarrange
= FALSE
;
7563 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, FALSE
);
7568 * Sets the state of one or many items.
7571 * [I] infoPtr : valid pointer to the listview structure
7572 * [I] nItem : item index
7573 * [I] lpLVItem : item or subitem info
7579 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
)
7581 BOOL bResult
= TRUE
;
7584 lvItem
.iItem
= nItem
;
7585 lvItem
.iSubItem
= 0;
7586 lvItem
.mask
= LVIF_STATE
;
7587 lvItem
.state
= lpLVItem
->state
;
7588 lvItem
.stateMask
= lpLVItem
->stateMask
;
7589 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
7593 /* apply to all items */
7594 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7595 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) bResult
= FALSE
;
7598 bResult
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
7601 * Update selection mark
7603 * Investigation on windows 2k showed that selection mark was updated
7604 * whenever a new selection was made, but if the selected item was
7605 * unselected it was not updated.
7607 * we are probably still not 100% accurate, but this at least sets the
7608 * proper selection mark when it is needed
7611 if (bResult
&& (lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) &&
7612 (infoPtr
->nSelectionMark
== -1))
7615 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
7617 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
7619 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
7621 infoPtr
->nSelectionMark
= i
;
7625 else if (ranges_contain(infoPtr
->selectionRanges
, i
))
7627 infoPtr
->nSelectionMark
= i
;
7638 * Sets the text of an item or subitem.
7641 * [I] hwnd : window handle
7642 * [I] nItem : item index
7643 * [I] lpLVItem : item or subitem info
7644 * [I] isW : TRUE if input is Unicode
7650 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
7654 if (nItem
< 0 && nItem
>= infoPtr
->nItemCount
) return FALSE
;
7656 lvItem
.iItem
= nItem
;
7657 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
7658 lvItem
.mask
= LVIF_TEXT
;
7659 lvItem
.pszText
= lpLVItem
->pszText
;
7660 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
7662 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
7664 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
7669 * Set item index that marks the start of a multiple selection.
7672 * [I] infoPtr : valid pointer to the listview structure
7673 * [I] nIndex : index
7676 * Index number or -1 if there is no selection mark.
7678 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
7680 INT nOldIndex
= infoPtr
->nSelectionMark
;
7682 TRACE("(nIndex=%d)\n", nIndex
);
7684 infoPtr
->nSelectionMark
= nIndex
;
7691 * Sets the text background color.
7694 * [I] infoPtr : valid pointer to the listview structure
7695 * [I] clrTextBk : text background color
7701 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrTextBk
)
7703 TRACE("(clrTextBk=%x)\n", clrTextBk
);
7705 if (infoPtr
->clrTextBk
!= clrTextBk
)
7707 infoPtr
->clrTextBk
= clrTextBk
;
7708 LISTVIEW_InvalidateList(infoPtr
);
7716 * Sets the text foreground color.
7719 * [I] infoPtr : valid pointer to the listview structure
7720 * [I] clrText : text color
7726 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF clrText
)
7728 TRACE("(clrText=%x)\n", clrText
);
7730 if (infoPtr
->clrText
!= clrText
)
7732 infoPtr
->clrText
= clrText
;
7733 LISTVIEW_InvalidateList(infoPtr
);
7741 * Determines which listview item is located at the specified position.
7744 * [I] infoPtr : valid pointer to the listview structure
7745 * [I] hwndNewToolTip : handle to new ToolTip
7750 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
7752 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
7753 infoPtr
->hwndToolTip
= hwndNewToolTip
;
7754 return hwndOldToolTip
;
7759 * sets the Unicode character format flag for the control
7761 * [I] infoPtr :valid pointer to the listview structure
7762 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7765 * Old Unicode Format
7767 static BOOL
LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO
*infoPtr
, BOOL fUnicode
)
7769 BOOL rc
= infoPtr
->notifyFormat
;
7770 infoPtr
->notifyFormat
= (fUnicode
)?NFR_UNICODE
:NFR_ANSI
;
7774 /* LISTVIEW_SetWorkAreas */
7778 * Callback internally used by LISTVIEW_SortItems()
7781 * [I] first : pointer to first ITEM_INFO to compare
7782 * [I] second : pointer to second ITEM_INFO to compare
7783 * [I] lParam : HWND of control
7786 * if first comes before second : negative
7787 * if first comes after second : positive
7788 * if first and second are equivalent : zero
7790 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
7792 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
7793 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
7794 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
7796 /* Forward the call to the client defined callback */
7797 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
7802 * Sorts the listview items.
7805 * [I] infoPtr : valid pointer to the listview structure
7806 * [I] pfnCompare : application-defined value
7807 * [I] lParamSort : pointer to comparison callback
7813 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
, LPARAM lParamSort
)
7815 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7818 LPVOID selectionMarkItem
;
7822 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
7824 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
7826 if (!pfnCompare
) return FALSE
;
7827 if (!infoPtr
->hdpaItems
) return FALSE
;
7829 /* if there are 0 or 1 items, there is no need to sort */
7830 if (infoPtr
->nItemCount
< 2) return TRUE
;
7832 if (infoPtr
->nFocusedItem
>= 0)
7834 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
7835 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
7836 if (lpItem
) lpItem
->state
|= LVIS_FOCUSED
;
7838 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7839 /* clear the lpItem->state for non-selected ones */
7840 /* remove the selection ranges */
7842 infoPtr
->pfnCompare
= pfnCompare
;
7843 infoPtr
->lParamSort
= lParamSort
;
7844 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
7846 /* Adjust selections and indices so that they are the way they should
7847 * be after the sort (otherwise, the list items move around, but
7848 * whatever is at the item's previous original position will be
7851 selectionMarkItem
=(infoPtr
->nSelectionMark
>=0)?DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
):NULL
;
7852 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
7854 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
7855 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
7857 if (lpItem
->state
& LVIS_SELECTED
)
7859 item
.state
= LVIS_SELECTED
;
7860 item
.stateMask
= LVIS_SELECTED
;
7861 LISTVIEW_SetItemState(infoPtr
, i
, &item
);
7863 if (lpItem
->state
& LVIS_FOCUSED
)
7865 infoPtr
->nFocusedItem
= i
;
7866 lpItem
->state
&= ~LVIS_FOCUSED
;
7869 if (selectionMarkItem
!= NULL
)
7870 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
7871 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7873 /* refresh the display */
7874 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
)
7875 LISTVIEW_InvalidateList(infoPtr
);
7882 * Update theme handle after a theme change.
7885 * [I] infoPtr : valid pointer to the listview structure
7889 * FAILURE : something else
7891 static LRESULT
LISTVIEW_ThemeChanged(const LISTVIEW_INFO
*infoPtr
)
7893 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
7894 CloseThemeData(theme
);
7895 OpenThemeData(infoPtr
->hwndSelf
, themeClass
);
7901 * Updates an items or rearranges the listview control.
7904 * [I] infoPtr : valid pointer to the listview structure
7905 * [I] nItem : item index
7911 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
7913 TRACE("(nItem=%d)\n", nItem
);
7915 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
7917 /* rearrange with default alignment style */
7918 if (is_autoarrange(infoPtr
))
7919 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
7921 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
7928 * Draw the track line at the place defined in the infoPtr structure.
7929 * The line is drawn with a XOR pen so drawing the line for the second time
7930 * in the same place erases the line.
7933 * [I] infoPtr : valid pointer to the listview structure
7939 static BOOL
LISTVIEW_DrawTrackLine(const LISTVIEW_INFO
*infoPtr
)
7945 if (infoPtr
->xTrackLine
== -1)
7948 if (!(hdc
= GetDC(infoPtr
->hwndSelf
)))
7950 hOldPen
= SelectObject(hdc
, GetStockObject(BLACK_PEN
));
7951 oldROP
= SetROP2(hdc
, R2_XORPEN
);
7952 MoveToEx(hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.top
, NULL
);
7953 LineTo(hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.bottom
);
7954 SetROP2(hdc
, oldROP
);
7955 SelectObject(hdc
, hOldPen
);
7956 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7962 * Called when an edit control should be displayed. This function is called after
7963 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7966 * [I] hwnd : Handle to the listview
7967 * [I] uMsg : WM_TIMER (ignored)
7968 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7969 * [I] dwTimer : The elapsed time (ignored)
7974 static VOID CALLBACK
LISTVIEW_DelayedEditItem(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
7976 DELAYED_ITEM_EDIT
*editItem
= (DELAYED_ITEM_EDIT
*)idEvent
;
7977 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
7979 KillTimer(hwnd
, idEvent
);
7980 editItem
->fEnabled
= FALSE
;
7981 /* check if the item is still selected */
7982 if (infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, editItem
->iItem
, LVIS_SELECTED
))
7983 LISTVIEW_EditLabelT(infoPtr
, editItem
->iItem
, TRUE
);
7988 * Creates the listview control - the WM_NCCREATE phase.
7991 * [I] hwnd : window handle
7992 * [I] lpcs : the create parameters
7998 static LRESULT
LISTVIEW_NCCreate(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
8000 LISTVIEW_INFO
*infoPtr
;
8003 TRACE("(lpcs=%p)\n", lpcs
);
8005 /* initialize info pointer */
8006 infoPtr
= Alloc(sizeof(LISTVIEW_INFO
));
8007 if (!infoPtr
) return FALSE
;
8009 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
8011 infoPtr
->hwndSelf
= hwnd
;
8012 infoPtr
->dwStyle
= lpcs
->style
; /* Note: may be changed in WM_CREATE */
8013 /* determine the type of structures to use */
8014 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
8015 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8017 /* initialize color information */
8018 infoPtr
->clrBk
= CLR_NONE
;
8019 infoPtr
->clrText
= CLR_DEFAULT
;
8020 infoPtr
->clrTextBk
= CLR_DEFAULT
;
8021 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
8023 /* set default values */
8024 infoPtr
->nFocusedItem
= -1;
8025 infoPtr
->nSelectionMark
= -1;
8026 infoPtr
->nHotItem
= -1;
8027 infoPtr
->bRedraw
= TRUE
;
8028 infoPtr
->bNoItemMetrics
= TRUE
;
8029 infoPtr
->bDoChangeNotify
= TRUE
;
8030 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
8031 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
8032 infoPtr
->nEditLabelItem
= -1;
8033 infoPtr
->dwHoverTime
= -1; /* default system hover time */
8034 infoPtr
->nMeasureItemHeight
= 0;
8035 infoPtr
->xTrackLine
= -1; /* no track line */
8036 infoPtr
->itemEdit
.fEnabled
= FALSE
;
8038 /* get default font (icon title) */
8039 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
8040 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
8041 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
8042 LISTVIEW_SaveTextMetrics(infoPtr
);
8044 /* allocate memory for the data structure */
8045 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
8046 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
8047 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
8048 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
8049 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
8053 DestroyWindow(infoPtr
->hwndHeader
);
8054 ranges_destroy(infoPtr
->selectionRanges
);
8055 DPA_Destroy(infoPtr
->hdpaItems
);
8056 DPA_Destroy(infoPtr
->hdpaPosX
);
8057 DPA_Destroy(infoPtr
->hdpaPosY
);
8058 DPA_Destroy(infoPtr
->hdpaColumns
);
8065 * Creates the listview control - the WM_CREATE phase. Most of the data is
8066 * already set up in LISTVIEW_NCCreate
8069 * [I] hwnd : window handle
8070 * [I] lpcs : the create parameters
8076 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
8078 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
8079 UINT uView
= lpcs
->style
& LVS_TYPEMASK
;
8080 DWORD dFlags
= WS_CHILD
| HDS_HORZ
| HDS_FULLDRAG
| HDS_DRAGDROP
;
8082 TRACE("(lpcs=%p)\n", lpcs
);
8084 infoPtr
->dwStyle
= lpcs
->style
;
8085 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
8086 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
8088 /* setup creation flags */
8089 dFlags
|= (LVS_NOSORTHEADER
& lpcs
->style
) ? 0 : HDS_BUTTONS
;
8090 dFlags
|= (LVS_NOCOLUMNHEADER
& lpcs
->style
) ? HDS_HIDDEN
: 0;
8093 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
, dFlags
,
8094 0, 0, 0, 0, hwnd
, NULL
,
8095 lpcs
->hInstance
, NULL
);
8096 if (!infoPtr
->hwndHeader
) return -1;
8098 /* set header unicode format */
8099 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, TRUE
, 0);
8101 /* set header font */
8102 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, (LPARAM
)TRUE
);
8104 /* init item size to avoid division by 0 */
8105 LISTVIEW_UpdateItemSize (infoPtr
);
8107 if (uView
== LVS_REPORT
)
8109 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
))
8111 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
8113 LISTVIEW_UpdateSize(infoPtr
);
8114 LISTVIEW_UpdateScroll(infoPtr
);
8117 OpenThemeData(hwnd
, themeClass
);
8119 /* initialize the icon sizes */
8120 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, uView
!= LVS_ICON
);
8121 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
8127 * Destroys the listview control.
8130 * [I] infoPtr : valid pointer to the listview structure
8136 static LRESULT
LISTVIEW_Destroy(const LISTVIEW_INFO
*infoPtr
)
8138 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
8139 CloseThemeData(theme
);
8145 * Enables the listview control.
8148 * [I] infoPtr : valid pointer to the listview structure
8149 * [I] bEnable : specifies whether to enable or disable the window
8155 static BOOL
LISTVIEW_Enable(const LISTVIEW_INFO
*infoPtr
, BOOL bEnable
)
8157 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
8158 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
8164 * Erases the background of the listview control.
8167 * [I] infoPtr : valid pointer to the listview structure
8168 * [I] hdc : device context handle
8174 static inline BOOL
LISTVIEW_EraseBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
8178 TRACE("(hdc=%p)\n", hdc
);
8180 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
8182 /* for double buffered controls we need to do this during refresh */
8183 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) return FALSE
;
8185 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
8191 * Helper function for LISTVIEW_[HV]Scroll *only*.
8192 * Performs vertical/horizontal scrolling by a give amount.
8195 * [I] infoPtr : valid pointer to the listview structure
8196 * [I] dx : amount of horizontal scroll
8197 * [I] dy : amount of vertical scroll
8199 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
8201 /* now we can scroll the list */
8202 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
8203 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
8204 /* if we have focus, adjust rect */
8205 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
8206 UpdateWindow(infoPtr
->hwndSelf
);
8211 * Performs vertical scrolling.
8214 * [I] infoPtr : valid pointer to the listview structure
8215 * [I] nScrollCode : scroll code
8216 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8217 * [I] hScrollWnd : scrollbar control window handle
8223 * SB_LINEUP/SB_LINEDOWN:
8224 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8225 * for LVS_REPORT is 1 line
8226 * for LVS_LIST cannot occur
8229 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
8230 INT nScrollDiff
, HWND hScrollWnd
)
8232 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8233 INT nOldScrollPos
, nNewScrollPos
;
8234 SCROLLINFO scrollInfo
;
8237 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
8238 debugscrollcode(nScrollCode
), nScrollDiff
);
8240 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8242 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8243 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
8245 is_an_icon
= ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
));
8247 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
8249 nOldScrollPos
= scrollInfo
.nPos
;
8250 switch (nScrollCode
)
8256 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
8260 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
8264 nScrollDiff
= -scrollInfo
.nPage
;
8268 nScrollDiff
= scrollInfo
.nPage
;
8271 case SB_THUMBPOSITION
:
8273 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
8280 /* quit right away if pos isn't changing */
8281 if (nScrollDiff
== 0) return 0;
8283 /* calculate new position, and handle overflows */
8284 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
8285 if (nScrollDiff
> 0) {
8286 if (nNewScrollPos
< nOldScrollPos
||
8287 nNewScrollPos
> scrollInfo
.nMax
)
8288 nNewScrollPos
= scrollInfo
.nMax
;
8290 if (nNewScrollPos
> nOldScrollPos
||
8291 nNewScrollPos
< scrollInfo
.nMin
)
8292 nNewScrollPos
= scrollInfo
.nMin
;
8295 /* set the new position, and reread in case it changed */
8296 scrollInfo
.fMask
= SIF_POS
;
8297 scrollInfo
.nPos
= nNewScrollPos
;
8298 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
8300 /* carry on only if it really changed */
8301 if (nNewScrollPos
== nOldScrollPos
) return 0;
8303 /* now adjust to client coordinates */
8304 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
8305 if (uView
== LVS_REPORT
) nScrollDiff
*= infoPtr
->nItemHeight
;
8307 /* and scroll the window */
8308 scroll_list(infoPtr
, 0, nScrollDiff
);
8315 * Performs horizontal scrolling.
8318 * [I] infoPtr : valid pointer to the listview structure
8319 * [I] nScrollCode : scroll code
8320 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8321 * [I] hScrollWnd : scrollbar control window handle
8327 * SB_LINELEFT/SB_LINERIGHT:
8328 * for LVS_ICON, LVS_SMALLICON 1 pixel
8329 * for LVS_REPORT is 1 pixel
8330 * for LVS_LIST is 1 column --> which is a 1 because the
8331 * scroll is based on columns not pixels
8334 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
8335 INT nScrollDiff
, HWND hScrollWnd
)
8337 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8338 INT nOldScrollPos
, nNewScrollPos
;
8339 SCROLLINFO scrollInfo
;
8341 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
8342 debugscrollcode(nScrollCode
), nScrollDiff
);
8344 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8346 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8347 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
8349 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
8351 nOldScrollPos
= scrollInfo
.nPos
;
8353 switch (nScrollCode
)
8367 nScrollDiff
= -scrollInfo
.nPage
;
8371 nScrollDiff
= scrollInfo
.nPage
;
8374 case SB_THUMBPOSITION
:
8376 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
8383 /* quit right away if pos isn't changing */
8384 if (nScrollDiff
== 0) return 0;
8386 /* calculate new position, and handle overflows */
8387 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
8388 if (nScrollDiff
> 0) {
8389 if (nNewScrollPos
< nOldScrollPos
||
8390 nNewScrollPos
> scrollInfo
.nMax
)
8391 nNewScrollPos
= scrollInfo
.nMax
;
8393 if (nNewScrollPos
> nOldScrollPos
||
8394 nNewScrollPos
< scrollInfo
.nMin
)
8395 nNewScrollPos
= scrollInfo
.nMin
;
8398 /* set the new position, and reread in case it changed */
8399 scrollInfo
.fMask
= SIF_POS
;
8400 scrollInfo
.nPos
= nNewScrollPos
;
8401 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
8403 /* carry on only if it really changed */
8404 if (nNewScrollPos
== nOldScrollPos
) return 0;
8406 if(uView
== LVS_REPORT
)
8407 LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
8409 /* now adjust to client coordinates */
8410 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
8411 if (uView
== LVS_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
8413 /* and scroll the window */
8414 scroll_list(infoPtr
, nScrollDiff
, 0);
8419 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
8421 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8422 INT gcWheelDelta
= 0;
8423 INT pulScrollLines
= 3;
8424 SCROLLINFO scrollInfo
;
8426 TRACE("(wheelDelta=%d)\n", wheelDelta
);
8428 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
8429 gcWheelDelta
-= wheelDelta
;
8431 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8432 scrollInfo
.fMask
= SIF_POS
;
8439 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8440 * should be fixed in the future.
8442 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (gcWheelDelta
< 0) ?
8443 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
, 0);
8447 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
8449 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
8450 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
8451 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, cLineScroll
, 0);
8456 LISTVIEW_HScroll(infoPtr
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0, 0);
8467 * [I] infoPtr : valid pointer to the listview structure
8468 * [I] nVirtualKey : virtual key
8469 * [I] lKeyData : key data
8474 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
8476 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8477 HWND hwndSelf
= infoPtr
->hwndSelf
;
8479 NMLVKEYDOWN nmKeyDown
;
8481 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey
, lKeyData
);
8483 /* send LVN_KEYDOWN notification */
8484 nmKeyDown
.wVKey
= nVirtualKey
;
8485 nmKeyDown
.flags
= 0;
8486 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
8487 if (!IsWindow(hwndSelf
))
8490 switch (nVirtualKey
)
8493 nItem
= infoPtr
->nFocusedItem
;
8494 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
8495 toggle_checkbox_state(infoPtr
, infoPtr
->nFocusedItem
);
8499 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
8501 if (!notify(infoPtr
, NM_RETURN
)) return 0;
8502 if (!notify(infoPtr
, LVN_ITEMACTIVATE
)) return 0;
8507 if (infoPtr
->nItemCount
> 0)
8512 if (infoPtr
->nItemCount
> 0)
8513 nItem
= infoPtr
->nItemCount
- 1;
8517 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
8521 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
8525 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
8529 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
8533 if (uView
== LVS_REPORT
)
8535 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8536 if (infoPtr
->nFocusedItem
== topidx
)
8537 nItem
= topidx
- LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
8542 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
8543 * LISTVIEW_GetCountPerRow(infoPtr
);
8544 if(nItem
< 0) nItem
= 0;
8548 if (uView
== LVS_REPORT
)
8550 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8551 INT cnt
= LISTVIEW_GetCountPerColumn(infoPtr
);
8552 if (infoPtr
->nFocusedItem
== topidx
+ cnt
- 1)
8553 nItem
= infoPtr
->nFocusedItem
+ cnt
- 1;
8555 nItem
= topidx
+ cnt
- 1;
8558 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
8559 * LISTVIEW_GetCountPerRow(infoPtr
);
8560 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
8564 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
|| nVirtualKey
== VK_SPACE
))
8565 LISTVIEW_KeySelection(infoPtr
, nItem
, nVirtualKey
== VK_SPACE
);
8575 * [I] infoPtr : valid pointer to the listview structure
8580 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
8584 /* if we did not have the focus, there's nothing to do */
8585 if (!infoPtr
->bFocus
) return 0;
8587 /* send NM_KILLFOCUS notification */
8588 if (!notify(infoPtr
, NM_KILLFOCUS
)) return 0;
8590 /* if we have a focus rectagle, get rid of it */
8591 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
8593 /* set window focus flag */
8594 infoPtr
->bFocus
= FALSE
;
8596 /* invalidate the selected items before resetting focus flag */
8597 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8604 * Processes double click messages (left mouse button).
8607 * [I] infoPtr : valid pointer to the listview structure
8608 * [I] wKey : key flag
8609 * [I] x,y : mouse coordinate
8614 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8616 LVHITTESTINFO htInfo
;
8618 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8620 /* Cancel the item edition if any */
8621 if (infoPtr
->itemEdit
.fEnabled
)
8623 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
)&infoPtr
->itemEdit
);
8624 infoPtr
->itemEdit
.fEnabled
= FALSE
;
8627 /* send NM_RELEASEDCAPTURE notification */
8628 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8633 /* send NM_DBLCLK notification */
8634 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
8635 if (!notify_click(infoPtr
, NM_DBLCLK
, &htInfo
)) return 0;
8637 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8638 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
8645 * Processes mouse down messages (left mouse button).
8648 * infoPtr [I ] valid pointer to the listview structure
8649 * wKey [I ] key flag
8650 * x,y [I ] mouse coordinate
8655 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8657 LVHITTESTINFO lvHitTestInfo
;
8658 static BOOL bGroupSelect
= TRUE
;
8659 POINT pt
= { x
, y
};
8662 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8664 /* send NM_RELEASEDCAPTURE notification */
8665 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8667 /* set left button down flag and record the click position */
8668 infoPtr
->bLButtonDown
= TRUE
;
8669 infoPtr
->ptClickPos
= pt
;
8670 infoPtr
->bDragging
= FALSE
;
8672 lvHitTestInfo
.pt
.x
= x
;
8673 lvHitTestInfo
.pt
.y
= y
;
8675 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8676 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt
), nItem
);
8677 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8679 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
8681 toggle_checkbox_state(infoPtr
, nItem
);
8685 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
8687 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8688 infoPtr
->nEditLabelItem
= nItem
;
8690 LISTVIEW_SetSelection(infoPtr
, nItem
);
8694 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
8698 if (!LISTVIEW_AddGroupSelection(infoPtr
, nItem
)) return 0;
8699 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8700 infoPtr
->nSelectionMark
= nItem
;
8706 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
8707 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8709 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
8710 infoPtr
->nSelectionMark
= nItem
;
8713 else if (wKey
& MK_CONTROL
)
8717 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
8719 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
8720 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8721 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
8722 infoPtr
->nSelectionMark
= nItem
;
8724 else if (wKey
& MK_SHIFT
)
8726 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
8730 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8731 infoPtr
->nEditLabelItem
= nItem
;
8733 /* set selection (clears other pre-existing selections) */
8734 LISTVIEW_SetSelection(infoPtr
, nItem
);
8738 if (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
)
8739 if(lvHitTestInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&lvHitTestInfo
);
8743 /* remove all selections */
8744 LISTVIEW_DeselectAll(infoPtr
);
8753 * Processes mouse up messages (left mouse button).
8756 * infoPtr [I ] valid pointer to the listview structure
8757 * wKey [I ] key flag
8758 * x,y [I ] mouse coordinate
8763 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8765 LVHITTESTINFO lvHitTestInfo
;
8767 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8769 if (!infoPtr
->bLButtonDown
) return 0;
8771 lvHitTestInfo
.pt
.x
= x
;
8772 lvHitTestInfo
.pt
.y
= y
;
8774 /* send NM_CLICK notification */
8775 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8776 if (!notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
)) return 0;
8778 /* set left button flag */
8779 infoPtr
->bLButtonDown
= FALSE
;
8781 if (infoPtr
->bDragging
)
8783 infoPtr
->bDragging
= FALSE
;
8787 /* if we clicked on a selected item, edit the label */
8788 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
8790 /* we want to make sure the user doesn't want to do a double click. So we will
8791 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8793 infoPtr
->itemEdit
.fEnabled
= TRUE
;
8794 infoPtr
->itemEdit
.iItem
= lvHitTestInfo
.iItem
;
8795 SetTimer(infoPtr
->hwndSelf
,
8796 (UINT_PTR
)&infoPtr
->itemEdit
,
8797 GetDoubleClickTime(),
8798 LISTVIEW_DelayedEditItem
);
8801 if (!infoPtr
->bFocus
)
8802 SetFocus(infoPtr
->hwndSelf
);
8809 * Destroys the listview control (called after WM_DESTROY).
8812 * [I] infoPtr : valid pointer to the listview structure
8817 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
8821 /* delete all items */
8822 LISTVIEW_DeleteAllItems(infoPtr
, TRUE
);
8824 /* destroy data structure */
8825 DPA_Destroy(infoPtr
->hdpaItems
);
8826 DPA_Destroy(infoPtr
->hdpaPosX
);
8827 DPA_Destroy(infoPtr
->hdpaPosY
);
8828 DPA_Destroy(infoPtr
->hdpaColumns
);
8829 ranges_destroy(infoPtr
->selectionRanges
);
8831 /* destroy image lists */
8832 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
8834 if (infoPtr
->himlNormal
)
8835 ImageList_Destroy(infoPtr
->himlNormal
);
8836 if (infoPtr
->himlSmall
)
8837 ImageList_Destroy(infoPtr
->himlSmall
);
8838 if (infoPtr
->himlState
)
8839 ImageList_Destroy(infoPtr
->himlState
);
8842 /* destroy font, bkgnd brush */
8844 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
8845 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
8847 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
8849 /* free listview info pointer*/
8857 * Handles notifications from header.
8860 * [I] infoPtr : valid pointer to the listview structure
8861 * [I] nCtrlId : control identifier
8862 * [I] lpnmh : notification information
8867 static LRESULT
LISTVIEW_HeaderNotification(LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
8869 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8870 HWND hwndSelf
= infoPtr
->hwndSelf
;
8872 TRACE("(lpnmh=%p)\n", lpnmh
);
8874 if (!lpnmh
|| lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
8876 switch (lpnmh
->hdr
.code
)
8881 COLUMN_INFO
*lpColumnInfo
;
8885 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8888 /* remove the old line (if any) */
8889 LISTVIEW_DrawTrackLine(infoPtr
);
8891 /* compute & draw the new line */
8892 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8893 x
= lpColumnInfo
->rcHeader
.left
+ lpnmh
->pitem
->cxy
;
8894 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
8895 infoPtr
->xTrackLine
= x
+ ptOrigin
.x
;
8896 LISTVIEW_DrawTrackLine(infoPtr
);
8902 /* remove the track line (if any) */
8903 LISTVIEW_DrawTrackLine(infoPtr
);
8904 infoPtr
->xTrackLine
= -1;
8908 FIXME("Changing column order not implemented\n");
8911 case HDN_ITEMCHANGINGW
:
8912 case HDN_ITEMCHANGINGA
:
8913 return notify_forward_header(infoPtr
, lpnmh
);
8915 case HDN_ITEMCHANGEDW
:
8916 case HDN_ITEMCHANGEDA
:
8918 COLUMN_INFO
*lpColumnInfo
;
8921 notify_forward_header(infoPtr
, lpnmh
);
8922 if (!IsWindow(hwndSelf
))
8925 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8929 hdi
.mask
= HDI_WIDTH
;
8930 if (!Header_GetItemW(infoPtr
->hwndHeader
, lpnmh
->iItem
, &hdi
)) return 0;
8934 cxy
= lpnmh
->pitem
->cxy
;
8936 /* determine how much we change since the last know position */
8937 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8938 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
8941 lpColumnInfo
->rcHeader
.right
+= dx
;
8942 if (lpnmh
->iItem
+ 1 < DPA_GetPtrCount(infoPtr
->hdpaColumns
))
8943 LISTVIEW_ScrollColumns(infoPtr
, lpnmh
->iItem
+ 1, dx
);
8946 /* only needs to update the scrolls */
8947 infoPtr
->nItemWidth
+= dx
;
8948 LISTVIEW_UpdateScroll(infoPtr
);
8950 LISTVIEW_UpdateItemSize(infoPtr
);
8951 if (uView
== LVS_REPORT
&& is_redrawing(infoPtr
))
8954 RECT rcCol
= lpColumnInfo
->rcHeader
;
8956 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
8957 OffsetRect(&rcCol
, ptOrigin
.x
, 0);
8959 rcCol
.top
= infoPtr
->rcList
.top
;
8960 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
8962 /* resizing left-aligned columns leaves most of the left side untouched */
8963 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
8965 INT nMaxDirty
= infoPtr
->nEllipsisWidth
+ infoPtr
->ntmMaxCharWidth
;
8968 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- nMaxDirty
);
8971 /* when shrinking the last column clear the now unused field */
8972 if (lpnmh
->iItem
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1 && dx
< 0)
8975 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
8981 case HDN_ITEMCLICKW
:
8982 case HDN_ITEMCLICKA
:
8984 /* Handle sorting by Header Column */
8987 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
8989 nmlv
.iSubItem
= lpnmh
->iItem
;
8990 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
8991 notify_forward_header(infoPtr
, lpnmh
);
8995 case HDN_DIVIDERDBLCLICKW
:
8996 case HDN_DIVIDERDBLCLICKA
:
8997 LISTVIEW_SetColumnWidth(infoPtr
, lpnmh
->iItem
, LVSCW_AUTOSIZE
);
9006 * Paint non-client area of control.
9009 * [I] infoPtr : valid pointer to the listview structureof the sender
9010 * [I] region : update region
9013 * TRUE - frame was painted
9014 * FALSE - call default window proc
9016 static BOOL
LISTVIEW_NCPaint(const LISTVIEW_INFO
*infoPtr
, HRGN region
)
9018 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
9022 int cxEdge
= GetSystemMetrics (SM_CXEDGE
),
9023 cyEdge
= GetSystemMetrics (SM_CYEDGE
);
9025 if (!theme
) return FALSE
;
9027 GetWindowRect(infoPtr
->hwndSelf
, &r
);
9029 cliprgn
= CreateRectRgn (r
.left
+ cxEdge
, r
.top
+ cyEdge
,
9030 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
9031 if (region
!= (HRGN
)1)
9032 CombineRgn (cliprgn
, cliprgn
, region
, RGN_AND
);
9033 OffsetRect(&r
, -r
.left
, -r
.top
);
9035 dc
= GetDCEx(infoPtr
->hwndSelf
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
9036 OffsetRect(&r
, -r
.left
, -r
.top
);
9038 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
9039 DrawThemeParentBackground(infoPtr
->hwndSelf
, dc
, &r
);
9040 DrawThemeBackground (theme
, dc
, 0, 0, &r
, 0);
9041 ReleaseDC(infoPtr
->hwndSelf
, dc
);
9043 /* Call default proc to get the scrollbars etc. painted */
9044 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)cliprgn
, 0);
9051 * Determines the type of structure to use.
9054 * [I] infoPtr : valid pointer to the listview structureof the sender
9055 * [I] hwndFrom : listview window handle
9056 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9061 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
9063 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
9065 if (nCommand
== NF_REQUERY
)
9066 infoPtr
->notifyFormat
= SendMessageW(hwndFrom
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
9068 return infoPtr
->notifyFormat
;
9073 * Paints/Repaints the listview control.
9076 * [I] infoPtr : valid pointer to the listview structure
9077 * [I] hdc : device context handle
9082 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
9084 TRACE("(hdc=%p)\n", hdc
);
9086 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
9088 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9090 infoPtr
->bNoItemMetrics
= FALSE
;
9091 LISTVIEW_UpdateItemSize(infoPtr
);
9092 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
9093 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9094 LISTVIEW_UpdateScroll(infoPtr
);
9097 UpdateWindow(infoPtr
->hwndHeader
);
9100 LISTVIEW_Refresh(infoPtr
, hdc
, NULL
);
9105 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
9107 LISTVIEW_Refresh(infoPtr
, hdc
, ps
.fErase
? &ps
.rcPaint
: NULL
);
9108 EndPaint(infoPtr
->hwndSelf
, &ps
);
9117 * Paints/Repaints the listview control.
9120 * [I] infoPtr : valid pointer to the listview structure
9121 * [I] hdc : device context handle
9122 * [I] options : drawing options
9127 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
9129 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc
, options
);
9131 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
9134 if (options
& PRF_ERASEBKGND
)
9135 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
9137 if (options
& PRF_CLIENT
)
9138 LISTVIEW_Paint(infoPtr
, hdc
);
9146 * Processes double click messages (right mouse button).
9149 * [I] infoPtr : valid pointer to the listview structure
9150 * [I] wKey : key flag
9151 * [I] x,y : mouse coordinate
9156 static LRESULT
LISTVIEW_RButtonDblClk(const LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9158 LVHITTESTINFO lvHitTestInfo
;
9160 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9162 /* send NM_RELEASEDCAPTURE notification */
9163 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9165 /* send NM_RDBLCLK notification */
9166 lvHitTestInfo
.pt
.x
= x
;
9167 lvHitTestInfo
.pt
.y
= y
;
9168 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9169 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
9176 * Processes mouse down messages (right mouse button).
9179 * [I] infoPtr : valid pointer to the listview structure
9180 * [I] wKey : key flag
9181 * [I] x,y : mouse coordinate
9186 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9188 LVHITTESTINFO lvHitTestInfo
;
9191 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9193 /* send NM_RELEASEDCAPTURE notification */
9194 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9196 /* make sure the listview control window has the focus */
9197 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
9199 /* set right button down flag */
9200 infoPtr
->bRButtonDown
= TRUE
;
9202 /* determine the index of the selected item */
9203 lvHitTestInfo
.pt
.x
= x
;
9204 lvHitTestInfo
.pt
.y
= y
;
9205 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
9207 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
9209 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
9210 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
9211 !LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
9212 LISTVIEW_SetSelection(infoPtr
, nItem
);
9216 LISTVIEW_DeselectAll(infoPtr
);
9224 * Processes mouse up messages (right mouse button).
9227 * [I] infoPtr : valid pointer to the listview structure
9228 * [I] wKey : key flag
9229 * [I] x,y : mouse coordinate
9234 static LRESULT
LISTVIEW_RButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9236 LVHITTESTINFO lvHitTestInfo
;
9239 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9241 if (!infoPtr
->bRButtonDown
) return 0;
9243 /* set button flag */
9244 infoPtr
->bRButtonDown
= FALSE
;
9246 /* Send NM_RClICK notification */
9247 lvHitTestInfo
.pt
.x
= x
;
9248 lvHitTestInfo
.pt
.y
= y
;
9249 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9250 if (!notify_click(infoPtr
, NM_RCLICK
, &lvHitTestInfo
)) return 0;
9252 /* Change to screen coordinate for WM_CONTEXTMENU */
9253 pt
= lvHitTestInfo
.pt
;
9254 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
9256 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9257 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
9258 (WPARAM
)infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
9269 * [I] infoPtr : valid pointer to the listview structure
9270 * [I] hwnd : window handle of window containing the cursor
9271 * [I] nHittest : hit-test code
9272 * [I] wMouseMsg : ideintifier of the mouse message
9275 * TRUE if cursor is set
9278 static BOOL
LISTVIEW_SetCursor(const LISTVIEW_INFO
*infoPtr
, HWND hwnd
, UINT nHittest
, UINT wMouseMsg
)
9280 LVHITTESTINFO lvHitTestInfo
;
9282 if(!(LISTVIEW_isHotTracking(infoPtr
))) return FALSE
;
9284 if(!infoPtr
->hHotCursor
) return FALSE
;
9286 GetCursorPos(&lvHitTestInfo
.pt
);
9287 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) return FALSE
;
9289 SetCursor(infoPtr
->hHotCursor
);
9299 * [I] infoPtr : valid pointer to the listview structure
9300 * [I] hwndLoseFocus : handle of previously focused window
9305 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
9307 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
9309 /* if we have the focus already, there's nothing to do */
9310 if (infoPtr
->bFocus
) return 0;
9312 /* send NM_SETFOCUS notification */
9313 if (!notify(infoPtr
, NM_SETFOCUS
)) return 0;
9315 /* set window focus flag */
9316 infoPtr
->bFocus
= TRUE
;
9318 /* put the focus rect back on */
9319 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
9321 /* redraw all visible selected items */
9322 LISTVIEW_InvalidateSelectedItems(infoPtr
);
9332 * [I] infoPtr : valid pointer to the listview structure
9333 * [I] fRedraw : font handle
9334 * [I] fRedraw : redraw flag
9339 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
9341 HFONT oldFont
= infoPtr
->hFont
;
9343 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
9345 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
9346 if (infoPtr
->hFont
== oldFont
) return 0;
9348 LISTVIEW_SaveTextMetrics(infoPtr
);
9350 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
)
9352 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
9353 LISTVIEW_UpdateSize(infoPtr
);
9354 LISTVIEW_UpdateScroll(infoPtr
);
9357 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
9364 * Message handling for WM_SETREDRAW.
9365 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9368 * [I] infoPtr : valid pointer to the listview structure
9369 * [I] bRedraw: state of redraw flag
9372 * DefWinProc return value
9374 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
9376 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr
->bRedraw
, bRedraw
);
9378 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9379 if ((infoPtr
->bRedraw
&& bRedraw
) || (!infoPtr
->bRedraw
&& !bRedraw
)) return 0;
9381 infoPtr
->bRedraw
= bRedraw
;
9383 if(!bRedraw
) return 0;
9385 if (is_autoarrange(infoPtr
))
9386 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9387 LISTVIEW_UpdateScroll(infoPtr
);
9389 /* despite what the WM_SETREDRAW docs says, apps expect us
9390 * to invalidate the listview here... stupid! */
9391 LISTVIEW_InvalidateList(infoPtr
);
9398 * Resizes the listview control. This function processes WM_SIZE
9399 * messages. At this time, the width and height are not used.
9402 * [I] infoPtr : valid pointer to the listview structure
9403 * [I] Width : new width
9404 * [I] Height : new height
9409 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
9411 RECT rcOld
= infoPtr
->rcList
;
9413 TRACE("(width=%d, height=%d)\n", Width
, Height
);
9415 LISTVIEW_UpdateSize(infoPtr
);
9416 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
9418 /* do not bother with display related stuff if we're not redrawing */
9419 if (!is_redrawing(infoPtr
)) return 0;
9421 if (is_autoarrange(infoPtr
))
9422 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9424 LISTVIEW_UpdateScroll(infoPtr
);
9426 /* refresh all only for lists whose height changed significantly */
9427 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
&&
9428 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
9429 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
9430 LISTVIEW_InvalidateList(infoPtr
);
9437 * Sets the size information.
9440 * [I] infoPtr : valid pointer to the listview structure
9445 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
9447 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9449 TRACE("uView=%d, rcList(old)=%s\n", uView
, wine_dbgstr_rect(&infoPtr
->rcList
));
9451 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
9453 if (uView
== LVS_LIST
)
9455 /* Apparently the "LIST" style is supposed to have the same
9456 * number of items in a column even if there is no scroll bar.
9457 * Since if a scroll bar already exists then the bottom is already
9458 * reduced, only reduce if the scroll bar does not currently exist.
9459 * The "2" is there to mimic the native control. I think it may be
9460 * related to either padding or edges. (GLA 7/2002)
9462 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_HSCROLL
))
9463 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
9464 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
9466 else if (uView
== LVS_REPORT
)
9471 hl
.prc
= &infoPtr
->rcList
;
9473 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9474 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp
.flags
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
9475 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9476 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
9477 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9478 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
9480 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
9481 infoPtr
->rcList
.top
+= (infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
) ? 2 : 0;
9484 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr
->rcList
));
9489 * Processes WM_STYLECHANGED messages.
9492 * [I] infoPtr : valid pointer to the listview structure
9493 * [I] wStyleType : window style type (normal or extended)
9494 * [I] lpss : window style information
9499 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
9500 const STYLESTRUCT
*lpss
)
9502 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
9503 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
9506 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9507 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
9509 if (wStyleType
!= GWL_STYLE
) return 0;
9511 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9512 /* what if LVS_OWNERDATA changed? */
9513 /* or LVS_SINGLESEL */
9514 /* or LVS_SORT{AS,DES}CENDING */
9516 infoPtr
->dwStyle
= lpss
->styleNew
;
9518 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
9519 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
9520 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
9522 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
9523 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
9524 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
9526 if (uNewView
!= uOldView
)
9528 SIZE oldIconSize
= infoPtr
->iconSize
;
9531 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9532 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
9534 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
9535 SetRectEmpty(&infoPtr
->rcFocus
);
9537 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
9538 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
9540 if (uNewView
== LVS_ICON
)
9542 if ((infoPtr
->iconSize
.cx
!= oldIconSize
.cx
) || (infoPtr
->iconSize
.cy
!= oldIconSize
.cy
))
9544 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9545 oldIconSize
.cx
, oldIconSize
.cy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
9546 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
9549 else if (uNewView
== LVS_REPORT
)
9554 hl
.prc
= &infoPtr
->rcList
;
9556 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9557 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9558 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
9559 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9562 LISTVIEW_UpdateItemSize(infoPtr
);
9565 if (uNewView
== LVS_REPORT
)
9567 if ((lpss
->styleOld
^ lpss
->styleNew
) & LVS_NOCOLUMNHEADER
)
9569 if (lpss
->styleNew
& LVS_NOCOLUMNHEADER
)
9571 /* Turn off the header control */
9572 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
9573 TRACE("Hide header control, was 0x%08x\n", style
);
9574 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
| HDS_HIDDEN
);
9576 /* Turn on the header control */
9577 if ((style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
)) & HDS_HIDDEN
)
9579 TRACE("Show header control, was 0x%08x\n", style
);
9580 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, (style
& ~HDS_HIDDEN
) | WS_VISIBLE
);
9586 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
9587 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
9588 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9590 /* update the size of the client area */
9591 LISTVIEW_UpdateSize(infoPtr
);
9593 /* add scrollbars if needed */
9594 LISTVIEW_UpdateScroll(infoPtr
);
9596 /* invalidate client area + erase background */
9597 LISTVIEW_InvalidateList(infoPtr
);
9604 * Window procedure of the listview control.
9607 static LRESULT WINAPI
9608 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9610 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9612 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg
, wParam
, lParam
);
9614 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
9615 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9619 case LVM_APPROXIMATEVIEWRECT
:
9620 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
9621 LOWORD(lParam
), HIWORD(lParam
));
9623 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
9625 /* case LVM_CANCELEDITLABEL: */
9627 case LVM_CREATEDRAGIMAGE
:
9628 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9630 case LVM_DELETEALLITEMS
:
9631 return LISTVIEW_DeleteAllItems(infoPtr
, FALSE
);
9633 case LVM_DELETECOLUMN
:
9634 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
9636 case LVM_DELETEITEM
:
9637 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
9639 case LVM_EDITLABELW
:
9640 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, TRUE
);
9642 case LVM_EDITLABELA
:
9643 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, FALSE
);
9645 /* case LVM_ENABLEGROUPVIEW: */
9647 case LVM_ENSUREVISIBLE
:
9648 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
9651 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
9654 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
9656 case LVM_GETBKCOLOR
:
9657 return infoPtr
->clrBk
;
9659 /* case LVM_GETBKIMAGE: */
9661 case LVM_GETCALLBACKMASK
:
9662 return infoPtr
->uCallbackMask
;
9664 case LVM_GETCOLUMNA
:
9665 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9667 case LVM_GETCOLUMNW
:
9668 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9670 case LVM_GETCOLUMNORDERARRAY
:
9671 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9673 case LVM_GETCOLUMNWIDTH
:
9674 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
9676 case LVM_GETCOUNTPERPAGE
:
9677 return LISTVIEW_GetCountPerPage(infoPtr
);
9679 case LVM_GETEDITCONTROL
:
9680 return (LRESULT
)infoPtr
->hwndEdit
;
9682 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
9683 return infoPtr
->dwLvExStyle
;
9685 /* case LVM_GETGROUPINFO: */
9687 /* case LVM_GETGROUPMETRICS: */
9690 return (LRESULT
)infoPtr
->hwndHeader
;
9692 case LVM_GETHOTCURSOR
:
9693 return (LRESULT
)infoPtr
->hHotCursor
;
9695 case LVM_GETHOTITEM
:
9696 return infoPtr
->nHotItem
;
9698 case LVM_GETHOVERTIME
:
9699 return infoPtr
->dwHoverTime
;
9701 case LVM_GETIMAGELIST
:
9702 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
9704 /* case LVM_GETINSERTMARK: */
9706 /* case LVM_GETINSERTMARKCOLOR: */
9708 /* case LVM_GETINSERTMARKRECT: */
9710 case LVM_GETISEARCHSTRINGA
:
9711 case LVM_GETISEARCHSTRINGW
:
9712 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9716 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9719 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9721 case LVM_GETITEMCOUNT
:
9722 return infoPtr
->nItemCount
;
9724 case LVM_GETITEMPOSITION
:
9725 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9727 case LVM_GETITEMRECT
:
9728 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
9730 case LVM_GETITEMSPACING
:
9731 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
9733 case LVM_GETITEMSTATE
:
9734 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
9736 case LVM_GETITEMTEXTA
:
9737 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9739 case LVM_GETITEMTEXTW
:
9740 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9742 case LVM_GETNEXTITEM
:
9743 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
9745 case LVM_GETNUMBEROFWORKAREAS
:
9746 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9750 if (!lParam
) return FALSE
;
9751 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
||
9752 (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
) return FALSE
;
9753 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
9756 /* case LVM_GETOUTLINECOLOR: */
9758 /* case LVM_GETSELECTEDCOLUMN: */
9760 case LVM_GETSELECTEDCOUNT
:
9761 return LISTVIEW_GetSelectedCount(infoPtr
);
9763 case LVM_GETSELECTIONMARK
:
9764 return infoPtr
->nSelectionMark
;
9766 case LVM_GETSTRINGWIDTHA
:
9767 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, FALSE
);
9769 case LVM_GETSTRINGWIDTHW
:
9770 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, TRUE
);
9772 case LVM_GETSUBITEMRECT
:
9773 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
9775 case LVM_GETTEXTBKCOLOR
:
9776 return infoPtr
->clrTextBk
;
9778 case LVM_GETTEXTCOLOR
:
9779 return infoPtr
->clrText
;
9781 /* case LVM_GETTILEINFO: */
9783 /* case LVM_GETTILEVIEWINFO: */
9785 case LVM_GETTOOLTIPS
:
9786 if( !infoPtr
->hwndToolTip
)
9787 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
9788 return (LRESULT
)infoPtr
->hwndToolTip
;
9790 case LVM_GETTOPINDEX
:
9791 return LISTVIEW_GetTopIndex(infoPtr
);
9793 case LVM_GETUNICODEFORMAT
:
9794 return (infoPtr
->notifyFormat
== NFR_UNICODE
);
9796 /* case LVM_GETVIEW: */
9798 case LVM_GETVIEWRECT
:
9799 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
9801 case LVM_GETWORKAREAS
:
9802 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9805 /* case LVM_HASGROUP: */
9808 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, FALSE
);
9810 case LVM_INSERTCOLUMNA
:
9811 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9813 case LVM_INSERTCOLUMNW
:
9814 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9816 /* case LVM_INSERTGROUP: */
9818 /* case LVM_INSERTGROUPSORTED: */
9820 case LVM_INSERTITEMA
:
9821 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9823 case LVM_INSERTITEMW
:
9824 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9826 /* case LVM_INSERTMARKHITTEST: */
9828 /* case LVM_ISGROUPVIEWENABLED: */
9830 /* case LVM_MAPIDTOINDEX: */
9832 /* case LVM_MAPINDEXTOID: */
9834 /* case LVM_MOVEGROUP: */
9836 /* case LVM_MOVEITEMTOGROUP: */
9838 case LVM_REDRAWITEMS
:
9839 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9841 /* case LVM_REMOVEALLGROUPS: */
9843 /* case LVM_REMOVEGROUP: */
9846 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9848 case LVM_SETBKCOLOR
:
9849 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
9851 /* case LVM_SETBKIMAGE: */
9853 case LVM_SETCALLBACKMASK
:
9854 infoPtr
->uCallbackMask
= (UINT
)wParam
;
9857 case LVM_SETCOLUMNA
:
9858 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9860 case LVM_SETCOLUMNW
:
9861 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9863 case LVM_SETCOLUMNORDERARRAY
:
9864 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9866 case LVM_SETCOLUMNWIDTH
:
9867 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
9869 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
9870 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
9872 /* case LVM_SETGROUPINFO: */
9874 /* case LVM_SETGROUPMETRICS: */
9876 case LVM_SETHOTCURSOR
:
9877 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
9879 case LVM_SETHOTITEM
:
9880 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
9882 case LVM_SETHOVERTIME
:
9883 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)wParam
);
9885 case LVM_SETICONSPACING
:
9886 return LISTVIEW_SetIconSpacing(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
9888 case LVM_SETIMAGELIST
:
9889 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
9891 /* case LVM_SETINFOTIP: */
9893 /* case LVM_SETINSERTMARK: */
9895 /* case LVM_SETINSERTMARKCOLOR: */
9898 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9901 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9903 case LVM_SETITEMCOUNT
:
9904 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
9906 case LVM_SETITEMPOSITION
:
9909 pt
.x
= (short)LOWORD(lParam
);
9910 pt
.y
= (short)HIWORD(lParam
);
9911 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, pt
);
9914 case LVM_SETITEMPOSITION32
:
9915 if (lParam
== 0) return FALSE
;
9916 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, *((POINT
*)lParam
));
9918 case LVM_SETITEMSTATE
:
9919 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
9921 case LVM_SETITEMTEXTA
:
9922 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9924 case LVM_SETITEMTEXTW
:
9925 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9927 /* case LVM_SETOUTLINECOLOR: */
9929 /* case LVM_SETSELECTEDCOLUMN: */
9931 case LVM_SETSELECTIONMARK
:
9932 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
9934 case LVM_SETTEXTBKCOLOR
:
9935 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
9937 case LVM_SETTEXTCOLOR
:
9938 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
9940 /* case LVM_SETTILEINFO: */
9942 /* case LVM_SETTILEVIEWINFO: */
9944 /* case LVM_SETTILEWIDTH: */
9946 case LVM_SETTOOLTIPS
:
9947 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
9949 case LVM_SETUNICODEFORMAT
:
9950 return LISTVIEW_SetUnicodeFormat(infoPtr
, wParam
);
9952 /* case LVM_SETVIEW: */
9954 /* case LVM_SETWORKAREAS: */
9956 /* case LVM_SORTGROUPS: */
9959 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
);
9961 /* LVM_SORTITEMSEX: */
9963 case LVM_SUBITEMHITTEST
:
9964 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
9967 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
9970 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
9973 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
9976 return LISTVIEW_NCCreate(hwnd
, (LPCREATESTRUCTW
)lParam
);
9979 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
9982 return LISTVIEW_Destroy(infoPtr
);
9985 return LISTVIEW_Enable(infoPtr
, (BOOL
)wParam
);
9988 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
9991 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
9994 return (LRESULT
)infoPtr
->hFont
;
9997 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
10000 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
10003 return LISTVIEW_KillFocus(infoPtr
);
10005 case WM_LBUTTONDBLCLK
:
10006 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10008 case WM_LBUTTONDOWN
:
10009 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10012 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10015 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10017 case WM_MOUSEHOVER
:
10018 return LISTVIEW_MouseHover(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10021 return LISTVIEW_NCDestroy(infoPtr
);
10024 if (LISTVIEW_NCPaint(infoPtr
, (HRGN
)wParam
))
10029 if (lParam
&& ((LPNMHDR
)lParam
)->hwndFrom
== infoPtr
->hwndHeader
)
10030 return LISTVIEW_HeaderNotification(infoPtr
, (LPNMHEADERW
)lParam
);
10033 case WM_NOTIFYFORMAT
:
10034 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
10036 case WM_PRINTCLIENT
:
10037 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
10040 return LISTVIEW_Paint(infoPtr
, (HDC
)wParam
);
10042 case WM_RBUTTONDBLCLK
:
10043 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10045 case WM_RBUTTONDOWN
:
10046 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10049 return LISTVIEW_RButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10052 if(LISTVIEW_SetCursor(infoPtr
, (HWND
)wParam
, LOWORD(lParam
), HIWORD(lParam
)))
10057 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
10060 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
10063 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
10066 return LISTVIEW_Size(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
10068 case WM_STYLECHANGED
:
10069 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
10071 case WM_SYSCOLORCHANGE
:
10072 COMCTL32_RefreshSysColors();
10075 /* case WM_TIMER: */
10076 case WM_THEMECHANGED
:
10077 return LISTVIEW_ThemeChanged(infoPtr
);
10080 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
10082 case WM_MOUSEWHEEL
:
10083 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
10084 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10085 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
10087 case WM_WINDOWPOSCHANGED
:
10088 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
10090 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
10091 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
10092 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
10094 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
10096 MEASUREITEMSTRUCT mis
;
10097 mis
.CtlType
= ODT_LISTVIEW
;
10098 mis
.CtlID
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
10102 mis
.itemHeight
= infoPtr
->nItemHeight
;
10103 SendMessageW(infoPtr
->hwndNotify
, WM_MEASUREITEM
, mis
.CtlID
, (LPARAM
)&mis
);
10104 if (infoPtr
->nItemHeight
!= max(mis
.itemHeight
, 1))
10105 infoPtr
->nMeasureItemHeight
= infoPtr
->nItemHeight
= max(mis
.itemHeight
, 1);
10108 LISTVIEW_UpdateSize(infoPtr
);
10109 LISTVIEW_UpdateScroll(infoPtr
);
10111 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10113 /* case WM_WININICHANGE: */
10116 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
) && !COMCTL32_IsReflectedMessage(uMsg
))
10117 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg
, wParam
, lParam
);
10120 /* call default window procedure */
10121 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10128 * Registers the window class.
10136 void LISTVIEW_Register(void)
10138 WNDCLASSW wndClass
;
10140 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
10141 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
10142 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
10143 wndClass
.cbClsExtra
= 0;
10144 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
10145 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
10146 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
10147 wndClass
.lpszClassName
= WC_LISTVIEWW
;
10148 RegisterClassW(&wndClass
);
10153 * Unregisters the window class.
10161 void LISTVIEW_Unregister(void)
10163 UnregisterClassW(WC_LISTVIEWW
, NULL
);
10168 * Handle any WM_COMMAND messages
10171 * [I] infoPtr : valid pointer to the listview structure
10172 * [I] wParam : the first message parameter
10173 * [I] lParam : the second message parameter
10178 static LRESULT
LISTVIEW_Command(const LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
10180 switch (HIWORD(wParam
))
10185 * Adjust the edit window size
10187 WCHAR buffer
[1024];
10188 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
10189 HFONT hFont
, hOldFont
= 0;
10193 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
10194 GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
10195 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
10197 /* Select font to get the right dimension of the string */
10198 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
10201 hOldFont
= SelectObject(hdc
, hFont
);
10204 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
10206 TEXTMETRICW textMetric
;
10208 /* Add Extra spacing for the next character */
10209 GetTextMetricsW(hdc
, &textMetric
);
10210 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10218 rect
.bottom
- rect
.top
,
10219 SWP_DRAWFRAME
|SWP_NOMOVE
);
10222 SelectObject(hdc
, hOldFont
);
10224 ReleaseDC(infoPtr
->hwndEdit
, hdc
);
10230 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);
10239 * Subclassed edit control windproc function
10242 * [I] hwnd : the edit window handle
10243 * [I] uMsg : the message that is to be processed
10244 * [I] wParam : first message parameter
10245 * [I] lParam : second message parameter
10246 * [I] isW : TRUE if input is Unicode
10251 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
10253 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
10254 BOOL cancel
= FALSE
;
10256 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10257 hwnd
, uMsg
, wParam
, lParam
, isW
);
10261 case WM_GETDLGCODE
:
10262 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
10269 WNDPROC editProc
= infoPtr
->EditWndProc
;
10270 infoPtr
->EditWndProc
= 0;
10271 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
10272 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10276 if (VK_ESCAPE
== (INT
)wParam
)
10281 else if (VK_RETURN
== (INT
)wParam
)
10285 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10288 /* kill the edit */
10289 if (infoPtr
->hwndEdit
)
10291 LPWSTR buffer
= NULL
;
10293 infoPtr
->hwndEdit
= 0;
10296 DWORD len
= isW
? GetWindowTextLengthW(hwnd
) : GetWindowTextLengthA(hwnd
);
10300 if ( (buffer
= Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))) )
10302 if (isW
) GetWindowTextW(hwnd
, buffer
, len
+1);
10303 else GetWindowTextA(hwnd
, (CHAR
*)buffer
, len
+1);
10307 LISTVIEW_EndEditLabelT(infoPtr
, buffer
, isW
);
10312 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
10318 * Subclassed edit control Unicode windproc function
10321 * [I] hwnd : the edit window handle
10322 * [I] uMsg : the message that is to be processed
10323 * [I] wParam : first message parameter
10324 * [I] lParam : second message parameter
10328 static LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10330 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
10335 * Subclassed edit control ANSI windproc function
10338 * [I] hwnd : the edit window handle
10339 * [I] uMsg : the message that is to be processed
10340 * [I] wParam : first message parameter
10341 * [I] lParam : second message parameter
10345 static LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10347 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
10352 * Creates a subclassed edit control
10355 * [I] infoPtr : valid pointer to the listview structure
10356 * [I] text : initial text for the edit
10357 * [I] style : the window style
10358 * [I] isW : TRUE if input is Unicode
10362 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, DWORD style
,
10363 INT x
, INT y
, INT width
, INT height
, BOOL isW
)
10365 WCHAR editName
[5] = { 'E', 'd', 'i', 't', '\0' };
10370 TEXTMETRICW textMetric
;
10371 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
10373 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text
, isW
), isW
);
10375 style
|= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
;
10376 hdc
= GetDC(infoPtr
->hwndSelf
);
10378 /* Select the font to get appropriate metric dimensions */
10379 if(infoPtr
->hFont
!= 0)
10380 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
10382 /*Get String Length in pixels */
10383 GetTextExtentPoint32W(hdc
, text
, lstrlenW(text
), &sz
);
10385 /*Add Extra spacing for the next character */
10386 GetTextMetricsW(hdc
, &textMetric
);
10387 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10389 if(infoPtr
->hFont
!= 0)
10390 SelectObject(hdc
, hOldFont
);
10392 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
10394 hedit
= CreateWindowW(editName
, text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
10396 hedit
= CreateWindowA("Edit", (LPCSTR
)text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
10398 if (!hedit
) return 0;
10400 infoPtr
->EditWndProc
= (WNDPROC
)
10401 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
10402 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
10404 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);