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 INT
LISTVIEW_CreateHeader(LISTVIEW_INFO
*infoPtr
)
1380 DWORD dFlags
= WS_CHILD
| HDS_HORZ
| HDS_FULLDRAG
| HDS_DRAGDROP
;
1383 if (infoPtr
->hwndHeader
) return 0;
1385 /* setup creation flags */
1386 dFlags
|= (LVS_NOSORTHEADER
& infoPtr
->dwStyle
) ? 0 : HDS_BUTTONS
;
1387 dFlags
|= (LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
) ? HDS_HIDDEN
: 0;
1389 hInst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
1392 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
, dFlags
,
1393 0, 0, 0, 0, infoPtr
->hwndSelf
, NULL
, hInst
, NULL
);
1394 if (!infoPtr
->hwndHeader
) return -1;
1396 /* set header unicode format */
1397 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, TRUE
, 0);
1399 /* set header font */
1400 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, (LPARAM
)TRUE
);
1405 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
, LPRECT lprc
)
1407 *lprc
= LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->rcHeader
;
1410 static inline BOOL
LISTVIEW_GetItemW(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
1412 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
1415 /* Listview invalidation functions: use _only_ these functions to invalidate */
1417 static inline BOOL
is_redrawing(const LISTVIEW_INFO
*infoPtr
)
1419 return infoPtr
->bRedraw
;
1422 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO
*infoPtr
, const RECT
* rect
)
1424 if(!is_redrawing(infoPtr
)) return;
1425 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect
));
1426 InvalidateRect(infoPtr
->hwndSelf
, rect
, TRUE
);
1429 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
1433 if(!is_redrawing(infoPtr
)) return;
1434 LISTVIEW_GetItemBox(infoPtr
, nItem
, &rcBox
);
1435 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1438 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
)
1440 POINT Origin
, Position
;
1443 if(!is_redrawing(infoPtr
)) return;
1444 assert ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
);
1445 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1446 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
1447 LISTVIEW_GetHeaderRect(infoPtr
, nSubItem
, &rcBox
);
1449 rcBox
.bottom
= infoPtr
->nItemHeight
;
1450 OffsetRect(&rcBox
, Origin
.x
+ Position
.x
, Origin
.y
+ Position
.y
);
1451 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1454 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO
*infoPtr
)
1456 LISTVIEW_InvalidateRect(infoPtr
, NULL
);
1459 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
1463 if(!is_redrawing(infoPtr
)) return;
1464 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
1465 rcCol
.top
= infoPtr
->rcList
.top
;
1466 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
1467 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
1472 * Retrieves the number of items that can fit vertically in the client area.
1475 * [I] infoPtr : valid pointer to the listview structure
1478 * Number of items per row.
1480 static inline INT
LISTVIEW_GetCountPerRow(const LISTVIEW_INFO
*infoPtr
)
1482 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1484 return max(nListWidth
/infoPtr
->nItemWidth
, 1);
1489 * Retrieves the number of items that can fit horizontally in the client
1493 * [I] infoPtr : valid pointer to the listview structure
1496 * Number of items per column.
1498 static inline INT
LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO
*infoPtr
)
1500 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1502 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
1506 /*************************************************************************
1507 * LISTVIEW_ProcessLetterKeys
1509 * Processes keyboard messages generated by pressing the letter keys
1511 * What this does is perform a case insensitive search from the
1512 * current position with the following quirks:
1513 * - If two chars or more are pressed in quick succession we search
1514 * for the corresponding string (e.g. 'abc').
1515 * - If there is a delay we wipe away the current search string and
1516 * restart with just that char.
1517 * - If the user keeps pressing the same character, whether slowly or
1518 * fast, so that the search string is entirely composed of this
1519 * character ('aaaaa' for instance), then we search for first item
1520 * that starting with that character.
1521 * - If the user types the above character in quick succession, then
1522 * we must also search for the corresponding string ('aaaaa'), and
1523 * go to that string if there is a match.
1526 * [I] hwnd : handle to the window
1527 * [I] charCode : the character code, the actual character
1528 * [I] keyData : key data
1536 * - The current implementation has a list of characters it will
1537 * accept and it ignores everything else. In particular it will
1538 * ignore accentuated characters which seems to match what
1539 * Windows does. But I'm not sure it makes sense to follow
1541 * - We don't sound a beep when the search fails.
1545 * TREEVIEW_ProcessLetterKeys
1547 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
1552 WCHAR buffer
[MAX_PATH
];
1553 DWORD lastKeyPressTimestamp
= infoPtr
->lastKeyPressTimestamp
;
1555 /* simple parameter checking */
1556 if (!charCode
|| !keyData
) return 0;
1558 /* only allow the valid WM_CHARs through */
1559 if (!isalnumW(charCode
) &&
1560 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
1561 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
1562 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
1563 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
1564 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
1565 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
1566 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
1567 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
1568 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
1571 /* if there's one item or less, there is no where to go */
1572 if (infoPtr
->nItemCount
<= 1) return 0;
1574 /* update the search parameters */
1575 infoPtr
->lastKeyPressTimestamp
= GetTickCount();
1576 if (infoPtr
->lastKeyPressTimestamp
- lastKeyPressTimestamp
< KEY_DELAY
) {
1577 if (infoPtr
->nSearchParamLength
< MAX_PATH
-1)
1578 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++]=charCode
;
1579 if (infoPtr
->charCode
!= charCode
)
1580 infoPtr
->charCode
= charCode
= 0;
1582 infoPtr
->charCode
=charCode
;
1583 infoPtr
->szSearchParam
[0]=charCode
;
1584 infoPtr
->nSearchParamLength
=1;
1585 /* Redundant with the 1 char string */
1589 /* and search from the current position */
1591 if (infoPtr
->nFocusedItem
>= 0) {
1592 endidx
=infoPtr
->nFocusedItem
;
1594 /* if looking for single character match,
1595 * then we must always move forward
1597 if (infoPtr
->nSearchParamLength
== 1)
1600 endidx
=infoPtr
->nItemCount
;
1604 /* Let application handle this for virtual listview */
1605 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
1610 ZeroMemory(&lvfi
, sizeof(lvfi
));
1611 lvfi
.flags
= (LVFI_WRAP
| LVFI_PARTIAL
);
1612 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
] = '\0';
1613 lvfi
.psz
= infoPtr
->szSearchParam
;
1617 nItem
= notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
1620 LISTVIEW_KeySelection(infoPtr
, nItem
, FALSE
);
1626 if (idx
== infoPtr
->nItemCount
) {
1627 if (endidx
== infoPtr
->nItemCount
|| endidx
== 0)
1633 item
.mask
= LVIF_TEXT
;
1636 item
.pszText
= buffer
;
1637 item
.cchTextMax
= MAX_PATH
;
1638 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
1640 /* check for a match */
1641 if (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,infoPtr
->nSearchParamLength
) == 0) {
1644 } else if ( (charCode
!= 0) && (nItem
== -1) && (nItem
!= infoPtr
->nFocusedItem
) &&
1645 (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,1) == 0) ) {
1646 /* This would work but we must keep looking for a longer match */
1650 } while (idx
!= endidx
);
1653 LISTVIEW_KeySelection(infoPtr
, nItem
, FALSE
);
1658 /*************************************************************************
1659 * LISTVIEW_UpdateHeaderSize [Internal]
1661 * Function to resize the header control
1664 * [I] hwnd : handle to a window
1665 * [I] nNewScrollPos : scroll pos to set
1670 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
1675 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
1677 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
1678 point
[0].x
= winRect
.left
;
1679 point
[0].y
= winRect
.top
;
1680 point
[1].x
= winRect
.right
;
1681 point
[1].y
= winRect
.bottom
;
1683 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
1684 point
[0].x
= -nNewScrollPos
;
1685 point
[1].x
+= nNewScrollPos
;
1687 SetWindowPos(infoPtr
->hwndHeader
,0,
1688 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
1689 (infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
|
1690 SWP_NOZORDER
| SWP_NOACTIVATE
);
1695 * Update the scrollbars. This functions should be called whenever
1696 * the content, size or view changes.
1699 * [I] infoPtr : valid pointer to the listview structure
1704 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO
*infoPtr
)
1706 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1707 SCROLLINFO horzInfo
, vertInfo
;
1710 if ((infoPtr
->dwStyle
& LVS_NOSCROLL
) || !is_redrawing(infoPtr
)) return;
1712 ZeroMemory(&horzInfo
, sizeof(SCROLLINFO
));
1713 horzInfo
.cbSize
= sizeof(SCROLLINFO
);
1714 horzInfo
.nPage
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1716 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1717 if (uView
== LVS_LIST
)
1719 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
1720 horzInfo
.nMax
= (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
;
1722 /* scroll by at least one column per page */
1723 if(horzInfo
.nPage
< infoPtr
->nItemWidth
)
1724 horzInfo
.nPage
= infoPtr
->nItemWidth
;
1726 horzInfo
.nPage
/= infoPtr
->nItemWidth
;
1728 else if (uView
== LVS_REPORT
)
1730 horzInfo
.nMax
= infoPtr
->nItemWidth
;
1732 else /* LVS_ICON, or LVS_SMALLICON */
1736 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) horzInfo
.nMax
= rcView
.right
- rcView
.left
;
1739 horzInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1740 horzInfo
.nMax
= max(horzInfo
.nMax
- 1, 0);
1741 dx
= GetScrollPos(infoPtr
->hwndSelf
, SB_HORZ
);
1742 dx
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
, TRUE
);
1743 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo
));
1745 /* Setting the horizontal scroll can change the listview size
1746 * (and potentially everything else) so we need to recompute
1747 * everything again for the vertical scroll
1750 ZeroMemory(&vertInfo
, sizeof(SCROLLINFO
));
1751 vertInfo
.cbSize
= sizeof(SCROLLINFO
);
1752 vertInfo
.nPage
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1754 if (uView
== LVS_REPORT
)
1756 vertInfo
.nMax
= infoPtr
->nItemCount
;
1758 /* scroll by at least one page */
1759 if(vertInfo
.nPage
< infoPtr
->nItemHeight
)
1760 vertInfo
.nPage
= infoPtr
->nItemHeight
;
1762 if (infoPtr
->nItemHeight
> 0)
1763 vertInfo
.nPage
/= infoPtr
->nItemHeight
;
1765 else if (uView
!= LVS_LIST
) /* LVS_ICON, or LVS_SMALLICON */
1769 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) vertInfo
.nMax
= rcView
.bottom
- rcView
.top
;
1772 vertInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1773 vertInfo
.nMax
= max(vertInfo
.nMax
- 1, 0);
1774 dy
= GetScrollPos(infoPtr
->hwndSelf
, SB_VERT
);
1775 dy
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &vertInfo
, TRUE
);
1776 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo
));
1778 /* Change of the range may have changed the scroll pos. If so move the content */
1779 if (dx
!= 0 || dy
!= 0)
1782 listRect
= infoPtr
->rcList
;
1783 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &listRect
, &listRect
, 0, 0,
1784 SW_ERASE
| SW_INVALIDATE
);
1787 /* Update the Header Control */
1788 if (uView
== LVS_REPORT
)
1790 horzInfo
.fMask
= SIF_POS
;
1791 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
);
1792 LISTVIEW_UpdateHeaderSize(infoPtr
, horzInfo
.nPos
);
1799 * Shows/hides the focus rectangle.
1802 * [I] infoPtr : valid pointer to the listview structure
1803 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1808 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO
*infoPtr
, BOOL fShow
)
1810 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1813 TRACE("fShow=%d, nItem=%d\n", fShow
, infoPtr
->nFocusedItem
);
1815 if (infoPtr
->nFocusedItem
< 0) return;
1817 /* we need some gymnastics in ICON mode to handle large items */
1818 if ( (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
1822 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcBox
);
1823 if ((rcBox
.bottom
- rcBox
.top
) > infoPtr
->nItemHeight
)
1825 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1830 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) return;
1832 /* for some reason, owner draw should work only in report mode */
1833 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
1838 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
1839 HFONT hOldFont
= SelectObject(hdc
, hFont
);
1841 item
.iItem
= infoPtr
->nFocusedItem
;
1843 item
.mask
= LVIF_PARAM
;
1844 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto done
;
1846 ZeroMemory(&dis
, sizeof(dis
));
1847 dis
.CtlType
= ODT_LISTVIEW
;
1848 dis
.CtlID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1849 dis
.itemID
= item
.iItem
;
1850 dis
.itemAction
= ODA_FOCUS
;
1851 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
1852 dis
.hwndItem
= infoPtr
->hwndSelf
;
1854 LISTVIEW_GetItemBox(infoPtr
, dis
.itemID
, &dis
.rcItem
);
1855 dis
.itemData
= item
.lParam
;
1857 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
1859 SelectObject(hdc
, hOldFont
);
1863 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
1866 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1870 * Invalidates all visible selected items.
1872 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO
*infoPtr
)
1876 iterator_frameditems(&i
, infoPtr
, &infoPtr
->rcList
);
1877 while(iterator_next(&i
))
1879 if (LISTVIEW_GetItemState(infoPtr
, i
.nItem
, LVIS_SELECTED
))
1880 LISTVIEW_InvalidateItem(infoPtr
, i
.nItem
);
1882 iterator_destroy(&i
);
1887 * DESCRIPTION: [INTERNAL]
1888 * Computes an item's (left,top) corner, relative to rcView.
1889 * That is, the position has NOT been made relative to the Origin.
1890 * This is deliberate, to avoid computing the Origin over, and
1891 * over again, when this function is called in a loop. Instead,
1892 * one can factor the computation of the Origin before the loop,
1893 * and offset the value returned by this function, on every iteration.
1896 * [I] infoPtr : valid pointer to the listview structure
1897 * [I] nItem : item number
1898 * [O] lpptOrig : item top, left corner
1903 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
1905 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1907 assert(nItem
>= 0 && nItem
< infoPtr
->nItemCount
);
1909 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1911 lpptPosition
->x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1912 lpptPosition
->y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1914 else if (uView
== LVS_LIST
)
1916 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
1917 lpptPosition
->x
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
1918 lpptPosition
->y
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
1920 else /* LVS_REPORT */
1922 lpptPosition
->x
= 0;
1923 lpptPosition
->y
= nItem
* infoPtr
->nItemHeight
;
1928 * DESCRIPTION: [INTERNAL]
1929 * Compute the rectangles of an item. This is to localize all
1930 * the computations in one place. If you are not interested in some
1931 * of these values, simply pass in a NULL -- the function is smart
1932 * enough to compute only what's necessary. The function computes
1933 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1934 * one, the BOX rectangle. This rectangle is very cheap to compute,
1935 * and is guaranteed to contain all the other rectangles. Computing
1936 * the ICON rect is also cheap, but all the others are potentially
1937 * expensive. This gives an easy and effective optimization when
1938 * searching (like point inclusion, or rectangle intersection):
1939 * first test against the BOX, and if TRUE, test against the desired
1941 * If the function does not have all the necessary information
1942 * to computed the requested rectangles, will crash with a
1943 * failed assertion. This is done so we catch all programming
1944 * errors, given that the function is called only from our code.
1946 * We have the following 'special' meanings for a few fields:
1947 * * If LVIS_FOCUSED is set, we assume the item has the focus
1948 * This is important in ICON mode, where it might get a larger
1949 * then usual rectangle
1951 * Please note that subitem support works only in REPORT mode.
1954 * [I] infoPtr : valid pointer to the listview structure
1955 * [I] lpLVItem : item to compute the measures for
1956 * [O] lprcBox : ptr to Box rectangle
1957 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1958 * [0] lprcSelectBox : ptr to select box rectangle
1959 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1960 * [O] lprcIcon : ptr to Icon rectangle
1961 * Same as LVM_GETITEMRECT with LVIR_ICON
1962 * [O] lprcStateIcon: ptr to State Icon rectangle
1963 * [O] lprcLabel : ptr to Label rectangle
1964 * Same as LVM_GETITEMRECT with LVIR_LABEL
1969 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
,
1970 LPRECT lprcBox
, LPRECT lprcSelectBox
,
1971 LPRECT lprcIcon
, LPRECT lprcStateIcon
, LPRECT lprcLabel
)
1973 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1974 BOOL doSelectBox
= FALSE
, doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
1975 RECT Box
, SelectBox
, Icon
, Label
;
1976 COLUMN_INFO
*lpColumnInfo
= NULL
;
1977 SIZE labelSize
= { 0, 0 };
1979 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem
, TRUE
));
1981 /* Be smart and try to figure out the minimum we have to do */
1982 if (lpLVItem
->iSubItem
) assert(uView
== LVS_REPORT
);
1983 if (uView
== LVS_ICON
&& (lprcBox
|| lprcLabel
))
1985 assert((lpLVItem
->mask
& LVIF_STATE
) && (lpLVItem
->stateMask
& LVIS_FOCUSED
));
1986 if (lpLVItem
->state
& LVIS_FOCUSED
) oversizedBox
= doLabel
= TRUE
;
1988 if (lprcSelectBox
) doSelectBox
= TRUE
;
1989 if (lprcLabel
) doLabel
= TRUE
;
1990 if (doLabel
|| lprcIcon
|| lprcStateIcon
) doIcon
= TRUE
;
1997 /************************************************************/
1998 /* compute the box rectangle (it should be cheap to do) */
1999 /************************************************************/
2000 if (lpLVItem
->iSubItem
|| uView
== LVS_REPORT
)
2001 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpLVItem
->iSubItem
);
2003 if (lpLVItem
->iSubItem
)
2005 Box
= lpColumnInfo
->rcHeader
;
2010 Box
.right
= infoPtr
->nItemWidth
;
2013 Box
.bottom
= infoPtr
->nItemHeight
;
2015 /******************************************************************/
2016 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2017 /******************************************************************/
2020 LONG state_width
= 0;
2022 if (infoPtr
->himlState
&& lpLVItem
->iSubItem
== 0)
2023 state_width
= infoPtr
->iconStateSize
.cx
;
2025 if (uView
== LVS_ICON
)
2027 Icon
.left
= Box
.left
+ state_width
;
2028 if (infoPtr
->himlNormal
)
2029 Icon
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
- state_width
) / 2;
2030 Icon
.top
= Box
.top
+ ICON_TOP_PADDING
;
2031 Icon
.right
= Icon
.left
;
2032 Icon
.bottom
= Icon
.top
;
2033 if (infoPtr
->himlNormal
)
2035 Icon
.right
+= infoPtr
->iconSize
.cx
;
2036 Icon
.bottom
+= infoPtr
->iconSize
.cy
;
2039 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2041 Icon
.left
= Box
.left
+ state_width
;
2043 if (uView
== LVS_REPORT
)
2044 Icon
.left
+= REPORT_MARGINX
;
2047 Icon
.right
= Icon
.left
;
2048 if (infoPtr
->himlSmall
&&
2049 (!lpColumnInfo
|| lpLVItem
->iSubItem
== 0 || (lpColumnInfo
->fmt
& LVCFMT_IMAGE
) ||
2050 ((infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
) && lpLVItem
->iImage
!= I_IMAGECALLBACK
)))
2051 Icon
.right
+= infoPtr
->iconSize
.cx
;
2052 Icon
.bottom
= Icon
.top
+ infoPtr
->iconSize
.cy
;
2054 if(lprcIcon
) *lprcIcon
= Icon
;
2055 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon
));
2057 /* TODO: is this correct? */
2060 lprcStateIcon
->left
= Icon
.left
- state_width
;
2061 lprcStateIcon
->right
= Icon
.left
;
2062 lprcStateIcon
->top
= Icon
.top
;
2063 lprcStateIcon
->bottom
= lprcStateIcon
->top
+ infoPtr
->iconSize
.cy
;
2064 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon
));
2067 else Icon
.right
= 0;
2069 /************************************************************/
2070 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2071 /************************************************************/
2074 /* calculate how far to the right can the label stretch */
2075 Label
.right
= Box
.right
;
2076 if (uView
== LVS_REPORT
)
2078 if (lpLVItem
->iSubItem
== 0) Label
= lpColumnInfo
->rcHeader
;
2081 if (lpLVItem
->iSubItem
|| ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && uView
== LVS_REPORT
))
2083 labelSize
.cx
= infoPtr
->nItemWidth
;
2084 labelSize
.cy
= infoPtr
->nItemHeight
;
2088 /* we need the text in non owner draw mode */
2089 assert(lpLVItem
->mask
& LVIF_TEXT
);
2090 if (is_textT(lpLVItem
->pszText
, TRUE
))
2092 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2093 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2094 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2098 /* compute rough rectangle where the label will go */
2099 SetRectEmpty(&rcText
);
2100 rcText
.right
= infoPtr
->nItemWidth
- TRAILING_LABEL_PADDING
;
2101 rcText
.bottom
= infoPtr
->nItemHeight
;
2102 if (uView
== LVS_ICON
)
2103 rcText
.bottom
-= ICON_TOP_PADDING
+ infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2105 /* now figure out the flags */
2106 if (uView
== LVS_ICON
)
2107 uFormat
= oversizedBox
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
;
2109 uFormat
= LV_SL_DT_FLAGS
;
2111 DrawTextW (hdc
, lpLVItem
->pszText
, -1, &rcText
, uFormat
| DT_CALCRECT
);
2113 labelSize
.cx
= min(rcText
.right
- rcText
.left
+ TRAILING_LABEL_PADDING
, infoPtr
->nItemWidth
);
2114 labelSize
.cy
= rcText
.bottom
- rcText
.top
;
2116 SelectObject(hdc
, hOldFont
);
2117 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2121 if (uView
== LVS_ICON
)
2123 Label
.left
= Box
.left
+ (infoPtr
->nItemWidth
- labelSize
.cx
) / 2;
2124 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
2125 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2126 Label
.right
= Label
.left
+ labelSize
.cx
;
2127 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2128 if (!oversizedBox
&& labelSize
.cy
> infoPtr
->ntmHeight
)
2130 labelSize
.cy
= min(Box
.bottom
- Label
.top
, labelSize
.cy
);
2131 labelSize
.cy
/= infoPtr
->ntmHeight
;
2132 labelSize
.cy
= max(labelSize
.cy
, 1);
2133 labelSize
.cy
*= infoPtr
->ntmHeight
;
2135 Label
.bottom
= Label
.top
+ labelSize
.cy
+ HEIGHT_PADDING
;
2137 else if (uView
== LVS_REPORT
)
2139 Label
.left
= Icon
.right
;
2140 Label
.top
= Box
.top
;
2141 Label
.right
= lpColumnInfo
->rcHeader
.right
;
2142 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2144 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2146 Label
.left
= Icon
.right
;
2147 Label
.top
= Box
.top
;
2148 Label
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2149 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2152 if (lprcLabel
) *lprcLabel
= Label
;
2153 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label
));
2156 /************************************************************/
2157 /* compute STATEICON bounding box */
2158 /************************************************************/
2161 if (uView
== LVS_REPORT
)
2163 SelectBox
.left
= Icon
.right
; /* FIXME: should be Icon.left */
2164 SelectBox
.top
= Box
.top
;
2165 SelectBox
.bottom
= Box
.bottom
;
2166 if (lpLVItem
->iSubItem
== 0)
2168 /* we need the indent in report mode */
2169 assert(lpLVItem
->mask
& LVIF_INDENT
);
2170 SelectBox
.left
+= infoPtr
->iconSize
.cx
* lpLVItem
->iIndent
;
2172 SelectBox
.right
= min(SelectBox
.left
+ labelSize
.cx
, Label
.right
);
2176 UnionRect(&SelectBox
, &Icon
, &Label
);
2178 if (lprcSelectBox
) *lprcSelectBox
= SelectBox
;
2179 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox
));
2182 /* Fix the Box if necessary */
2185 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
2186 else *lprcBox
= Box
;
2188 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box
));
2192 * DESCRIPTION: [INTERNAL]
2195 * [I] infoPtr : valid pointer to the listview structure
2196 * [I] nItem : item number
2197 * [O] lprcBox : ptr to Box rectangle
2202 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprcBox
)
2204 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2205 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2206 POINT Position
, Origin
;
2209 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
2210 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
2212 /* Be smart and try to figure out the minimum we have to do */
2214 if (uView
== LVS_ICON
&& infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
2215 lvItem
.mask
|= LVIF_TEXT
;
2216 lvItem
.iItem
= nItem
;
2217 lvItem
.iSubItem
= 0;
2218 lvItem
.pszText
= szDispText
;
2219 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2220 if (lvItem
.mask
) LISTVIEW_GetItemW(infoPtr
, &lvItem
);
2221 if (uView
== LVS_ICON
)
2223 lvItem
.mask
|= LVIF_STATE
;
2224 lvItem
.stateMask
= LVIS_FOCUSED
;
2225 lvItem
.state
= (lvItem
.mask
& LVIF_TEXT
? LVIS_FOCUSED
: 0);
2227 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprcBox
, 0, 0, 0, 0);
2229 OffsetRect(lprcBox
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
2235 * Returns the current icon position, and advances it along the top.
2236 * The returned position is not offset by Origin.
2239 * [I] infoPtr : valid pointer to the listview structure
2240 * [O] lpPos : will get the current icon position
2245 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2247 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2249 *lpPos
= infoPtr
->currIconPos
;
2251 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2252 if (infoPtr
->currIconPos
.x
+ infoPtr
->nItemWidth
<= nListWidth
) return;
2254 infoPtr
->currIconPos
.x
= 0;
2255 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2261 * Returns the current icon position, and advances it down the left edge.
2262 * The returned position is not offset by Origin.
2265 * [I] infoPtr : valid pointer to the listview structure
2266 * [O] lpPos : will get the current icon position
2271 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2273 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2275 *lpPos
= infoPtr
->currIconPos
;
2277 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2278 if (infoPtr
->currIconPos
.y
+ infoPtr
->nItemHeight
<= nListHeight
) return;
2280 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2281 infoPtr
->currIconPos
.y
= 0;
2287 * Moves an icon to the specified position.
2288 * It takes care of invalidating the item, etc.
2291 * [I] infoPtr : valid pointer to the listview structure
2292 * [I] nItem : the item to move
2293 * [I] lpPos : the new icon position
2294 * [I] isNew : flags the item as being new
2300 static BOOL
LISTVIEW_MoveIconTo(const LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*lppt
, BOOL isNew
)
2306 old
.x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2307 old
.y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2309 if (lppt
->x
== old
.x
&& lppt
->y
== old
.y
) return TRUE
;
2310 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2313 /* Allocating a POINTER for every item is too resource intensive,
2314 * so we'll keep the (x,y) in different arrays */
2315 if (!DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)(LONG_PTR
)lppt
->x
)) return FALSE
;
2316 if (!DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)(LONG_PTR
)lppt
->y
)) return FALSE
;
2318 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2325 * Arranges listview items in icon display mode.
2328 * [I] infoPtr : valid pointer to the listview structure
2329 * [I] nAlignCode : alignment code
2335 static BOOL
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
2337 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2338 void (*next_pos
)(LISTVIEW_INFO
*, LPPOINT
);
2342 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
) return FALSE
;
2344 TRACE("nAlignCode=%d\n", nAlignCode
);
2346 if (nAlignCode
== LVA_DEFAULT
)
2348 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
) nAlignCode
= LVA_ALIGNLEFT
;
2349 else nAlignCode
= LVA_ALIGNTOP
;
2354 case LVA_ALIGNLEFT
: next_pos
= LISTVIEW_NextIconPosLeft
; break;
2355 case LVA_ALIGNTOP
: next_pos
= LISTVIEW_NextIconPosTop
; break;
2356 case LVA_SNAPTOGRID
: next_pos
= LISTVIEW_NextIconPosTop
; break; /* FIXME */
2357 default: return FALSE
;
2360 infoPtr
->bAutoarrange
= TRUE
;
2361 infoPtr
->currIconPos
.x
= infoPtr
->currIconPos
.y
= 0;
2362 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2364 next_pos(infoPtr
, &pos
);
2365 LISTVIEW_MoveIconTo(infoPtr
, i
, &pos
, FALSE
);
2373 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2376 * [I] infoPtr : valid pointer to the listview structure
2377 * [O] lprcView : bounding rectangle
2383 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2387 SetRectEmpty(lprcView
);
2389 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
2393 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2395 x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, i
);
2396 y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, i
);
2397 lprcView
->right
= max(lprcView
->right
, x
);
2398 lprcView
->bottom
= max(lprcView
->bottom
, y
);
2400 if (infoPtr
->nItemCount
> 0)
2402 lprcView
->right
+= infoPtr
->nItemWidth
;
2403 lprcView
->bottom
+= infoPtr
->nItemHeight
;
2408 y
= LISTVIEW_GetCountPerColumn(infoPtr
);
2409 x
= infoPtr
->nItemCount
/ y
;
2410 if (infoPtr
->nItemCount
% y
) x
++;
2411 lprcView
->right
= x
* infoPtr
->nItemWidth
;
2412 lprcView
->bottom
= y
* infoPtr
->nItemHeight
;
2416 lprcView
->right
= infoPtr
->nItemWidth
;
2417 lprcView
->bottom
= infoPtr
->nItemCount
* infoPtr
->nItemHeight
;
2424 * Retrieves the bounding rectangle of all the items.
2427 * [I] infoPtr : valid pointer to the listview structure
2428 * [O] lprcView : bounding rectangle
2434 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2438 TRACE("(lprcView=%p)\n", lprcView
);
2440 if (!lprcView
) return FALSE
;
2442 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
2443 LISTVIEW_GetAreaRect(infoPtr
, lprcView
);
2444 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
2446 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView
));
2453 * Retrieves the subitem pointer associated with the subitem index.
2456 * [I] hdpaSubItems : DPA handle for a specific item
2457 * [I] nSubItem : index of subitem
2460 * SUCCESS : subitem pointer
2463 static SUBITEM_INFO
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
, INT nSubItem
)
2465 SUBITEM_INFO
*lpSubItem
;
2468 /* we should binary search here if need be */
2469 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
2471 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
2472 if (lpSubItem
->iSubItem
== nSubItem
)
2482 * Calculates the desired item width.
2485 * [I] infoPtr : valid pointer to the listview structure
2488 * The desired item width.
2490 static INT
LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO
*infoPtr
)
2492 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2495 TRACE("uView=%d\n", uView
);
2497 if (uView
== LVS_ICON
)
2498 nItemWidth
= infoPtr
->iconSpacing
.cx
;
2499 else if (uView
== LVS_REPORT
)
2503 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2505 LISTVIEW_GetHeaderRect(infoPtr
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, &rcHeader
);
2506 nItemWidth
= rcHeader
.right
;
2509 else /* LVS_SMALLICON, or LVS_LIST */
2513 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2514 nItemWidth
= max(LISTVIEW_GetLabelWidth(infoPtr
, i
), nItemWidth
);
2516 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
2517 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
2519 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
+ WIDTH_PADDING
);
2522 return max(nItemWidth
, 1);
2527 * Calculates the desired item height.
2530 * [I] infoPtr : valid pointer to the listview structure
2533 * The desired item height.
2535 static INT
LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO
*infoPtr
)
2537 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2540 TRACE("uView=%d\n", uView
);
2542 if (uView
== LVS_ICON
)
2543 nItemHeight
= infoPtr
->iconSpacing
.cy
;
2546 nItemHeight
= infoPtr
->ntmHeight
;
2547 if (uView
== LVS_REPORT
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
2549 if (infoPtr
->himlState
)
2550 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
2551 if (infoPtr
->himlSmall
)
2552 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
2553 if (infoPtr
->himlState
|| infoPtr
->himlSmall
)
2554 nItemHeight
+= HEIGHT_PADDING
;
2555 if (infoPtr
->nMeasureItemHeight
> 0)
2556 nItemHeight
= infoPtr
->nMeasureItemHeight
;
2559 return max(nItemHeight
, 1);
2564 * Updates the width, and height of an item.
2567 * [I] infoPtr : valid pointer to the listview structure
2572 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO
*infoPtr
)
2574 infoPtr
->nItemWidth
= LISTVIEW_CalculateItemWidth(infoPtr
);
2575 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
2581 * Retrieves and saves important text metrics info for the current
2585 * [I] infoPtr : valid pointer to the listview structure
2588 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
2590 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2591 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2592 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2596 if (GetTextMetricsW(hdc
, &tm
))
2598 infoPtr
->ntmHeight
= tm
.tmHeight
;
2599 infoPtr
->ntmMaxCharWidth
= tm
.tmMaxCharWidth
;
2602 if (GetTextExtentPoint32A(hdc
, "...", 3, &sz
))
2603 infoPtr
->nEllipsisWidth
= sz
.cx
;
2605 SelectObject(hdc
, hOldFont
);
2606 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2608 TRACE("tmHeight=%d\n", infoPtr
->ntmHeight
);
2613 * A compare function for ranges
2616 * [I] range1 : pointer to range 1;
2617 * [I] range2 : pointer to range 2;
2621 * > 0 : if range 1 > range 2
2622 * < 0 : if range 2 > range 1
2623 * = 0 : if range intersects range 2
2625 static INT CALLBACK
ranges_cmp(LPVOID range1
, LPVOID range2
, LPARAM flags
)
2629 if (((RANGE
*)range1
)->upper
<= ((RANGE
*)range2
)->lower
)
2631 else if (((RANGE
*)range2
)->upper
<= ((RANGE
*)range1
)->lower
)
2636 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1
), debugrange(range2
), cmp
);
2642 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2644 #define ranges_check(ranges, desc) do { } while(0)
2647 static void ranges_assert(RANGES ranges
, LPCSTR desc
, const char *func
, int line
)
2652 TRACE("*** Checking %s:%d:%s ***\n", func
, line
, desc
);
2654 assert (DPA_GetPtrCount(ranges
->hdpa
) >= 0);
2655 ranges_dump(ranges
);
2656 prev
= DPA_GetPtr(ranges
->hdpa
, 0);
2657 if (DPA_GetPtrCount(ranges
->hdpa
) > 0)
2658 assert (prev
->lower
>= 0 && prev
->lower
< prev
->upper
);
2659 for (i
= 1; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2661 curr
= DPA_GetPtr(ranges
->hdpa
, i
);
2662 assert (prev
->upper
<= curr
->lower
);
2663 assert (curr
->lower
< curr
->upper
);
2666 TRACE("--- Done checking---\n");
2669 static RANGES
ranges_create(int count
)
2671 RANGES ranges
= Alloc(sizeof(struct tagRANGES
));
2672 if (!ranges
) return NULL
;
2673 ranges
->hdpa
= DPA_Create(count
);
2674 if (ranges
->hdpa
) return ranges
;
2679 static void ranges_clear(RANGES ranges
)
2683 for(i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2684 Free(DPA_GetPtr(ranges
->hdpa
, i
));
2685 DPA_DeleteAllPtrs(ranges
->hdpa
);
2689 static void ranges_destroy(RANGES ranges
)
2691 if (!ranges
) return;
2692 ranges_clear(ranges
);
2693 DPA_Destroy(ranges
->hdpa
);
2697 static RANGES
ranges_clone(RANGES ranges
)
2702 if (!(clone
= ranges_create(DPA_GetPtrCount(ranges
->hdpa
)))) goto fail
;
2704 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2706 RANGE
*newrng
= Alloc(sizeof(RANGE
));
2707 if (!newrng
) goto fail
;
2708 *newrng
= *((RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
));
2709 DPA_SetPtr(clone
->hdpa
, i
, newrng
);
2714 TRACE ("clone failed\n");
2715 ranges_destroy(clone
);
2719 static RANGES
ranges_diff(RANGES ranges
, RANGES sub
)
2723 for (i
= 0; i
< DPA_GetPtrCount(sub
->hdpa
); i
++)
2724 ranges_del(ranges
, *((RANGE
*)DPA_GetPtr(sub
->hdpa
, i
)));
2729 static void ranges_dump(RANGES ranges
)
2733 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2734 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges
->hdpa
, i
)));
2737 static inline BOOL
ranges_contain(RANGES ranges
, INT nItem
)
2739 RANGE srchrng
= { nItem
, nItem
+ 1 };
2741 TRACE("(nItem=%d)\n", nItem
);
2742 ranges_check(ranges
, "before contain");
2743 return DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
) != -1;
2746 static INT
ranges_itemcount(RANGES ranges
)
2750 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2752 RANGE
*sel
= DPA_GetPtr(ranges
->hdpa
, i
);
2753 count
+= sel
->upper
- sel
->lower
;
2759 static BOOL
ranges_shift(RANGES ranges
, INT nItem
, INT delta
, INT nUpper
)
2761 RANGE srchrng
= { nItem
, nItem
+ 1 }, *chkrng
;
2764 index
= DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2765 if (index
== -1) return TRUE
;
2767 for (; index
< DPA_GetPtrCount(ranges
->hdpa
); index
++)
2769 chkrng
= DPA_GetPtr(ranges
->hdpa
, index
);
2770 if (chkrng
->lower
>= nItem
)
2771 chkrng
->lower
= max(min(chkrng
->lower
+ delta
, nUpper
- 1), 0);
2772 if (chkrng
->upper
> nItem
)
2773 chkrng
->upper
= max(min(chkrng
->upper
+ delta
, nUpper
), 0);
2778 static BOOL
ranges_add(RANGES ranges
, RANGE range
)
2783 TRACE("(%s)\n", debugrange(&range
));
2784 ranges_check(ranges
, "before add");
2786 /* try find overlapping regions first */
2787 srchrgn
.lower
= range
.lower
- 1;
2788 srchrgn
.upper
= range
.upper
+ 1;
2789 index
= DPA_Search(ranges
->hdpa
, &srchrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
);
2795 TRACE("Adding new range\n");
2797 /* create the brand new range to insert */
2798 newrgn
= Alloc(sizeof(RANGE
));
2799 if(!newrgn
) goto fail
;
2802 /* figure out where to insert it */
2803 index
= DPA_Search(ranges
->hdpa
, newrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2804 TRACE("index=%d\n", index
);
2805 if (index
== -1) index
= 0;
2807 /* and get it over with */
2808 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2816 RANGE
*chkrgn
, *mrgrgn
;
2817 INT fromindex
, mergeindex
;
2819 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2820 TRACE("Merge with %s @%d\n", debugrange(chkrgn
), index
);
2822 chkrgn
->lower
= min(range
.lower
, chkrgn
->lower
);
2823 chkrgn
->upper
= max(range
.upper
, chkrgn
->upper
);
2825 TRACE("New range %s @%d\n", debugrange(chkrgn
), index
);
2827 /* merge now common ranges */
2829 srchrgn
.lower
= chkrgn
->lower
- 1;
2830 srchrgn
.upper
= chkrgn
->upper
+ 1;
2834 mergeindex
= DPA_Search(ranges
->hdpa
, &srchrgn
, fromindex
, ranges_cmp
, 0, 0);
2835 if (mergeindex
== -1) break;
2836 if (mergeindex
== index
)
2838 fromindex
= index
+ 1;
2842 TRACE("Merge with index %i\n", mergeindex
);
2844 mrgrgn
= DPA_GetPtr(ranges
->hdpa
, mergeindex
);
2845 chkrgn
->lower
= min(chkrgn
->lower
, mrgrgn
->lower
);
2846 chkrgn
->upper
= max(chkrgn
->upper
, mrgrgn
->upper
);
2848 DPA_DeletePtr(ranges
->hdpa
, mergeindex
);
2849 if (mergeindex
< index
) index
--;
2853 ranges_check(ranges
, "after add");
2857 ranges_check(ranges
, "failed add");
2861 static BOOL
ranges_del(RANGES ranges
, RANGE range
)
2866 TRACE("(%s)\n", debugrange(&range
));
2867 ranges_check(ranges
, "before del");
2869 /* we don't use DPAS_SORTED here, since we need *
2870 * to find the first overlapping range */
2871 index
= DPA_Search(ranges
->hdpa
, &range
, 0, ranges_cmp
, 0, 0);
2874 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2876 TRACE("Matches range %s @%d\n", debugrange(chkrgn
), index
);
2878 /* case 1: Same range */
2879 if ( (chkrgn
->upper
== range
.upper
) &&
2880 (chkrgn
->lower
== range
.lower
) )
2882 DPA_DeletePtr(ranges
->hdpa
, index
);
2885 /* case 2: engulf */
2886 else if ( (chkrgn
->upper
<= range
.upper
) &&
2887 (chkrgn
->lower
>= range
.lower
) )
2889 DPA_DeletePtr(ranges
->hdpa
, index
);
2891 /* case 3: overlap upper */
2892 else if ( (chkrgn
->upper
<= range
.upper
) &&
2893 (chkrgn
->lower
< range
.lower
) )
2895 chkrgn
->upper
= range
.lower
;
2897 /* case 4: overlap lower */
2898 else if ( (chkrgn
->upper
> range
.upper
) &&
2899 (chkrgn
->lower
>= range
.lower
) )
2901 chkrgn
->lower
= range
.upper
;
2904 /* case 5: fully internal */
2907 RANGE tmprgn
= *chkrgn
, *newrgn
;
2909 if (!(newrgn
= Alloc(sizeof(RANGE
)))) goto fail
;
2910 newrgn
->lower
= chkrgn
->lower
;
2911 newrgn
->upper
= range
.lower
;
2912 chkrgn
->lower
= range
.upper
;
2913 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2922 index
= DPA_Search(ranges
->hdpa
, &range
, index
, ranges_cmp
, 0, 0);
2925 ranges_check(ranges
, "after del");
2929 ranges_check(ranges
, "failed del");
2935 * Removes all selection ranges
2938 * [I] infoPtr : valid pointer to the listview structure
2939 * [I] toSkip : item range to skip removing the selection
2945 static BOOL
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO
*infoPtr
, RANGES toSkip
)
2954 lvItem
.stateMask
= LVIS_SELECTED
;
2956 /* need to clone the DPA because callbacks can change it */
2957 if (!(clone
= ranges_clone(infoPtr
->selectionRanges
))) return FALSE
;
2958 iterator_rangesitems(&i
, ranges_diff(clone
, toSkip
));
2959 while(iterator_next(&i
))
2960 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &lvItem
);
2961 /* note that the iterator destructor will free the cloned range */
2962 iterator_destroy(&i
);
2967 static inline BOOL
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2971 if (!(toSkip
= ranges_create(1))) return FALSE
;
2972 if (nItem
!= -1) ranges_additem(toSkip
, nItem
);
2973 LISTVIEW_DeselectAllSkipItems(infoPtr
, toSkip
);
2974 ranges_destroy(toSkip
);
2978 static inline BOOL
LISTVIEW_DeselectAll(LISTVIEW_INFO
*infoPtr
)
2980 return LISTVIEW_DeselectAllSkipItem(infoPtr
, -1);
2985 * Retrieves the number of items that are marked as selected.
2988 * [I] infoPtr : valid pointer to the listview structure
2991 * Number of items selected.
2993 static INT
LISTVIEW_GetSelectedCount(const LISTVIEW_INFO
*infoPtr
)
2995 INT nSelectedCount
= 0;
2997 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3000 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
3002 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
3007 nSelectedCount
= ranges_itemcount(infoPtr
->selectionRanges
);
3009 TRACE("nSelectedCount=%d\n", nSelectedCount
);
3010 return nSelectedCount
;
3015 * Manages the item focus.
3018 * [I] infoPtr : valid pointer to the listview structure
3019 * [I] nItem : item index
3022 * TRUE : focused item changed
3023 * FALSE : focused item has NOT changed
3025 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3027 INT oldFocus
= infoPtr
->nFocusedItem
;
3030 if (nItem
== infoPtr
->nFocusedItem
) return FALSE
;
3032 lvItem
.state
= nItem
== -1 ? 0 : LVIS_FOCUSED
;
3033 lvItem
.stateMask
= LVIS_FOCUSED
;
3034 LISTVIEW_SetItemState(infoPtr
, nItem
== -1 ? infoPtr
->nFocusedItem
: nItem
, &lvItem
);
3036 return oldFocus
!= infoPtr
->nFocusedItem
;
3039 /* Helper function for LISTVIEW_ShiftIndices *only* */
3040 static INT
shift_item(const LISTVIEW_INFO
*infoPtr
, INT nShiftItem
, INT nItem
, INT direction
)
3042 if (nShiftItem
< nItem
) return nShiftItem
;
3044 if (nShiftItem
> nItem
) return nShiftItem
+ direction
;
3046 if (direction
> 0) return nShiftItem
+ direction
;
3048 return min(nShiftItem
, infoPtr
->nItemCount
- 1);
3053 * Updates the various indices after an item has been inserted or deleted.
3056 * [I] infoPtr : valid pointer to the listview structure
3057 * [I] nItem : item index
3058 * [I] direction : Direction of shift, +1 or -1.
3063 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
3068 /* temporarily disable change notification while shifting items */
3069 bOldChange
= infoPtr
->bDoChangeNotify
;
3070 infoPtr
->bDoChangeNotify
= FALSE
;
3072 TRACE("Shifting %iu, %i steps\n", nItem
, direction
);
3074 ranges_shift(infoPtr
->selectionRanges
, nItem
, direction
, infoPtr
->nItemCount
);
3076 assert(abs(direction
) == 1);
3078 infoPtr
->nSelectionMark
= shift_item(infoPtr
, infoPtr
->nSelectionMark
, nItem
, direction
);
3080 nNewFocus
= shift_item(infoPtr
, infoPtr
->nFocusedItem
, nItem
, direction
);
3081 if (nNewFocus
!= infoPtr
->nFocusedItem
)
3082 LISTVIEW_SetItemFocus(infoPtr
, nNewFocus
);
3084 /* But we are not supposed to modify nHotItem! */
3086 infoPtr
->bDoChangeNotify
= bOldChange
;
3092 * Adds a block of selections.
3095 * [I] infoPtr : valid pointer to the listview structure
3096 * [I] nItem : item index
3099 * Whether the window is still valid.
3101 static BOOL
LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3103 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
3104 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
3105 HWND hwndSelf
= infoPtr
->hwndSelf
;
3106 NMLVODSTATECHANGE nmlv
;
3111 /* Temporarily disable change notification
3112 * If the control is LVS_OWNERDATA, we need to send
3113 * only one LVN_ODSTATECHANGED notification.
3114 * See MSDN documentation for LVN_ITEMCHANGED.
3116 bOldChange
= infoPtr
->bDoChangeNotify
;
3117 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3119 if (nFirst
== -1) nFirst
= nItem
;
3121 item
.state
= LVIS_SELECTED
;
3122 item
.stateMask
= LVIS_SELECTED
;
3124 for (i
= nFirst
; i
<= nLast
; i
++)
3125 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
3127 ZeroMemory(&nmlv
, sizeof(nmlv
));
3128 nmlv
.iFrom
= nFirst
;
3131 nmlv
.uOldState
= item
.state
;
3133 notify_hdr(infoPtr
, LVN_ODSTATECHANGED
, (LPNMHDR
)&nmlv
);
3134 if (!IsWindow(hwndSelf
))
3136 infoPtr
->bDoChangeNotify
= bOldChange
;
3143 * Sets a single group selection.
3146 * [I] infoPtr : valid pointer to the listview structure
3147 * [I] nItem : item index
3152 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3154 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3160 if (!(selection
= ranges_create(100))) return;
3162 item
.state
= LVIS_SELECTED
;
3163 item
.stateMask
= LVIS_SELECTED
;
3165 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
3167 if (infoPtr
->nSelectionMark
== -1)
3169 infoPtr
->nSelectionMark
= nItem
;
3170 ranges_additem(selection
, nItem
);
3176 sel
.lower
= min(infoPtr
->nSelectionMark
, nItem
);
3177 sel
.upper
= max(infoPtr
->nSelectionMark
, nItem
) + 1;
3178 ranges_add(selection
, sel
);
3183 RECT rcItem
, rcSel
, rcSelMark
;
3186 rcItem
.left
= LVIR_BOUNDS
;
3187 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return;
3188 rcSelMark
.left
= LVIR_BOUNDS
;
3189 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) return;
3190 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
3191 iterator_frameditems(&i
, infoPtr
, &rcSel
);
3192 while(iterator_next(&i
))
3194 LISTVIEW_GetItemPosition(infoPtr
, i
.nItem
, &ptItem
);
3195 if (PtInRect(&rcSel
, ptItem
)) ranges_additem(selection
, i
.nItem
);
3197 iterator_destroy(&i
);
3200 /* disable per item notifications on LVS_OWNERDATA style
3201 FIXME: single LVN_ODSTATECHANGED should be used */
3202 bOldChange
= infoPtr
->bDoChangeNotify
;
3203 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3205 LISTVIEW_DeselectAllSkipItems(infoPtr
, selection
);
3208 iterator_rangesitems(&i
, selection
);
3209 while(iterator_next(&i
))
3210 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &item
);
3211 /* this will also destroy the selection */
3212 iterator_destroy(&i
);
3214 infoPtr
->bDoChangeNotify
= bOldChange
;
3216 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3221 * Sets a single selection.
3224 * [I] infoPtr : valid pointer to the listview structure
3225 * [I] nItem : item index
3230 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3234 TRACE("nItem=%d\n", nItem
);
3236 LISTVIEW_DeselectAllSkipItem(infoPtr
, nItem
);
3238 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3239 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3240 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3242 infoPtr
->nSelectionMark
= nItem
;
3247 * Set selection(s) with keyboard.
3250 * [I] infoPtr : valid pointer to the listview structure
3251 * [I] nItem : item index
3252 * [I] space : VK_SPACE code sent
3255 * SUCCESS : TRUE (needs to be repainted)
3256 * FAILURE : FALSE (nothing has changed)
3258 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL space
)
3260 /* FIXME: pass in the state */
3261 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
3262 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
3263 BOOL bResult
= FALSE
;
3265 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem
, wShift
, wCtrl
);
3266 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3268 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
3271 LISTVIEW_SetSelection(infoPtr
, nItem
);
3278 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3283 lvItem
.state
= ~LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3284 lvItem
.stateMask
= LVIS_SELECTED
;
3287 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3288 if (lvItem
.state
& LVIS_SELECTED
)
3289 infoPtr
->nSelectionMark
= nItem
;
3291 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3296 LISTVIEW_SetSelection(infoPtr
, nItem
);
3299 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3302 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3306 static BOOL
LISTVIEW_GetItemAtPt(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, POINT pt
)
3308 LVHITTESTINFO lvHitTestInfo
;
3310 ZeroMemory(&lvHitTestInfo
, sizeof(lvHitTestInfo
));
3311 lvHitTestInfo
.pt
.x
= pt
.x
;
3312 lvHitTestInfo
.pt
.y
= pt
.y
;
3314 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
3316 lpLVItem
->mask
= LVIF_PARAM
;
3317 lpLVItem
->iItem
= lvHitTestInfo
.iItem
;
3318 lpLVItem
->iSubItem
= 0;
3320 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
3323 static inline BOOL
LISTVIEW_isHotTracking(const LISTVIEW_INFO
*infoPtr
)
3325 return ((infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) ||
3326 (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
) ||
3327 (infoPtr
->dwLvExStyle
& LVS_EX_TWOCLICKACTIVATE
));
3332 * Called when the mouse is being actively tracked and has hovered for a specified
3336 * [I] infoPtr : valid pointer to the listview structure
3337 * [I] fwKeys : key indicator
3338 * [I] x,y : mouse position
3341 * 0 if the message was processed, non-zero if there was an error
3344 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3345 * over the item for a certain period of time.
3348 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3350 if (LISTVIEW_isHotTracking(infoPtr
))
3358 if (LISTVIEW_GetItemAtPt(infoPtr
, &item
, pt
))
3359 LISTVIEW_SetSelection(infoPtr
, item
.iItem
);
3367 * Called whenever WM_MOUSEMOVE is received.
3370 * [I] infoPtr : valid pointer to the listview structure
3371 * [I] fwKeys : key indicator
3372 * [I] x,y : mouse position
3375 * 0 if the message is processed, non-zero if there was an error
3377 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3379 TRACKMOUSEEVENT trackinfo
;
3381 if (!(fwKeys
& MK_LBUTTON
))
3382 infoPtr
->bLButtonDown
= FALSE
;
3384 if (infoPtr
->bLButtonDown
)
3388 WORD wDragWidth
= GetSystemMetrics(SM_CXDRAG
);
3389 WORD wDragHeight
= GetSystemMetrics(SM_CYDRAG
);
3391 rect
.left
= infoPtr
->ptClickPos
.x
- wDragWidth
;
3392 rect
.right
= infoPtr
->ptClickPos
.x
+ wDragWidth
;
3393 rect
.top
= infoPtr
->ptClickPos
.y
- wDragHeight
;
3394 rect
.bottom
= infoPtr
->ptClickPos
.y
+ wDragHeight
;
3399 if (!PtInRect(&rect
, tmp
))
3401 LVHITTESTINFO lvHitTestInfo
;
3404 lvHitTestInfo
.pt
= infoPtr
->ptClickPos
;
3405 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
3407 ZeroMemory(&nmlv
, sizeof(nmlv
));
3408 nmlv
.iItem
= lvHitTestInfo
.iItem
;
3409 nmlv
.ptAction
= infoPtr
->ptClickPos
;
3411 if (!infoPtr
->bDragging
)
3413 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
3414 infoPtr
->bDragging
= TRUE
;
3421 infoPtr
->bLButtonDown
= FALSE
;
3423 /* see if we are supposed to be tracking mouse hovering */
3424 if (LISTVIEW_isHotTracking(infoPtr
)) {
3425 /* fill in the trackinfo struct */
3426 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3427 trackinfo
.dwFlags
= TME_QUERY
;
3428 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
3429 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
3431 /* see if we are already tracking this hwnd */
3432 _TrackMouseEvent(&trackinfo
);
3434 if(!(trackinfo
.dwFlags
& TME_HOVER
)) {
3435 trackinfo
.dwFlags
= TME_HOVER
;
3437 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3438 _TrackMouseEvent(&trackinfo
);
3447 * Tests whether the item is assignable to a list with style lStyle
3449 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
3451 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
3452 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
3453 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
3461 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3464 * [I] infoPtr : valid pointer to the listview structure
3465 * [I] lpLVItem : valid pointer to new item attributes
3466 * [I] isNew : the item being set is being inserted
3467 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3468 * [O] bChanged : will be set to TRUE if the item really changed
3474 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
3476 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3481 /* stateMask is ignored for LVM_INSERTITEM */
3482 UINT stateMask
= isNew
? ~0 : lpLVItem
->stateMask
;
3486 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
3488 if (lpLVItem
->mask
== 0) return TRUE
;
3490 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
3492 /* a virtual listview only stores selection and focus */
3493 if (lpLVItem
->mask
& ~LVIF_STATE
)
3499 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3500 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
3504 /* we need to get the lParam and state of the item */
3505 item
.iItem
= lpLVItem
->iItem
;
3506 item
.iSubItem
= lpLVItem
->iSubItem
;
3507 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
3508 item
.stateMask
= ~0;
3511 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
3513 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
3514 /* determine what fields will change */
3515 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & stateMask
& ~infoPtr
->uCallbackMask
))
3516 uChanged
|= LVIF_STATE
;
3518 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
3519 uChanged
|= LVIF_IMAGE
;
3521 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
3522 uChanged
|= LVIF_PARAM
;
3524 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
3525 uChanged
|= LVIF_INDENT
;
3527 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
3528 uChanged
|= LVIF_TEXT
;
3530 TRACE("uChanged=0x%x\n", uChanged
);
3531 if (!uChanged
) return TRUE
;
3534 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
3535 nmlv
.iItem
= lpLVItem
->iItem
;
3536 nmlv
.uNewState
= (item
.state
& ~stateMask
) | (lpLVItem
->state
& stateMask
);
3537 nmlv
.uOldState
= item
.state
;
3538 nmlv
.uChanged
= uChanged
;
3539 nmlv
.lParam
= item
.lParam
;
3541 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3542 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3544 if(lpItem
&& !isNew
&& infoPtr
->bDoChangeNotify
)
3546 HWND hwndSelf
= infoPtr
->hwndSelf
;
3548 if (notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
3550 if (!IsWindow(hwndSelf
))
3554 /* copy information */
3555 if (lpLVItem
->mask
& LVIF_TEXT
)
3556 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3558 if (lpLVItem
->mask
& LVIF_IMAGE
)
3559 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
3561 if (lpLVItem
->mask
& LVIF_PARAM
)
3562 lpItem
->lParam
= lpLVItem
->lParam
;
3564 if (lpLVItem
->mask
& LVIF_INDENT
)
3565 lpItem
->iIndent
= lpLVItem
->iIndent
;
3567 if (uChanged
& LVIF_STATE
)
3569 if (lpItem
&& (stateMask
& ~infoPtr
->uCallbackMask
& ~(LVIS_FOCUSED
| LVIS_SELECTED
)))
3571 lpItem
->state
&= ~stateMask
;
3572 lpItem
->state
|= (lpLVItem
->state
& stateMask
);
3574 if (lpLVItem
->state
& stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3576 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
3577 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3579 else if (stateMask
& LVIS_SELECTED
)
3580 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3582 /* if we are asked to change focus, and we manage it, do it */
3583 if (stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
3585 if (lpLVItem
->state
& LVIS_FOCUSED
)
3587 LISTVIEW_SetItemFocus(infoPtr
, -1);
3588 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
3589 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, uView
== LVS_LIST
);
3591 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
3592 infoPtr
->nFocusedItem
= -1;
3596 /* if we're inserting the item, we're done */
3597 if (isNew
) return TRUE
;
3599 /* send LVN_ITEMCHANGED notification */
3600 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
3601 if (infoPtr
->bDoChangeNotify
) notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
3608 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3611 * [I] infoPtr : valid pointer to the listview structure
3612 * [I] lpLVItem : valid pointer to new subitem attributes
3613 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3614 * [O] bChanged : will be set to TRUE if the item really changed
3620 static BOOL
set_sub_item(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
3623 SUBITEM_INFO
*lpSubItem
;
3625 /* we do not support subitems for virtual listviews */
3626 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
3628 /* set subitem only if column is present */
3629 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
3631 /* First do some sanity checks */
3632 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3633 particularly useful. We currently do not actually do anything with
3634 the flag on subitems.
3636 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
)) return FALSE
;
3637 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
))) return TRUE
;
3639 /* get the subitem structure, and create it if not there */
3640 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3641 assert (hdpaSubItems
);
3643 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
3646 SUBITEM_INFO
*tmpSubItem
;
3649 lpSubItem
= Alloc(sizeof(SUBITEM_INFO
));
3650 if (!lpSubItem
) return FALSE
;
3651 /* we could binary search here, if need be...*/
3652 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
3654 tmpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
3655 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
3657 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
3662 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
3663 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
3667 if (lpLVItem
->mask
& LVIF_IMAGE
)
3668 if (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
)
3670 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
3674 if (lpLVItem
->mask
& LVIF_TEXT
)
3675 if (lpSubItem
->hdr
.pszText
!= lpLVItem
->pszText
)
3677 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3686 * Sets item attributes.
3689 * [I] infoPtr : valid pointer to the listview structure
3690 * [I] lpLVItem : new item attributes
3691 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3697 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, LVITEMW
*lpLVItem
, BOOL isW
)
3699 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3700 HWND hwndSelf
= infoPtr
->hwndSelf
;
3701 LPWSTR pszText
= NULL
;
3702 BOOL bResult
, bChanged
= FALSE
;
3704 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
3706 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
3709 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3710 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_textW(lpLVItem
->pszText
))
3712 pszText
= lpLVItem
->pszText
;
3713 lpLVItem
->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
3716 /* actually set the fields */
3717 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
3719 if (lpLVItem
->iSubItem
)
3720 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
3722 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
3723 if (!IsWindow(hwndSelf
))
3726 /* redraw item, if necessary */
3727 if (bChanged
&& !infoPtr
->bIsDrawing
)
3729 /* this little optimization eliminates some nasty flicker */
3730 if ( uView
== LVS_REPORT
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
3731 !(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) &&
3732 lpLVItem
->iSubItem
> 0 && lpLVItem
->iSubItem
<= DPA_GetPtrCount(infoPtr
->hdpaColumns
) )
3733 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
3735 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
3740 textfreeT(lpLVItem
->pszText
, isW
);
3741 lpLVItem
->pszText
= pszText
;
3749 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3752 * [I] infoPtr : valid pointer to the listview structure
3757 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*infoPtr
)
3759 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3761 SCROLLINFO scrollInfo
;
3763 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3764 scrollInfo
.fMask
= SIF_POS
;
3766 if (uView
== LVS_LIST
)
3768 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
3769 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
3771 else if (uView
== LVS_REPORT
)
3773 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3774 nItem
= scrollInfo
.nPos
;
3778 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3779 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
3782 TRACE("nItem=%d\n", nItem
);
3790 * Erases the background of the given rectangle
3793 * [I] infoPtr : valid pointer to the listview structure
3794 * [I] hdc : device context handle
3795 * [I] lprcBox : clipping rectangle
3801 static inline BOOL
LISTVIEW_FillBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
3803 if (!infoPtr
->hBkBrush
) return FALSE
;
3805 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, wine_dbgstr_rect(lprcBox
), infoPtr
->hBkBrush
);
3807 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
3815 * [I] infoPtr : valid pointer to the listview structure
3816 * [I] hdc : device context handle
3817 * [I] nItem : item index
3818 * [I] nSubItem : subitem index
3819 * [I] pos : item position in client coordinates
3820 * [I] cdmode : custom draw mode
3826 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, INT nSubItem
, POINT pos
, DWORD cdmode
)
3828 UINT uFormat
, uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3829 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
3830 static WCHAR szCallback
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3831 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
3833 RECT rcSelect
, rcBox
, rcIcon
, rcLabel
, rcStateIcon
;
3834 NMLVCUSTOMDRAW nmlvcd
;
3839 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc
, nItem
, nSubItem
, wine_dbgstr_point(&pos
));
3841 /* get information needed for drawing the item */
3842 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_PARAM
;
3843 if (nSubItem
== 0) lvItem
.mask
|= LVIF_STATE
;
3844 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
3845 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
;
3846 lvItem
.iItem
= nItem
;
3847 lvItem
.iSubItem
= nSubItem
;
3850 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3851 lvItem
.pszText
= szDispText
;
3852 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
3853 if (nSubItem
> 0 && (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3854 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3855 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= szCallback
;
3856 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3858 /* now check if we need to update the focus rectangle */
3859 lprcFocus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
3861 if (!lprcFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
3862 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcSelect
, &rcIcon
, &rcStateIcon
, &rcLabel
);
3863 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
3864 OffsetRect(&rcSelect
, pos
.x
, pos
.y
);
3865 OffsetRect(&rcIcon
, pos
.x
, pos
.y
);
3866 OffsetRect(&rcStateIcon
, pos
.x
, pos
.y
);
3867 OffsetRect(&rcLabel
, pos
.x
, pos
.y
);
3868 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3869 wine_dbgstr_rect(&rcBox
), wine_dbgstr_rect(&rcSelect
),
3870 wine_dbgstr_rect(&rcIcon
), wine_dbgstr_rect(&rcLabel
));
3872 /* fill in the custom draw structure */
3873 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
3875 hOldFont
= GetCurrentObject(hdc
, OBJ_FONT
);
3876 if (nSubItem
> 0) cdmode
= infoPtr
->cditemmode
;
3877 if (cdmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3878 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3879 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3880 if (nSubItem
== 0) infoPtr
->cditemmode
= cdsubitemmode
;
3881 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3882 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3883 if (nSubItem
== 0 && cdsubitemmode
== CDRF_NOTIFYITEMDRAW
)
3885 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
3886 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3888 if (nSubItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
3889 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
3890 else if ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) == FALSE
)
3891 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, TRUE
);
3893 /* in full row select, subitems, will just use main item's colors */
3894 if (nSubItem
&& uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3895 nmlvcd
.clrTextBk
= CLR_NONE
;
3898 if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && (nSubItem
== 0))
3900 UINT uStateImage
= STATEIMAGEINDEX(lvItem
.state
);
3903 TRACE("uStateImage=%d\n", uStateImage
);
3904 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
,
3905 rcStateIcon
.left
, rcStateIcon
.top
, ILD_NORMAL
);
3910 himl
= (uView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
3911 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
))
3913 TRACE("iImage=%d\n", lvItem
.iImage
);
3914 ImageList_DrawEx(himl
, lvItem
.iImage
, hdc
, rcIcon
.left
, rcIcon
.top
,
3915 rcIcon
.right
- rcIcon
.left
, rcIcon
.bottom
- rcIcon
.top
, infoPtr
->clrBk
, CLR_DEFAULT
,
3916 (lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
) ? ILD_SELECTED
: ILD_NORMAL
);
3919 /* Don't bother painting item being edited */
3920 if (infoPtr
->hwndEdit
&& nItem
== infoPtr
->nEditLabelItem
&& nSubItem
== 0) goto postpaint
;
3922 /* FIXME: temporary hack */
3923 rcSelect
.left
= rcLabel
.left
;
3925 /* draw the selection background, if we're drawing the main item */
3928 /* in icon mode, the label rect is really what we want to draw the
3930 if (uView
== LVS_ICON
)
3933 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3934 rcSelect
.right
= rcBox
.right
;
3936 if (nmlvcd
.clrTextBk
!= CLR_NONE
)
3937 ExtTextOutW(hdc
, rcSelect
.left
, rcSelect
.top
, ETO_OPAQUE
, &rcSelect
, 0, 0, 0);
3938 if(lprcFocus
) *lprcFocus
= rcSelect
;
3941 /* figure out the text drawing flags */
3942 uFormat
= (uView
== LVS_ICON
? (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
3943 if (uView
== LVS_ICON
)
3944 uFormat
= (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
3947 switch (LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
3949 case LVCFMT_RIGHT
: uFormat
|= DT_RIGHT
; break;
3950 case LVCFMT_CENTER
: uFormat
|= DT_CENTER
; break;
3951 default: uFormat
|= DT_LEFT
;
3954 if (!(uFormat
& (DT_RIGHT
| DT_CENTER
)))
3956 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
3957 else rcLabel
.left
+= LABEL_HOR_PADDING
;
3959 else if (uFormat
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
3961 /* for GRIDLINES reduce the bottom so the text formats correctly */
3962 if (uView
== LVS_REPORT
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
3965 DrawTextW(hdc
, lvItem
.pszText
, -1, &rcLabel
, uFormat
);
3968 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
3969 notify_postpaint(infoPtr
, &nmlvcd
);
3970 if (cdsubitemmode
& CDRF_NEWFONT
)
3971 SelectObject(hdc
, hOldFont
);
3977 * Draws listview items when in owner draw mode.
3980 * [I] infoPtr : valid pointer to the listview structure
3981 * [I] hdc : device context handle
3986 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3988 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
3989 DWORD cditemmode
= CDRF_DODEFAULT
;
3990 NMLVCUSTOMDRAW nmlvcd
;
3991 POINT Origin
, Position
;
3997 ZeroMemory(&dis
, sizeof(dis
));
3999 /* Get scroll info once before loop */
4000 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4002 /* iterate through the invalidated rows */
4003 while(iterator_next(i
))
4005 item
.iItem
= i
->nItem
;
4007 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4008 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4009 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
4011 dis
.CtlType
= ODT_LISTVIEW
;
4013 dis
.itemID
= item
.iItem
;
4014 dis
.itemAction
= ODA_DRAWENTIRE
;
4016 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
4017 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
4018 dis
.hwndItem
= infoPtr
->hwndSelf
;
4020 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
4021 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
4022 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
4023 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
4024 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
4025 dis
.itemData
= item
.lParam
;
4027 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), wine_dbgstr_rect(&dis
.rcItem
));
4030 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4031 * structure for the rest. of the paint cycle
4033 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
4034 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
4035 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4037 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
4039 prepaint_setup (infoPtr
, hdc
, &nmlvcd
, FALSE
);
4040 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
4043 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
4044 notify_postpaint(infoPtr
, &nmlvcd
);
4050 * Draws listview items when in report display mode.
4053 * [I] infoPtr : valid pointer to the listview structure
4054 * [I] hdc : device context handle
4055 * [I] cdmode : custom draw mode
4060 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4063 RECT rcClip
, rcItem
;
4064 POINT Origin
, Position
;
4070 /* figure out what to draw */
4071 rgntype
= GetClipBox(hdc
, &rcClip
);
4072 if (rgntype
== NULLREGION
) return;
4074 /* Get scroll info once before loop */
4075 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4077 /* narrow down the columns we need to paint */
4078 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
4080 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
4081 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
4083 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
4085 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
4086 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
4088 iterator_rangeitems(&j
, colRange
);
4090 /* in full row select, we _have_ to draw the main item */
4091 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4094 /* iterate through the invalidated rows */
4095 while(iterator_next(i
))
4097 /* iterate through the invalidated columns */
4098 while(iterator_next(&j
))
4100 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4101 Position
.x
+= Origin
.x
;
4102 Position
.y
+= Origin
.y
;
4104 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
4106 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4108 rcItem
.bottom
= infoPtr
->nItemHeight
;
4109 OffsetRect(&rcItem
, Position
.x
, Position
.y
);
4110 if (!RectVisible(hdc
, &rcItem
)) continue;
4113 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, j
.nItem
, Position
, cdmode
);
4116 iterator_destroy(&j
);
4121 * Draws the gridlines if necessary when in report display mode.
4124 * [I] infoPtr : valid pointer to the listview structure
4125 * [I] hdc : device context handle
4130 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
4135 RECT rcClip
, rcItem
;
4142 /* figure out what to draw */
4143 rgntype
= GetClipBox(hdc
, &rcClip
);
4144 if (rgntype
== NULLREGION
) return;
4146 /* Get scroll info once before loop */
4147 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4149 /* narrow down the columns we need to paint */
4150 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
4152 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
4153 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
4155 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
4157 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
4158 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
4160 iterator_rangeitems(&j
, colRange
);
4162 if ((hPen
= CreatePen( PS_SOLID
, 1, comctl32_color
.clr3dFace
)))
4164 hOldPen
= SelectObject ( hdc
, hPen
);
4166 /* draw the vertical lines for the columns */
4167 iterator_rangeitems(&j
, colRange
);
4168 while(iterator_next(&j
))
4170 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4171 if (rcItem
.left
== 0) continue; /* skip first column */
4172 rcItem
.left
+= Origin
.x
;
4173 rcItem
.right
+= Origin
.x
;
4174 rcItem
.top
= infoPtr
->rcList
.top
;
4175 rcItem
.bottom
= infoPtr
->rcList
.bottom
;
4176 TRACE("vert col=%d, rcItem=%s\n", j
.nItem
, wine_dbgstr_rect(&rcItem
));
4177 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4178 LineTo (hdc
, rcItem
.left
, rcItem
.bottom
);
4180 iterator_destroy(&j
);
4182 /* draw the horizontial lines for the rows */
4183 itemheight
= LISTVIEW_CalculateItemHeight(infoPtr
);
4184 rcItem
.left
= infoPtr
->rcList
.left
+ Origin
.x
;
4185 rcItem
.right
= infoPtr
->rcList
.right
+ Origin
.x
;
4186 rcItem
.bottom
= rcItem
.top
= Origin
.y
- 1;
4187 MoveToEx(hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4188 LineTo(hdc
, rcItem
.right
, rcItem
.top
);
4189 for(y
=itemheight
-1+Origin
.y
; y
<=infoPtr
->rcList
.bottom
; y
+=itemheight
)
4191 rcItem
.bottom
= rcItem
.top
= y
;
4192 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem
));
4193 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4194 LineTo (hdc
, rcItem
.right
, rcItem
.top
);
4197 SelectObject( hdc
, hOldPen
);
4198 DeleteObject( hPen
);
4204 * Draws listview items when in list display mode.
4207 * [I] infoPtr : valid pointer to the listview structure
4208 * [I] hdc : device context handle
4209 * [I] cdmode : custom draw mode
4214 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4216 POINT Origin
, Position
;
4218 /* Get scroll info once before loop */
4219 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4221 while(iterator_prev(i
))
4223 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4224 Position
.x
+= Origin
.x
;
4225 Position
.y
+= Origin
.y
;
4227 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, 0, Position
, cdmode
);
4234 * Draws listview items.
4237 * [I] infoPtr : valid pointer to the listview structure
4238 * [I] hdc : device context handle
4239 * [I] prcErase : rect to be erased before refresh (may be NULL)
4244 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*prcErase
)
4246 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4247 COLORREF oldTextColor
= 0, oldBkColor
= 0, oldClrTextBk
, oldClrText
;
4248 NMLVCUSTOMDRAW nmlvcd
;
4255 HBITMAP hbmp
= NULL
;
4257 LISTVIEW_DUMP(infoPtr
);
4259 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
4260 TRACE("double buffering\n");
4262 hdc
= CreateCompatibleDC(hdcOrig
);
4264 ERR("Failed to create DC for backbuffer\n");
4267 hbmp
= CreateCompatibleBitmap(hdcOrig
, infoPtr
->rcList
.right
,
4268 infoPtr
->rcList
.bottom
);
4270 ERR("Failed to create bitmap for backbuffer\n");
4275 SelectObject(hdc
, hbmp
);
4276 SelectObject(hdc
, infoPtr
->hFont
);
4278 /* Save dc values we're gonna trash while drawing
4279 * FIXME: Should be done in LISTVIEW_DrawItem() */
4280 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
4281 oldBkMode
= GetBkMode(hdc
);
4282 oldBkColor
= GetBkColor(hdc
);
4283 oldTextColor
= GetTextColor(hdc
);
4286 infoPtr
->bIsDrawing
= TRUE
;
4289 LISTVIEW_FillBkgnd(infoPtr
, hdc
, prcErase
);
4290 } else if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
4291 /* If no erasing was done (usually because RedrawWindow was called
4292 * with RDW_INVALIDATE only) we need to copy the old contents into
4293 * the backbuffer before continuing. */
4294 BitBlt(hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
4295 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
4296 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
4297 hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
4300 /* FIXME: Shouldn't need to do this */
4301 oldClrTextBk
= infoPtr
->clrTextBk
;
4302 oldClrText
= infoPtr
->clrText
;
4304 infoPtr
->cditemmode
= CDRF_DODEFAULT
;
4306 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
4307 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
4308 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4309 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
4310 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
4312 /* Use these colors to draw the items */
4313 infoPtr
->clrTextBk
= nmlvcd
.clrTextBk
;
4314 infoPtr
->clrText
= nmlvcd
.clrText
;
4316 /* nothing to draw */
4317 if(infoPtr
->nItemCount
== 0) goto enddraw
;
4319 /* figure out what we need to draw */
4320 iterator_visibleitems(&i
, infoPtr
, hdc
);
4322 /* send cache hint notification */
4323 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4325 RANGE range
= iterator_range(&i
);
4328 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
4329 nmlv
.iFrom
= range
.lower
;
4330 nmlv
.iTo
= range
.upper
- 1;
4331 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
4334 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
4335 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
4338 if (uView
== LVS_REPORT
)
4339 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
4340 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4341 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
4343 /* if we have a focus rect, draw it */
4344 if (infoPtr
->bFocus
)
4345 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
4347 iterator_destroy(&i
);
4350 /* For LVS_EX_GRIDLINES go and draw lines */
4351 /* This includes the case where there were *no* items */
4352 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
&&
4353 infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
4354 LISTVIEW_RefreshReportGrid(infoPtr
, hdc
);
4356 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
4357 notify_postpaint(infoPtr
, &nmlvcd
);
4359 infoPtr
->clrTextBk
= oldClrTextBk
;
4360 infoPtr
->clrText
= oldClrText
;
4363 BitBlt(hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
4364 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
4365 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
4366 hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
4371 SelectObject(hdc
, hOldFont
);
4372 SetBkMode(hdc
, oldBkMode
);
4373 SetBkColor(hdc
, oldBkColor
);
4374 SetTextColor(hdc
, oldTextColor
);
4377 infoPtr
->bIsDrawing
= FALSE
;
4383 * Calculates the approximate width and height of a given number of items.
4386 * [I] infoPtr : valid pointer to the listview structure
4387 * [I] nItemCount : number of items
4388 * [I] wWidth : width
4389 * [I] wHeight : height
4392 * Returns a DWORD. The width in the low word and the height in high word.
4394 static DWORD
LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
4395 WORD wWidth
, WORD wHeight
)
4397 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4398 INT nItemCountPerColumn
= 1;
4399 INT nColumnCount
= 0;
4400 DWORD dwViewRect
= 0;
4402 if (nItemCount
== -1)
4403 nItemCount
= infoPtr
->nItemCount
;
4405 if (uView
== LVS_LIST
)
4407 if (wHeight
== 0xFFFF)
4409 /* use current height */
4410 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4413 if (wHeight
< infoPtr
->nItemHeight
)
4414 wHeight
= infoPtr
->nItemHeight
;
4418 if (infoPtr
->nItemHeight
> 0)
4420 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
4421 if (nItemCountPerColumn
== 0)
4422 nItemCountPerColumn
= 1;
4424 if (nItemCount
% nItemCountPerColumn
!= 0)
4425 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
4427 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
4431 /* Microsoft padding magic */
4432 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
4433 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
4435 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4437 else if (uView
== LVS_REPORT
)
4441 if (infoPtr
->nItemCount
> 0)
4443 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
4444 wWidth
= rcBox
.right
- rcBox
.left
;
4445 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
4449 /* use current height and width */
4450 if (wHeight
== 0xffff)
4451 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4452 if (wWidth
== 0xffff)
4453 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
4456 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4458 else if (uView
== LVS_SMALLICON
)
4459 FIXME("uView == LVS_SMALLICON: not implemented\n");
4460 else if (uView
== LVS_ICON
)
4461 FIXME("uView == LVS_ICON: not implemented\n");
4469 * Create a drag image list for the specified item.
4472 * [I] infoPtr : valid pointer to the listview structure
4473 * [I] iItem : index of item
4474 * [O] lppt : Upperr-left corner of the image
4477 * Returns a handle to the image list if successful, NULL otherwise.
4479 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
4485 HBITMAP hbmp
, hOldbmp
;
4486 HIMAGELIST dragList
= 0;
4487 TRACE("iItem=%d Count=%d\n", iItem
, infoPtr
->nItemCount
);
4489 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
)
4492 rcItem
.left
= LVIR_BOUNDS
;
4493 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
4496 lppt
->x
= rcItem
.left
;
4497 lppt
->y
= rcItem
.top
;
4499 size
.cx
= rcItem
.right
- rcItem
.left
;
4500 size
.cy
= rcItem
.bottom
- rcItem
.top
;
4502 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
4503 hdc
= CreateCompatibleDC(hdcOrig
);
4504 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
4505 hOldbmp
= SelectObject(hdc
, hbmp
);
4507 rcItem
.left
= rcItem
.top
= 0;
4508 rcItem
.right
= size
.cx
;
4509 rcItem
.bottom
= size
.cy
;
4510 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
4513 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, 0, pos
, infoPtr
->cditemmode
))
4515 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
4516 SelectObject(hdc
, hOldbmp
);
4517 ImageList_Add(dragList
, hbmp
, 0);
4520 SelectObject(hdc
, hOldbmp
);
4524 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
4526 TRACE("ret=%p\n", dragList
);
4534 * Removes all listview items and subitems.
4537 * [I] infoPtr : valid pointer to the listview structure
4543 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
, BOOL destroy
)
4546 HDPA hdpaSubItems
= NULL
;
4553 /* we do it directly, to avoid notifications */
4554 ranges_clear(infoPtr
->selectionRanges
);
4555 infoPtr
->nSelectionMark
= -1;
4556 infoPtr
->nFocusedItem
= -1;
4557 SetRectEmpty(&infoPtr
->rcFocus
);
4558 /* But we are supposed to leave nHotItem as is! */
4561 /* send LVN_DELETEALLITEMS notification */
4562 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4564 bSuppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
4566 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
4568 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4570 /* send LVN_DELETEITEM notification, if not suppressed
4571 and if it is not a virtual listview */
4572 if (!bSuppress
) notify_deleteitem(infoPtr
, i
);
4573 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
4574 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
4576 hdrItem
= DPA_GetPtr(hdpaSubItems
, j
);
4577 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4580 DPA_Destroy(hdpaSubItems
);
4581 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
4583 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
4584 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
4585 infoPtr
->nItemCount
--;
4590 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4591 LISTVIEW_UpdateScroll(infoPtr
);
4593 LISTVIEW_InvalidateList(infoPtr
);
4600 * Scrolls, and updates the columns, when a column is changing width.
4603 * [I] infoPtr : valid pointer to the listview structure
4604 * [I] nColumn : column to scroll
4605 * [I] dx : amount of scroll, in pixels
4610 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
4612 COLUMN_INFO
*lpColumnInfo
;
4617 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
4618 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
4619 rcCol
= lpColumnInfo
->rcHeader
;
4620 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
4621 rcCol
.left
= rcCol
.right
;
4623 /* adjust the other columns */
4624 for (nCol
= nColumn
; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
4626 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
4627 lpColumnInfo
->rcHeader
.left
+= dx
;
4628 lpColumnInfo
->rcHeader
.right
+= dx
;
4631 /* do not update screen if not in report mode */
4632 if (!is_redrawing(infoPtr
) || (infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return;
4634 /* if we have a focus, we must first erase the focus rect */
4635 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
4637 /* Need to reset the item width when inserting a new column */
4638 infoPtr
->nItemWidth
+= dx
;
4640 LISTVIEW_UpdateScroll(infoPtr
);
4641 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
4643 /* scroll to cover the deleted column, and invalidate for redraw */
4644 rcOld
= infoPtr
->rcList
;
4645 rcOld
.left
= ptOrigin
.x
+ rcCol
.left
+ dx
;
4646 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4648 /* we can restore focus now */
4649 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
4654 * Removes a column from the listview control.
4657 * [I] infoPtr : valid pointer to the listview structure
4658 * [I] nColumn : column index
4664 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4668 TRACE("nColumn=%d\n", nColumn
);
4670 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0
4671 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4673 /* While the MSDN specifically says that column zero should not be deleted,
4674 what actually happens is that the column itself is deleted but no items or subitems
4678 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
4680 if (!Header_DeleteItem(infoPtr
->hwndHeader
, nColumn
))
4683 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
4684 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
4686 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
4688 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
4690 INT nItem
, nSubItem
, i
;
4692 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
4694 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
4697 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4699 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
4700 if (lpSubItem
->iSubItem
== nColumn
)
4703 lpDelItem
= lpSubItem
;
4705 else if (lpSubItem
->iSubItem
> nColumn
)
4707 lpSubItem
->iSubItem
--;
4711 /* if we found our subitem, zapp it */
4715 if (is_textW(lpDelItem
->hdr
.pszText
))
4716 Free(lpDelItem
->hdr
.pszText
);
4721 /* free dpa memory */
4722 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
4727 /* update the other column info */
4728 LISTVIEW_UpdateItemSize(infoPtr
);
4729 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
4730 LISTVIEW_InvalidateList(infoPtr
);
4732 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
4739 * Invalidates the listview after an item's insertion or deletion.
4742 * [I] infoPtr : valid pointer to the listview structure
4743 * [I] nItem : item index
4744 * [I] dir : -1 if deleting, 1 if inserting
4749 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
4751 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4752 INT nPerCol
, nItemCol
, nItemRow
;
4756 /* if we don't refresh, what's the point of scrolling? */
4757 if (!is_redrawing(infoPtr
)) return;
4759 assert (abs(dir
) == 1);
4761 /* arrange icons if autoarrange is on */
4762 if (is_autoarrange(infoPtr
))
4764 BOOL arrange
= TRUE
;
4765 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
4766 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
4767 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4770 /* scrollbars need updating */
4771 LISTVIEW_UpdateScroll(infoPtr
);
4773 /* figure out the item's position */
4774 if (uView
== LVS_REPORT
)
4775 nPerCol
= infoPtr
->nItemCount
+ 1;
4776 else if (uView
== LVS_LIST
)
4777 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
4778 else /* LVS_ICON, or LVS_SMALLICON */
4781 nItemCol
= nItem
/ nPerCol
;
4782 nItemRow
= nItem
% nPerCol
;
4783 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4785 /* move the items below up a slot */
4786 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
4787 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
4788 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
4789 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4790 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4791 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
4792 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4794 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll
), wine_dbgstr_rect(&infoPtr
->rcList
));
4795 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4796 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4799 /* report has only that column, so we're done */
4800 if (uView
== LVS_REPORT
) return;
4802 /* now for LISTs, we have to deal with the columns to the right */
4803 rcScroll
.left
= (nItemCol
+ 1) * infoPtr
->nItemWidth
;
4805 rcScroll
.right
= (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
4806 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4807 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4808 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4809 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4810 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4815 * Removes an item from the listview control.
4818 * [I] infoPtr : valid pointer to the listview structure
4819 * [I] nItem : item index
4825 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
4828 const UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4829 const BOOL is_icon
= (uView
== LVS_SMALLICON
|| uView
== LVS_ICON
);
4831 TRACE("(nItem=%d)\n", nItem
);
4833 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
4835 /* remove selection, and focus */
4837 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4838 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
4840 /* send LVN_DELETEITEM notification. */
4841 if (!notify_deleteitem(infoPtr
, nItem
)) return FALSE
;
4843 /* we need to do this here, because we'll be deleting stuff */
4845 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4847 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4853 hdpaSubItems
= DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
4854 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4856 hdrItem
= DPA_GetPtr(hdpaSubItems
, i
);
4857 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4860 DPA_Destroy(hdpaSubItems
);
4865 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
4866 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
4869 infoPtr
->nItemCount
--;
4870 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
4872 /* now is the invalidation fun */
4874 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
4881 * Callback implementation for editlabel control
4884 * [I] infoPtr : valid pointer to the listview structure
4885 * [I] pszText : modified text
4886 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4892 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, LPWSTR pszText
, BOOL isW
)
4894 HWND hwndSelf
= infoPtr
->hwndSelf
;
4895 NMLVDISPINFOW dispInfo
;
4896 INT editedItem
= infoPtr
->nEditLabelItem
;
4899 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
4901 infoPtr
->nEditLabelItem
= -1;
4903 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4904 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4905 dispInfo
.item
.iItem
= editedItem
;
4906 dispInfo
.item
.iSubItem
= 0;
4907 dispInfo
.item
.stateMask
= ~0;
4908 if (!LISTVIEW_GetItemW(infoPtr
, &dispInfo
.item
)) return FALSE
;
4911 bSame
= (lstrcmpW(dispInfo
.item
.pszText
, pszText
) == 0);
4914 LPWSTR tmp
= textdupTtoW(pszText
, FALSE
);
4915 bSame
= (lstrcmpW(dispInfo
.item
.pszText
, tmp
) == 0);
4916 textfreeT(tmp
, FALSE
);
4918 if (bSame
) return TRUE
;
4920 /* add the text from the edit in */
4921 dispInfo
.item
.mask
|= LVIF_TEXT
;
4922 dispInfo
.item
.pszText
= pszText
;
4923 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4925 /* Do we need to update the Item Text */
4926 if (!notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
)) return FALSE
;
4927 if (!IsWindow(hwndSelf
))
4929 if (!pszText
) return TRUE
;
4931 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4933 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, editedItem
);
4934 ITEM_INFO
* lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
4935 if (lpItem
&& lpItem
->hdr
.pszText
== LPSTR_TEXTCALLBACKW
)
4937 LISTVIEW_InvalidateItem(infoPtr
, editedItem
);
4942 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4943 dispInfo
.item
.mask
= LVIF_TEXT
;
4944 dispInfo
.item
.iItem
= editedItem
;
4945 dispInfo
.item
.iSubItem
= 0;
4946 dispInfo
.item
.pszText
= pszText
;
4947 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4948 return LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
4953 * Begin in place editing of specified list view item
4956 * [I] infoPtr : valid pointer to the listview structure
4957 * [I] nItem : item index
4958 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4964 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
4966 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
4967 NMLVDISPINFOW dispInfo
;
4969 HWND hwndSelf
= infoPtr
->hwndSelf
;
4971 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
4973 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
4974 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
4976 infoPtr
->nEditLabelItem
= nItem
;
4978 /* Is the EditBox still there, if so remove it */
4979 if(infoPtr
->hwndEdit
!= 0)
4981 SetFocus(infoPtr
->hwndSelf
);
4982 infoPtr
->hwndEdit
= 0;
4985 LISTVIEW_SetSelection(infoPtr
, nItem
);
4986 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
4987 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4989 rect
.left
= LVIR_LABEL
;
4990 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
4992 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4993 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4994 dispInfo
.item
.iItem
= nItem
;
4995 dispInfo
.item
.iSubItem
= 0;
4996 dispInfo
.item
.stateMask
= ~0;
4997 dispInfo
.item
.pszText
= szDispText
;
4998 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
4999 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
5001 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, WS_VISIBLE
,
5002 rect
.left
-2, rect
.top
-1, 0, rect
.bottom
- rect
.top
+2, isW
);
5003 if (!infoPtr
->hwndEdit
) return 0;
5005 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
5007 if (!IsWindow(hwndSelf
))
5009 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
5010 infoPtr
->hwndEdit
= 0;
5014 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
5015 SetFocus(infoPtr
->hwndEdit
);
5016 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
5017 return infoPtr
->hwndEdit
;
5023 * Ensures the specified item is visible, scrolling into view if necessary.
5026 * [I] infoPtr : valid pointer to the listview structure
5027 * [I] nItem : item index
5028 * [I] bPartial : partially or entirely visible
5034 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
5036 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5037 INT nScrollPosHeight
= 0;
5038 INT nScrollPosWidth
= 0;
5039 INT nHorzAdjust
= 0;
5040 INT nVertAdjust
= 0;
5043 RECT rcItem
, rcTemp
;
5045 rcItem
.left
= LVIR_BOUNDS
;
5046 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
5048 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
5050 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
5052 /* scroll left/right, but in LVS_REPORT mode */
5053 if (uView
== LVS_LIST
)
5054 nScrollPosWidth
= infoPtr
->nItemWidth
;
5055 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5056 nScrollPosWidth
= 1;
5058 if (rcItem
.left
< infoPtr
->rcList
.left
)
5061 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
5066 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
5070 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
5072 /* scroll up/down, but not in LVS_LIST mode */
5073 if (uView
== LVS_REPORT
)
5074 nScrollPosHeight
= infoPtr
->nItemHeight
;
5075 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
5076 nScrollPosHeight
= 1;
5078 if (rcItem
.top
< infoPtr
->rcList
.top
)
5081 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
5086 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
5090 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
5092 if (nScrollPosWidth
)
5094 INT diff
= nHorzDiff
/ nScrollPosWidth
;
5095 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
5096 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
5099 if (nScrollPosHeight
)
5101 INT diff
= nVertDiff
/ nScrollPosHeight
;
5102 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
5103 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
5111 * Searches for an item with specific characteristics.
5114 * [I] hwnd : window handle
5115 * [I] nStart : base item index
5116 * [I] lpFindInfo : item information to look for
5119 * SUCCESS : index of item
5122 static INT
LISTVIEW_FindItemW(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
5123 const LVFINDINFOW
*lpFindInfo
)
5125 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5126 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5127 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
5128 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
5129 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
5130 POINT Position
, Destination
;
5133 /* Search in virtual listviews should be done by application, not by
5134 listview control, so we just send LVN_ODFINDITEMW and return the result */
5135 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5139 nmlv
.iStart
= nStart
;
5140 nmlv
.lvfi
= *lpFindInfo
;
5141 return notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
5144 if (!lpFindInfo
|| nItem
< 0) return -1;
5147 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
))
5149 lvItem
.mask
|= LVIF_TEXT
;
5150 lvItem
.pszText
= szDispText
;
5151 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5154 if (lpFindInfo
->flags
& LVFI_WRAP
)
5157 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
5158 (uView
== LVS_ICON
|| uView
==LVS_SMALLICON
))
5163 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5164 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
5165 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
5166 switch(lpFindInfo
->vkDirection
)
5168 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
5169 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
5170 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
5171 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
5172 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
5173 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
5174 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
5176 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
5177 Destination
.x
= rcArea
.right
;
5178 Destination
.y
= rcArea
.bottom
;
5180 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
5184 else Destination
.x
= Destination
.y
= 0;
5186 /* if LVFI_PARAM is specified, all other flags are ignored */
5187 if (lpFindInfo
->flags
& LVFI_PARAM
)
5189 lvItem
.mask
|= LVIF_PARAM
;
5191 lvItem
.mask
&= ~LVIF_TEXT
;
5195 for (; nItem
< nLast
; nItem
++)
5197 lvItem
.iItem
= nItem
;
5198 lvItem
.iSubItem
= 0;
5199 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
5201 if (lvItem
.mask
& LVIF_PARAM
)
5203 if (lpFindInfo
->lParam
== lvItem
.lParam
)
5209 if (lvItem
.mask
& LVIF_TEXT
)
5211 if (lpFindInfo
->flags
& LVFI_PARTIAL
)
5213 if (strstrW(lvItem
.pszText
, lpFindInfo
->psz
) == NULL
) continue;
5217 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
5221 if (!bNearest
) return nItem
;
5223 /* This is very inefficient. To do a good job here,
5224 * we need a sorted array of (x,y) item positions */
5225 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5227 /* compute the distance^2 to the destination */
5228 xdist
= Destination
.x
- Position
.x
;
5229 ydist
= Destination
.y
- Position
.y
;
5230 dist
= xdist
* xdist
+ ydist
* ydist
;
5232 /* remember the distance, and item if it's closer */
5236 nNearestItem
= nItem
;
5243 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
5248 return nNearestItem
;
5253 * Searches for an item with specific characteristics.
5256 * [I] hwnd : window handle
5257 * [I] nStart : base item index
5258 * [I] lpFindInfo : item information to look for
5261 * SUCCESS : index of item
5264 static INT
LISTVIEW_FindItemA(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
5265 const LVFINDINFOA
*lpFindInfo
)
5267 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
);
5272 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
5273 if (hasText
) fiw
.psz
= strW
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
5274 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
5275 textfreeT(strW
, FALSE
);
5281 * Retrieves the background image of the listview control.
5284 * [I] infoPtr : valid pointer to the listview structure
5285 * [O] lpBkImage : background image attributes
5291 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5293 /* FIXME (listview, "empty stub!\n"); */
5299 * Retrieves column attributes.
5302 * [I] infoPtr : valid pointer to the listview structure
5303 * [I] nColumn : column index
5304 * [IO] lpColumn : column information
5305 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5306 * otherwise it is in fact a LPLVCOLUMNA
5312 static BOOL
LISTVIEW_GetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
5314 COLUMN_INFO
*lpColumnInfo
;
5317 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5318 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
5320 /* initialize memory */
5321 ZeroMemory(&hdi
, sizeof(hdi
));
5323 if (lpColumn
->mask
& LVCF_TEXT
)
5325 hdi
.mask
|= HDI_TEXT
;
5326 hdi
.pszText
= lpColumn
->pszText
;
5327 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
5330 if (lpColumn
->mask
& LVCF_IMAGE
)
5331 hdi
.mask
|= HDI_IMAGE
;
5333 if (lpColumn
->mask
& LVCF_ORDER
)
5334 hdi
.mask
|= HDI_ORDER
;
5336 if (lpColumn
->mask
& LVCF_SUBITEM
)
5337 hdi
.mask
|= HDI_LPARAM
;
5339 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
5341 if (lpColumn
->mask
& LVCF_FMT
)
5342 lpColumn
->fmt
= lpColumnInfo
->fmt
;
5344 if (lpColumn
->mask
& LVCF_WIDTH
)
5345 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
5347 if (lpColumn
->mask
& LVCF_IMAGE
)
5348 lpColumn
->iImage
= hdi
.iImage
;
5350 if (lpColumn
->mask
& LVCF_ORDER
)
5351 lpColumn
->iOrder
= hdi
.iOrder
;
5353 if (lpColumn
->mask
& LVCF_SUBITEM
)
5354 lpColumn
->iSubItem
= hdi
.lParam
;
5360 static BOOL
LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
5367 /* FIXME: little hack */
5368 for (i
= 0; i
< iCount
; i
++)
5376 * Retrieves the column width.
5379 * [I] infoPtr : valid pointer to the listview structure
5380 * [I] int : column index
5383 * SUCCESS : column width
5386 static INT
LISTVIEW_GetColumnWidth(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
5388 INT nColumnWidth
= 0;
5391 TRACE("nColumn=%d\n", nColumn
);
5393 /* we have a 'column' in LIST and REPORT mode only */
5394 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
)
5397 nColumnWidth
= infoPtr
->nItemWidth
;
5400 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5401 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5402 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5404 * TODO: should we do the same in LVM_GETCOLUMN?
5406 hdItem
.mask
= HDI_WIDTH
;
5407 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdItem
))
5409 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr
->hwndSelf
, nColumn
);
5412 nColumnWidth
= hdItem
.cxy
;
5416 TRACE("nColumnWidth=%d\n", nColumnWidth
);
5417 return nColumnWidth
;
5422 * In list or report display mode, retrieves the number of items that can fit
5423 * vertically in the visible area. In icon or small icon display mode,
5424 * retrieves the total number of visible items.
5427 * [I] infoPtr : valid pointer to the listview structure
5430 * Number of fully visible items.
5432 static INT
LISTVIEW_GetCountPerPage(const LISTVIEW_INFO
*infoPtr
)
5434 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
5438 return infoPtr
->nItemCount
;
5440 return LISTVIEW_GetCountPerColumn(infoPtr
);
5442 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
5450 * Retrieves an image list handle.
5453 * [I] infoPtr : valid pointer to the listview structure
5454 * [I] nImageList : image list identifier
5457 * SUCCESS : image list handle
5460 static HIMAGELIST
LISTVIEW_GetImageList(const LISTVIEW_INFO
*infoPtr
, INT nImageList
)
5464 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
5465 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
5466 case LVSIL_STATE
: return infoPtr
->himlState
;
5471 /* LISTVIEW_GetISearchString */
5475 * Retrieves item attributes.
5478 * [I] hwnd : window handle
5479 * [IO] lpLVItem : item info
5480 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5481 * if FALSE, then lpLVItem is a LPLVITEMA.
5484 * This is the internal 'GetItem' interface -- it tries to
5485 * be smart and avoid text copies, if possible, by modifying
5486 * lpLVItem->pszText to point to the text string. Please note
5487 * that this is not always possible (e.g. OWNERDATA), so on
5488 * entry you *must* supply valid values for pszText, and cchTextMax.
5489 * The only difference to the documented interface is that upon
5490 * return, you should use *only* the lpLVItem->pszText, rather than
5491 * the buffer pointer you provided on input. Most code already does
5492 * that, so it's not a problem.
5493 * For the two cases when the text must be copied (that is,
5494 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5500 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5502 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
5503 NMLVDISPINFOW dispInfo
;
5509 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
5511 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5514 if (lpLVItem
->mask
== 0) return TRUE
;
5516 /* make a local copy */
5517 isubitem
= lpLVItem
->iSubItem
;
5519 /* a quick optimization if all we're asked is the focus state
5520 * these queries are worth optimising since they are common,
5521 * and can be answered in constant time, without the heavy accesses */
5522 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
5523 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
5525 lpLVItem
->state
= 0;
5526 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5527 lpLVItem
->state
|= LVIS_FOCUSED
;
5531 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5533 /* if the app stores all the data, handle it separately */
5534 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5536 dispInfo
.item
.state
= 0;
5538 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5539 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) || infoPtr
->uCallbackMask
)
5541 /* NOTE: copy only fields which we _know_ are initialized, some apps
5542 * depend on the uninitialized fields being 0 */
5543 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
5544 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5545 dispInfo
.item
.iSubItem
= isubitem
;
5546 if (lpLVItem
->mask
& LVIF_TEXT
)
5548 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5549 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5551 if (lpLVItem
->mask
& LVIF_STATE
)
5552 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
5553 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5554 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
5555 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
5557 /* full size structure expected - _WIN32IE >= 0x560 */
5558 *lpLVItem
= dispInfo
.item
;
5560 else if (lpLVItem
->mask
& LVIF_INDENT
)
5562 /* indent member expected - _WIN32IE >= 0x300 */
5563 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
5567 /* minimal structure expected */
5568 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
5570 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
5573 /* make sure lParam is zeroed out */
5574 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
5576 /* we store only a little state, so if we're not asked, we're done */
5577 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
5579 /* if focus is handled by us, report it */
5580 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5582 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5583 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5584 lpLVItem
->state
|= LVIS_FOCUSED
;
5587 /* and do the same for selection, if we handle it */
5588 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5590 lpLVItem
->state
&= ~LVIS_SELECTED
;
5591 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5592 lpLVItem
->state
|= LVIS_SELECTED
;
5598 /* find the item and subitem structures before we proceed */
5599 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
5600 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5605 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
5606 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
5609 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
5614 pItemHdr
= &lpItem
->hdr
;
5616 /* Do we need to query the state from the app? */
5617 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
5619 dispInfo
.item
.mask
|= LVIF_STATE
;
5620 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
5623 /* Do we need to enquire about the image? */
5624 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
5625 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
5627 dispInfo
.item
.mask
|= LVIF_IMAGE
;
5628 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
5631 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5632 if ((lpLVItem
->mask
& LVIF_TEXT
) && !is_textW(pItemHdr
->pszText
))
5634 dispInfo
.item
.mask
|= LVIF_TEXT
;
5635 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5636 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5637 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
5638 *dispInfo
.item
.pszText
= '\0';
5641 /* If we don't have all the requested info, query the application */
5642 if (dispInfo
.item
.mask
!= 0)
5644 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5645 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
5646 dispInfo
.item
.lParam
= lpItem
->lParam
;
5647 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5648 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
5651 /* we should not store values for subitems */
5652 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
5654 /* Now, handle the iImage field */
5655 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
5657 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
5658 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
5659 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
5661 else if (lpLVItem
->mask
& LVIF_IMAGE
)
5663 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
5664 lpLVItem
->iImage
= pItemHdr
->iImage
;
5666 lpLVItem
->iImage
= 0;
5669 /* The pszText field */
5670 if (dispInfo
.item
.mask
& LVIF_TEXT
)
5672 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
5673 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
5675 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
5677 else if (lpLVItem
->mask
& LVIF_TEXT
)
5679 if (isW
) lpLVItem
->pszText
= pItemHdr
->pszText
;
5680 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
5683 /* Next is the lParam field */
5684 if (dispInfo
.item
.mask
& LVIF_PARAM
)
5686 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
5687 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
5688 lpItem
->lParam
= dispInfo
.item
.lParam
;
5690 else if (lpLVItem
->mask
& LVIF_PARAM
)
5691 lpLVItem
->lParam
= lpItem
->lParam
;
5693 /* if this is a subitem, we're done */
5694 if (isubitem
) return TRUE
;
5696 /* ... the state field (this one is different due to uCallbackmask) */
5697 if (lpLVItem
->mask
& LVIF_STATE
)
5699 lpLVItem
->state
= lpItem
->state
& lpLVItem
->stateMask
;
5700 if (dispInfo
.item
.mask
& LVIF_STATE
)
5702 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
5703 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
5705 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5707 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5708 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5709 lpLVItem
->state
|= LVIS_FOCUSED
;
5711 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5713 lpLVItem
->state
&= ~LVIS_SELECTED
;
5714 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5715 lpLVItem
->state
|= LVIS_SELECTED
;
5719 /* and last, but not least, the indent field */
5720 if (lpLVItem
->mask
& LVIF_INDENT
)
5721 lpLVItem
->iIndent
= lpItem
->iIndent
;
5728 * Retrieves item attributes.
5731 * [I] hwnd : window handle
5732 * [IO] lpLVItem : item info
5733 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5734 * if FALSE, then lpLVItem is a LPLVITEMA.
5737 * This is the external 'GetItem' interface -- it properly copies
5738 * the text in the provided buffer.
5744 static BOOL
LISTVIEW_GetItemExtT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5749 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5752 pszText
= lpLVItem
->pszText
;
5753 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
5754 if (bResult
&& lpLVItem
->pszText
!= pszText
)
5755 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
5756 lpLVItem
->pszText
= pszText
;
5764 * Retrieves the position (upper-left) of the listview control item.
5765 * Note that for LVS_ICON style, the upper-left is that of the icon
5766 * and not the bounding box.
5769 * [I] infoPtr : valid pointer to the listview structure
5770 * [I] nItem : item index
5771 * [O] lpptPosition : coordinate information
5777 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
5779 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5782 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
5784 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5786 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5787 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
5789 if (uView
== LVS_ICON
)
5791 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
5792 lpptPosition
->y
+= ICON_TOP_PADDING
;
5794 lpptPosition
->x
+= Origin
.x
;
5795 lpptPosition
->y
+= Origin
.y
;
5797 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition
));
5804 * Retrieves the bounding rectangle for a listview control item.
5807 * [I] infoPtr : valid pointer to the listview structure
5808 * [I] nItem : item index
5809 * [IO] lprc : bounding rectangle coordinates
5810 * lprc->left specifies the portion of the item for which the bounding
5811 * rectangle will be retrieved.
5813 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5814 * including the icon and label.
5817 * * Experiment shows that native control returns:
5818 * * width = min (48, length of text line)
5819 * * .left = position.x - (width - iconsize.cx)/2
5820 * * .right = .left + width
5821 * * height = #lines of text * ntmHeight + icon height + 8
5822 * * .top = position.y - 2
5823 * * .bottom = .top + height
5824 * * separation between items .y = itemSpacing.cy - height
5825 * * .x = itemSpacing.cx - width
5826 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5829 * * Experiment shows that native control returns:
5830 * * width = iconSize.cx + 16
5831 * * .left = position.x - (width - iconsize.cx)/2
5832 * * .right = .left + width
5833 * * height = iconSize.cy + 4
5834 * * .top = position.y - 2
5835 * * .bottom = .top + height
5836 * * separation between items .y = itemSpacing.cy - height
5837 * * .x = itemSpacing.cx - width
5838 * LVIR_LABEL Returns the bounding rectangle of the item text.
5841 * * Experiment shows that native control returns:
5842 * * width = text length
5843 * * .left = position.x - width/2
5844 * * .right = .left + width
5845 * * height = ntmH * linecount + 2
5846 * * .top = position.y + iconSize.cy + 6
5847 * * .bottom = .top + height
5848 * * separation between items .y = itemSpacing.cy - height
5849 * * .x = itemSpacing.cx - width
5850 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5851 * rectangles, but excludes columns in report view.
5858 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5859 * upon whether the window has the focus currently and on whether the item
5860 * is the one with the focus. Ensure that the control's record of which
5861 * item has the focus agrees with the items' records.
5863 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5865 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5866 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5867 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
5868 POINT Position
, Origin
;
5871 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
5873 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5875 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5876 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5878 /* Be smart and try to figure out the minimum we have to do */
5879 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
5880 if (uView
== LVS_REPORT
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
5881 if (uView
== LVS_ICON
&& lprc
->left
!= LVIR_ICON
&&
5882 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
5883 oversizedBox
= TRUE
;
5885 /* get what we need from the item before hand, so we make
5886 * only one request. This can speed up things, if data
5887 * is stored on the app side */
5889 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
5890 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
5891 lvItem
.iItem
= nItem
;
5892 lvItem
.iSubItem
= 0;
5893 lvItem
.pszText
= szDispText
;
5894 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5895 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5896 /* we got the state already up, simulate it here, to avoid a reget */
5897 if (uView
== LVS_ICON
&& (lprc
->left
!= LVIR_ICON
))
5899 lvItem
.mask
|= LVIF_STATE
;
5900 lvItem
.stateMask
= LVIS_FOCUSED
;
5901 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
5904 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
5905 lprc
->left
= LVIR_BOUNDS
;
5909 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
5913 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, NULL
, lprc
);
5917 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
5920 case LVIR_SELECTBOUNDS
:
5921 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, lprc
, NULL
, NULL
, NULL
);
5925 WARN("Unknown value: %d\n", lprc
->left
);
5929 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
5931 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc
));
5938 * Retrieves the spacing between listview control items.
5941 * [I] infoPtr : valid pointer to the listview structure
5942 * [IO] lprc : rectangle to receive the output
5943 * on input, lprc->top = nSubItem
5944 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5946 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5947 * not only those of the first column.
5948 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5954 static BOOL
LISTVIEW_GetSubItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5960 if (!lprc
) return FALSE
;
5962 nColumn
= lprc
->top
;
5964 TRACE("(nItem=%d, nSubItem=%d)\n", nItem
, lprc
->top
);
5965 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5967 return LISTVIEW_GetItemRect(infoPtr
, nItem
, lprc
);
5969 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return FALSE
;
5971 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &Position
)) return FALSE
;
5973 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5976 lvItem
.iItem
= nItem
;
5977 lvItem
.iSubItem
= nColumn
;
5979 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5983 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
5988 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
5992 ERR("Unknown bounds=%d\n", lprc
->left
);
5996 OffsetRect(lprc
, Position
.x
, Position
.y
);
6003 * Retrieves the width of a label.
6006 * [I] infoPtr : valid pointer to the listview structure
6009 * SUCCESS : string width (in pixels)
6012 static INT
LISTVIEW_GetLabelWidth(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
6014 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6017 TRACE("(nItem=%d)\n", nItem
);
6019 lvItem
.mask
= LVIF_TEXT
;
6020 lvItem
.iItem
= nItem
;
6021 lvItem
.iSubItem
= 0;
6022 lvItem
.pszText
= szDispText
;
6023 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6024 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
6026 return LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
6031 * Retrieves the spacing between listview control items.
6034 * [I] infoPtr : valid pointer to the listview structure
6035 * [I] bSmall : flag for small or large icon
6038 * Horizontal + vertical spacing
6040 static LONG
LISTVIEW_GetItemSpacing(const LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
6046 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
6050 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
6051 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
6053 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
6060 * Retrieves the state of a listview control item.
6063 * [I] infoPtr : valid pointer to the listview structure
6064 * [I] nItem : item index
6065 * [I] uMask : state mask
6068 * State specified by the mask.
6070 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
6074 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6076 lvItem
.iItem
= nItem
;
6077 lvItem
.iSubItem
= 0;
6078 lvItem
.mask
= LVIF_STATE
;
6079 lvItem
.stateMask
= uMask
;
6080 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
6082 return lvItem
.state
& uMask
;
6087 * Retrieves the text of a listview control item or subitem.
6090 * [I] hwnd : window handle
6091 * [I] nItem : item index
6092 * [IO] lpLVItem : item information
6093 * [I] isW : TRUE if lpLVItem is Unicode
6096 * SUCCESS : string length
6099 static INT
LISTVIEW_GetItemTextT(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
6101 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6103 lpLVItem
->mask
= LVIF_TEXT
;
6104 lpLVItem
->iItem
= nItem
;
6105 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
6107 return textlenT(lpLVItem
->pszText
, isW
);
6112 * Searches for an item based on properties + relationships.
6115 * [I] infoPtr : valid pointer to the listview structure
6116 * [I] nItem : item index
6117 * [I] uFlags : relationship flag
6120 * SUCCESS : item index
6123 static INT
LISTVIEW_GetNextItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
6125 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6127 LVFINDINFOW lvFindInfo
;
6128 INT nCountPerColumn
;
6132 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
6133 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
6135 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
6137 if (uFlags
& LVNI_CUT
)
6140 if (uFlags
& LVNI_DROPHILITED
)
6141 uMask
|= LVIS_DROPHILITED
;
6143 if (uFlags
& LVNI_FOCUSED
)
6144 uMask
|= LVIS_FOCUSED
;
6146 if (uFlags
& LVNI_SELECTED
)
6147 uMask
|= LVIS_SELECTED
;
6149 /* if we're asked for the focused item, that's only one,
6150 * so it's worth optimizing */
6151 if (uFlags
& LVNI_FOCUSED
)
6153 if ((LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) != uMask
) return -1;
6154 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
6157 if (uFlags
& LVNI_ABOVE
)
6159 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
6164 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6170 /* Special case for autoarrange - move 'til the top of a list */
6171 if (is_autoarrange(infoPtr
))
6173 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6174 while (nItem
- nCountPerRow
>= 0)
6176 nItem
-= nCountPerRow
;
6177 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6182 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6183 lvFindInfo
.vkDirection
= VK_UP
;
6184 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6185 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6187 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6192 else if (uFlags
& LVNI_BELOW
)
6194 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
6196 while (nItem
< infoPtr
->nItemCount
)
6199 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6205 /* Special case for autoarrange - move 'til the bottom of a list */
6206 if (is_autoarrange(infoPtr
))
6208 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6209 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
6211 nItem
+= nCountPerRow
;
6212 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6217 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6218 lvFindInfo
.vkDirection
= VK_DOWN
;
6219 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6220 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6222 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6227 else if (uFlags
& LVNI_TOLEFT
)
6229 if (uView
== LVS_LIST
)
6231 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
6232 while (nItem
- nCountPerColumn
>= 0)
6234 nItem
-= nCountPerColumn
;
6235 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6239 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6241 /* Special case for autoarrange - move 'ti the beginning of a row */
6242 if (is_autoarrange(infoPtr
))
6244 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6245 while (nItem
% nCountPerRow
> 0)
6248 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6253 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6254 lvFindInfo
.vkDirection
= VK_LEFT
;
6255 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6256 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6258 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6263 else if (uFlags
& LVNI_TORIGHT
)
6265 if (uView
== LVS_LIST
)
6267 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
6268 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
6270 nItem
+= nCountPerColumn
;
6271 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6275 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6277 /* Special case for autoarrange - move 'til the end of a row */
6278 if (is_autoarrange(infoPtr
))
6280 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6281 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
6284 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6289 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6290 lvFindInfo
.vkDirection
= VK_RIGHT
;
6291 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6292 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6294 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6303 /* search by index */
6304 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
6306 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
6314 /* LISTVIEW_GetNumberOfWorkAreas */
6318 * Retrieves the origin coordinates when in icon or small icon display mode.
6321 * [I] infoPtr : valid pointer to the listview structure
6322 * [O] lpptOrigin : coordinate information
6327 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
6329 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6330 INT nHorzPos
= 0, nVertPos
= 0;
6331 SCROLLINFO scrollInfo
;
6333 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
6334 scrollInfo
.fMask
= SIF_POS
;
6336 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
6337 nHorzPos
= scrollInfo
.nPos
;
6338 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
6339 nVertPos
= scrollInfo
.nPos
;
6341 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
6343 lpptOrigin
->x
= infoPtr
->rcList
.left
;
6344 lpptOrigin
->y
= infoPtr
->rcList
.top
;
6345 if (uView
== LVS_LIST
)
6346 nHorzPos
*= infoPtr
->nItemWidth
;
6347 else if (uView
== LVS_REPORT
)
6348 nVertPos
*= infoPtr
->nItemHeight
;
6350 lpptOrigin
->x
-= nHorzPos
;
6351 lpptOrigin
->y
-= nVertPos
;
6353 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin
));
6358 * Retrieves the width of a string.
6361 * [I] hwnd : window handle
6362 * [I] lpszText : text string to process
6363 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6366 * SUCCESS : string width (in pixels)
6369 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
6374 if (is_textT(lpszText
, isW
))
6376 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
6377 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
6378 HFONT hOldFont
= SelectObject(hdc
, hFont
);
6381 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
6383 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
6384 SelectObject(hdc
, hOldFont
);
6385 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6387 return stringSize
.cx
;
6392 * Determines which listview item is located at the specified position.
6395 * [I] infoPtr : valid pointer to the listview structure
6396 * [IO] lpht : hit test information
6397 * [I] subitem : fill out iSubItem.
6398 * [I] select : return the index only if the hit selects the item
6401 * (mm 20001022): We must not allow iSubItem to be touched, for
6402 * an app might pass only a structure with space up to iItem!
6403 * (MS Office 97 does that for instance in the file open dialog)
6406 * SUCCESS : item index
6409 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
6411 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6412 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6413 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
6414 POINT Origin
, Position
, opt
;
6419 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht
->pt
), subitem
, select
);
6423 if (subitem
) lpht
->iSubItem
= 0;
6425 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
6426 lpht
->flags
|= LVHT_TOLEFT
;
6427 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
6428 lpht
->flags
|= LVHT_TORIGHT
;
6430 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
6431 lpht
->flags
|= LVHT_ABOVE
;
6432 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
6433 lpht
->flags
|= LVHT_BELOW
;
6435 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6436 if (lpht
->flags
) return -1;
6438 lpht
->flags
|= LVHT_NOWHERE
;
6440 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6442 /* first deal with the large items */
6443 rcSearch
.left
= lpht
->pt
.x
;
6444 rcSearch
.top
= lpht
->pt
.y
;
6445 rcSearch
.right
= rcSearch
.left
+ 1;
6446 rcSearch
.bottom
= rcSearch
.top
+ 1;
6448 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
6449 iterator_next(&i
); /* go to first item in the sequence */
6451 iterator_destroy(&i
);
6453 TRACE("lpht->iItem=%d\n", iItem
);
6454 if (iItem
== -1) return -1;
6456 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
6457 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
6458 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
6459 if (uView
== LVS_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
6460 lvItem
.iItem
= iItem
;
6461 lvItem
.iSubItem
= 0;
6462 lvItem
.pszText
= szDispText
;
6463 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6464 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
6465 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
6467 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
6468 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
6469 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
6470 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
6472 if (uView
== LVS_REPORT
)
6476 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6477 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6479 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds
));
6480 if (!PtInRect(&rcBounds
, opt
)) return -1;
6482 if (PtInRect(&rcIcon
, opt
))
6483 lpht
->flags
|= LVHT_ONITEMICON
;
6484 else if (PtInRect(&rcLabel
, opt
))
6485 lpht
->flags
|= LVHT_ONITEMLABEL
;
6486 else if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && PtInRect(&rcState
, opt
))
6487 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
6488 if (lpht
->flags
& LVHT_ONITEM
)
6489 lpht
->flags
&= ~LVHT_NOWHERE
;
6491 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6492 if (uView
== LVS_REPORT
&& subitem
)
6496 rcBounds
.right
= rcBounds
.left
;
6497 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
6499 rcBounds
.left
= rcBounds
.right
;
6500 rcBounds
.right
+= LISTVIEW_GetColumnWidth(infoPtr
, j
);
6501 if (PtInRect(&rcBounds
, opt
))
6509 if (select
&& !(uView
== LVS_REPORT
&&
6510 ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) ||
6511 (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))))
6513 if (uView
== LVS_REPORT
)
6515 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6516 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6518 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
6520 return lpht
->iItem
= iItem
;
6524 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6525 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6526 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6527 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6528 their own sort proc. when sending LVM_SORTITEMS.
6531 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6533 LVS_SORTXXX must be specified,
6534 LVS_OWNERDRAW is not set,
6535 <item>.pszText is not LPSTR_TEXTCALLBACK.
6537 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6538 are sorted based on item text..."
6540 static INT WINAPI
LISTVIEW_InsertCompare( LPVOID first
, LPVOID second
, LPARAM lParam
)
6542 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
6543 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
6544 INT cmpv
= textcmpWT(lv_first
->hdr
.pszText
, lv_second
->hdr
.pszText
, TRUE
);
6546 /* if we're sorting descending, negate the return value */
6547 return (((const LISTVIEW_INFO
*)lParam
)->dwStyle
& LVS_SORTDESCENDING
) ? -cmpv
: cmpv
;
6552 * Inserts a new item in the listview control.
6555 * [I] infoPtr : valid pointer to the listview structure
6556 * [I] lpLVItem : item information
6557 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6560 * SUCCESS : new item index
6563 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
6565 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6570 BOOL is_sorted
, has_changed
;
6572 HWND hwndSelf
= infoPtr
->hwndSelf
;
6574 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6576 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
6578 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6579 if (!lpLVItem
|| lpLVItem
->iSubItem
) return -1;
6581 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
6583 if (!(lpItem
= Alloc(sizeof(ITEM_INFO
)))) return -1;
6585 /* insert item in listview control data structure */
6586 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
6587 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
6589 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
6590 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
6592 if (lpLVItem
->iItem
< 0 && !is_sorted
) return -1;
6594 nItem
= is_sorted
? infoPtr
->nItemCount
: min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
6595 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
6596 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
6597 if (nItem
== -1) goto fail
;
6598 infoPtr
->nItemCount
++;
6600 /* shift indices first so they don't get tangled */
6601 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
6603 /* set the item attributes */
6604 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6606 /* full size structure expected - _WIN32IE >= 0x560 */
6609 else if (lpLVItem
->mask
& LVIF_INDENT
)
6611 /* indent member expected - _WIN32IE >= 0x300 */
6612 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
6616 /* minimal structure expected */
6617 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
6620 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
6622 item
.mask
|= LVIF_STATE
;
6623 item
.stateMask
|= LVIS_STATEIMAGEMASK
;
6624 item
.state
&= ~LVIS_STATEIMAGEMASK
;
6625 item
.state
|= INDEXTOSTATEIMAGEMASK(1);
6627 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
6629 /* if we're sorted, sort the list, and update the index */
6632 DPA_Sort( infoPtr
->hdpaItems
, LISTVIEW_InsertCompare
, (LPARAM
)infoPtr
);
6633 nItem
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, hdpaSubItems
);
6634 assert(nItem
!= -1);
6637 /* make room for the position, if we are in the right mode */
6638 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6640 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
6642 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
6644 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
6649 /* send LVN_INSERTITEM notification */
6650 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
6652 nmlv
.lParam
= lpItem
->lParam
;
6653 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
6654 if (!IsWindow(hwndSelf
))
6657 /* align items (set position of each item) */
6658 if ((uView
== LVS_SMALLICON
|| uView
== LVS_ICON
))
6662 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
6663 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
6665 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
6667 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
6670 /* now is the invalidation fun */
6671 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
6675 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
6676 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
6677 infoPtr
->nItemCount
--;
6679 DPA_DeletePtr(hdpaSubItems
, 0);
6680 DPA_Destroy (hdpaSubItems
);
6687 * Redraws a range of items.
6690 * [I] infoPtr : valid pointer to the listview structure
6691 * [I] nFirst : first item
6692 * [I] nLast : last item
6698 static BOOL
LISTVIEW_RedrawItems(const LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
6702 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
6703 max(nFirst
, nLast
) >= infoPtr
->nItemCount
)
6706 for (i
= nFirst
; i
<= nLast
; i
++)
6707 LISTVIEW_InvalidateItem(infoPtr
, i
);
6714 * Scroll the content of a listview.
6717 * [I] infoPtr : valid pointer to the listview structure
6718 * [I] dx : horizontal scroll amount in pixels
6719 * [I] dy : vertical scroll amount in pixels
6726 * If the control is in report mode (LVS_REPORT) the control can
6727 * be scrolled only in line increments. "dy" will be rounded to the
6728 * nearest number of pixels that are a whole line. Ex: if line height
6729 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6730 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6732 * For: (per experimentation with native control and CSpy ListView)
6733 * LVS_ICON dy=1 = 1 pixel (vertical only)
6735 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6737 * LVS_LIST dx=1 = 1 column (horizontal only)
6738 * but will only scroll 1 column per message
6739 * no matter what the value.
6740 * dy must be 0 or FALSE returned.
6741 * LVS_REPORT dx=1 = 1 pixel
6745 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
6747 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
) {
6749 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
6750 dy
/= infoPtr
->nItemHeight
;
6753 if (dy
!= 0) return FALSE
;
6760 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
, 0);
6761 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
, 0);
6768 * Sets the background color.
6771 * [I] infoPtr : valid pointer to the listview structure
6772 * [I] clrBk : background color
6778 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrBk
)
6780 TRACE("(clrBk=%x)\n", clrBk
);
6782 if(infoPtr
->clrBk
!= clrBk
) {
6783 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
6784 infoPtr
->clrBk
= clrBk
;
6785 if (clrBk
== CLR_NONE
)
6786 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
6788 infoPtr
->hBkBrush
= CreateSolidBrush(clrBk
);
6789 LISTVIEW_InvalidateList(infoPtr
);
6795 /* LISTVIEW_SetBkImage */
6797 /*** Helper for {Insert,Set}ColumnT *only* */
6798 static void column_fill_hditem(const LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
,
6799 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6801 if (lpColumn
->mask
& LVCF_FMT
)
6803 /* format member is valid */
6804 lphdi
->mask
|= HDI_FORMAT
;
6806 /* set text alignment (leftmost column must be left-aligned) */
6807 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
6808 lphdi
->fmt
|= HDF_LEFT
;
6809 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
6810 lphdi
->fmt
|= HDF_RIGHT
;
6811 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
6812 lphdi
->fmt
|= HDF_CENTER
;
6814 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6815 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
6817 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6819 lphdi
->fmt
|= HDF_IMAGE
;
6820 lphdi
->iImage
= I_IMAGECALLBACK
;
6824 if (lpColumn
->mask
& LVCF_WIDTH
)
6826 lphdi
->mask
|= HDI_WIDTH
;
6827 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
6829 /* make it fill the remainder of the controls width */
6833 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
6835 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
6836 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
6839 /* retrieve the layout of the header */
6840 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
6841 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, wine_dbgstr_rect(&rcHeader
));
6843 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
6846 lphdi
->cxy
= lpColumn
->cx
;
6849 if (lpColumn
->mask
& LVCF_TEXT
)
6851 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
6852 lphdi
->fmt
|= HDF_STRING
;
6853 lphdi
->pszText
= lpColumn
->pszText
;
6854 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
6857 if (lpColumn
->mask
& LVCF_IMAGE
)
6859 lphdi
->mask
|= HDI_IMAGE
;
6860 lphdi
->iImage
= lpColumn
->iImage
;
6863 if (lpColumn
->mask
& LVCF_ORDER
)
6865 lphdi
->mask
|= HDI_ORDER
;
6866 lphdi
->iOrder
= lpColumn
->iOrder
;
6873 * Inserts a new column.
6876 * [I] infoPtr : valid pointer to the listview structure
6877 * [I] nColumn : column index
6878 * [I] lpColumn : column information
6879 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6882 * SUCCESS : new column index
6885 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6886 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6888 COLUMN_INFO
*lpColumnInfo
;
6891 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6893 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6895 if (!lpColumn
|| nColumn
< 0) return -1;
6896 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
6898 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6899 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6902 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6903 * (can be seen in SPY) otherwise column never gets added.
6905 if (!(lpColumn
->mask
& LVCF_WIDTH
)) {
6906 hdi
.mask
|= HDI_WIDTH
;
6911 * when the iSubItem is available Windows copies it to the header lParam. It seems
6912 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6914 if (lpColumn
->mask
& LVCF_SUBITEM
)
6916 hdi
.mask
|= HDI_LPARAM
;
6917 hdi
.lParam
= lpColumn
->iSubItem
;
6920 /* create header if not present */
6921 LISTVIEW_CreateHeader(infoPtr
);
6922 if (!(LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
) &&
6923 (LVS_REPORT
== uView
) && (WS_VISIBLE
& infoPtr
->dwStyle
))
6925 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
6928 /* insert item in header control */
6929 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
6930 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
6931 (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6932 if (nNewColumn
== -1) return -1;
6933 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
6935 /* create our own column info */
6936 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
6937 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
6939 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
6940 if (!Header_GetItemRect(infoPtr
->hwndHeader
, nNewColumn
, &lpColumnInfo
->rcHeader
)) goto fail
;
6942 /* now we have to actually adjust the data */
6943 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
6945 SUBITEM_INFO
*lpSubItem
;
6949 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
6951 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
6952 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
6954 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
6955 if (lpSubItem
->iSubItem
>= nNewColumn
)
6956 lpSubItem
->iSubItem
++;
6961 /* make space for the new column */
6962 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
6963 LISTVIEW_UpdateItemSize(infoPtr
);
6968 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
6971 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
6979 * Sets the attributes of a header item.
6982 * [I] infoPtr : valid pointer to the listview structure
6983 * [I] nColumn : column index
6984 * [I] lpColumn : column attributes
6985 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6991 static BOOL
LISTVIEW_SetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6992 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6994 HDITEMW hdi
, hdiget
;
6997 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6999 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
7001 ZeroMemory(&hdi
, sizeof(HDITEMW
));
7002 if (lpColumn
->mask
& LVCF_FMT
)
7004 hdi
.mask
|= HDI_FORMAT
;
7005 hdiget
.mask
= HDI_FORMAT
;
7006 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdiget
))
7007 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
7009 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
7011 /* set header item attributes */
7012 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, (WPARAM
)nColumn
, (LPARAM
)&hdi
);
7013 if (!bResult
) return FALSE
;
7015 if (lpColumn
->mask
& LVCF_FMT
)
7017 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
7018 int oldFmt
= lpColumnInfo
->fmt
;
7020 lpColumnInfo
->fmt
= lpColumn
->fmt
;
7021 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
7023 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7024 if (uView
== LVS_REPORT
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
7033 * Sets the column order array
7036 * [I] infoPtr : valid pointer to the listview structure
7037 * [I] iCount : number of elements in column order array
7038 * [I] lpiArray : pointer to column order array
7044 static BOOL
LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
7046 FIXME("iCount %d lpiArray %p\n", iCount
, lpiArray
);
7057 * Sets the width of a column
7060 * [I] infoPtr : valid pointer to the listview structure
7061 * [I] nColumn : column index
7062 * [I] cx : column width
7068 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
7070 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7071 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
7075 TRACE("(nColumn=%d, cx=%d\n", nColumn
, cx
);
7077 /* set column width only if in report or list mode */
7078 if (uView
!= LVS_REPORT
&& uView
!= LVS_LIST
) return FALSE
;
7080 /* take care of invalid cx values */
7081 if(uView
== LVS_REPORT
&& cx
< -2) cx
= LVSCW_AUTOSIZE
;
7082 else if (uView
== LVS_LIST
&& cx
< 1) return FALSE
;
7084 /* resize all columns if in LVS_LIST mode */
7085 if(uView
== LVS_LIST
)
7087 infoPtr
->nItemWidth
= cx
;
7088 LISTVIEW_InvalidateList(infoPtr
);
7092 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
7094 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
7099 lvItem
.mask
= LVIF_TEXT
;
7101 lvItem
.iSubItem
= nColumn
;
7102 lvItem
.pszText
= szDispText
;
7103 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7104 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7106 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
7107 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
7108 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
7110 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
7111 max_cx
+= infoPtr
->iconSize
.cx
;
7112 max_cx
+= TRAILING_LABEL_PADDING
;
7115 /* autosize based on listview items width */
7116 if(cx
== LVSCW_AUTOSIZE
)
7118 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
7120 /* if iCol is the last column make it fill the remainder of the controls width */
7121 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
7126 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7127 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
7129 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
7133 /* Despite what the MS docs say, if this is not the last
7134 column, then MS resizes the column to the width of the
7135 largest text string in the column, including headers
7136 and items. This is different from LVSCW_AUTOSIZE in that
7137 LVSCW_AUTOSIZE ignores the header string length. */
7140 /* retrieve header text */
7141 hdi
.mask
= HDI_TEXT
;
7142 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
7143 hdi
.pszText
= szDispText
;
7144 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
))
7146 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
7147 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
7150 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
7151 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
7152 /* FIXME: Take into account the header image, if one is present */
7153 SelectObject(hdc
, old_font
);
7154 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7156 cx
= max (cx
, max_cx
);
7160 if (cx
< 0) return FALSE
;
7162 /* call header to update the column change */
7163 hdi
.mask
= HDI_WIDTH
;
7165 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
7166 return Header_SetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
);
7170 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7173 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO
*infoPtr
)
7176 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
7178 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
7179 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
7182 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
7183 ILC_COLOR
| ILC_MASK
, 2, 2);
7184 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
7185 hdc
= CreateCompatibleDC(hdc_wnd
);
7186 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
7187 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
7188 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
7190 rc
.left
= rc
.top
= 0;
7191 rc
.right
= GetSystemMetrics(SM_CXSMICON
);
7192 rc
.bottom
= GetSystemMetrics(SM_CYSMICON
);
7194 hbm_orig
= SelectObject(hdc
, hbm_mask
);
7195 FillRect(hdc
, &rc
, hbr_white
);
7196 InflateRect(&rc
, -3, -3);
7197 FillRect(hdc
, &rc
, hbr_black
);
7199 SelectObject(hdc
, hbm_im
);
7200 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
7201 SelectObject(hdc
, hbm_orig
);
7202 ImageList_Add(himl
, hbm_im
, hbm_mask
);
7204 SelectObject(hdc
, hbm_im
);
7205 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
7206 SelectObject(hdc
, hbm_orig
);
7207 ImageList_Add(himl
, hbm_im
, hbm_mask
);
7209 DeleteObject(hbm_mask
);
7210 DeleteObject(hbm_im
);
7218 * Sets the extended listview style.
7221 * [I] infoPtr : valid pointer to the listview structure
7223 * [I] dwStyle : style
7226 * SUCCESS : previous style
7229 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD dwMask
, DWORD dwExStyle
)
7231 DWORD dwOldExStyle
= infoPtr
->dwLvExStyle
;
7235 infoPtr
->dwLvExStyle
= (dwOldExStyle
& ~dwMask
) | (dwExStyle
& dwMask
);
7237 infoPtr
->dwLvExStyle
= dwExStyle
;
7239 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_CHECKBOXES
)
7241 HIMAGELIST himl
= 0;
7242 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
7245 item
.mask
= LVIF_STATE
;
7246 item
.stateMask
= LVIS_STATEIMAGEMASK
;
7247 item
.state
= INDEXTOSTATEIMAGEMASK(1);
7248 LISTVIEW_SetItemState(infoPtr
, -1, &item
);
7250 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
7252 LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
7255 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_HEADERDRAGDROP
)
7257 DWORD dwStyle
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
7258 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
)
7259 dwStyle
|= HDS_DRAGDROP
;
7261 dwStyle
&= ~HDS_DRAGDROP
;
7262 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, dwStyle
);
7265 /* GRIDLINES adds decoration at top so changes sizes */
7266 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_GRIDLINES
)
7268 LISTVIEW_UpdateSize(infoPtr
);
7272 LISTVIEW_InvalidateList(infoPtr
);
7273 return dwOldExStyle
;
7278 * Sets the new hot cursor used during hot tracking and hover selection.
7281 * [I] infoPtr : valid pointer to the listview structure
7282 * [I] hCursor : the new hot cursor handle
7285 * Returns the previous hot cursor
7287 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
7289 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
7291 infoPtr
->hHotCursor
= hCursor
;
7299 * Sets the hot item index.
7302 * [I] infoPtr : valid pointer to the listview structure
7303 * [I] iIndex : index
7306 * SUCCESS : previous hot item index
7307 * FAILURE : -1 (no hot item)
7309 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
7311 INT iOldIndex
= infoPtr
->nHotItem
;
7313 infoPtr
->nHotItem
= iIndex
;
7321 * Sets the amount of time the cursor must hover over an item before it is selected.
7324 * [I] infoPtr : valid pointer to the listview structure
7325 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7328 * Returns the previous hover time
7330 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
7332 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
7334 infoPtr
->dwHoverTime
= dwHoverTime
;
7336 return oldHoverTime
;
7341 * Sets spacing for icons of LVS_ICON style.
7344 * [I] infoPtr : valid pointer to the listview structure
7345 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7346 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7349 * MAKELONG(oldcx, oldcy)
7351 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
7353 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
7354 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7356 TRACE("requested=(%d,%d)\n", cx
, cy
);
7358 /* this is supported only for LVS_ICON style */
7359 if (uView
!= LVS_ICON
) return oldspacing
;
7361 /* set to defaults, if instructed to */
7362 if (cx
== -1) cx
= GetSystemMetrics(SM_CXICONSPACING
);
7363 if (cy
== -1) cy
= GetSystemMetrics(SM_CYICONSPACING
);
7365 /* if 0 then compute width
7366 * FIXME: Should scan each item and determine max width of
7367 * icon or label, then make that the width */
7369 cx
= infoPtr
->iconSpacing
.cx
;
7371 /* if 0 then compute height */
7373 cy
= infoPtr
->iconSize
.cy
+ 2 * infoPtr
->ntmHeight
+
7374 ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_PADDING
;
7377 infoPtr
->iconSpacing
.cx
= cx
;
7378 infoPtr
->iconSpacing
.cy
= cy
;
7380 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7381 LOWORD(oldspacing
), HIWORD(oldspacing
), cx
, cy
,
7382 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
7383 infoPtr
->ntmHeight
);
7385 /* these depend on the iconSpacing */
7386 LISTVIEW_UpdateItemSize(infoPtr
);
7391 static inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL small
)
7395 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
7402 size
->cx
= GetSystemMetrics(small
? SM_CXSMICON
: SM_CXICON
);
7403 size
->cy
= GetSystemMetrics(small
? SM_CYSMICON
: SM_CYICON
);
7412 * [I] infoPtr : valid pointer to the listview structure
7413 * [I] nType : image list type
7414 * [I] himl : image list handle
7417 * SUCCESS : old image list
7420 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
7422 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7423 INT oldHeight
= infoPtr
->nItemHeight
;
7424 HIMAGELIST himlOld
= 0;
7426 TRACE("(nType=%d, himl=%p\n", nType
, himl
);
7431 himlOld
= infoPtr
->himlNormal
;
7432 infoPtr
->himlNormal
= himl
;
7433 if (uView
== LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
7434 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
7438 himlOld
= infoPtr
->himlSmall
;
7439 infoPtr
->himlSmall
= himl
;
7440 if (uView
!= LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
7444 himlOld
= infoPtr
->himlState
;
7445 infoPtr
->himlState
= himl
;
7446 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
7447 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
7451 ERR("Unknown icon type=%d\n", nType
);
7455 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
7456 if (infoPtr
->nItemHeight
!= oldHeight
)
7457 LISTVIEW_UpdateScroll(infoPtr
);
7464 * Preallocates memory (does *not* set the actual count of items !)
7467 * [I] infoPtr : valid pointer to the listview structure
7468 * [I] nItems : item count (projected number of items to allocate)
7469 * [I] dwFlags : update flags
7475 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
7477 TRACE("(nItems=%d, dwFlags=%x)\n", nItems
, dwFlags
);
7479 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
7481 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7482 INT nOldCount
= infoPtr
->nItemCount
;
7484 if (nItems
< nOldCount
)
7486 RANGE range
= { nItems
, nOldCount
};
7487 ranges_del(infoPtr
->selectionRanges
, range
);
7488 if (infoPtr
->nFocusedItem
>= nItems
)
7490 infoPtr
->nFocusedItem
= -1;
7491 SetRectEmpty(&infoPtr
->rcFocus
);
7495 infoPtr
->nItemCount
= nItems
;
7496 LISTVIEW_UpdateScroll(infoPtr
);
7498 /* the flags are valid only in ownerdata report and list modes */
7499 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
) dwFlags
= 0;
7501 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
7502 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
7504 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
7505 LISTVIEW_InvalidateList(infoPtr
);
7512 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7513 nFrom
= min(nOldCount
, nItems
);
7514 nTo
= max(nOldCount
, nItems
);
7516 if (uView
== LVS_REPORT
)
7519 rcErase
.top
= nFrom
* infoPtr
->nItemHeight
;
7520 rcErase
.right
= infoPtr
->nItemWidth
;
7521 rcErase
.bottom
= nTo
* infoPtr
->nItemHeight
;
7522 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7523 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7524 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7528 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
7530 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
7531 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
7532 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
7533 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7534 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7535 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7536 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7538 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7540 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7541 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7542 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7543 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7544 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7550 /* According to MSDN for non-LVS_OWNERDATA this is just
7551 * a performance issue. The control allocates its internal
7552 * data structures for the number of items specified. It
7553 * cuts down on the number of memory allocations. Therefore
7554 * we will just issue a WARN here
7556 WARN("for non-ownerdata performance option not implemented.\n");
7564 * Sets the position of an item.
7567 * [I] infoPtr : valid pointer to the listview structure
7568 * [I] nItem : item index
7569 * [I] pt : coordinate
7575 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, POINT pt
)
7577 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7580 TRACE("(nItem=%d, &pt=%s\n", nItem
, wine_dbgstr_point(&pt
));
7582 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
7583 !(uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)) return FALSE
;
7585 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7587 /* This point value seems to be an undocumented feature.
7588 * The best guess is that it means either at the origin,
7589 * or at true beginning of the list. I will assume the origin. */
7590 if ((pt
.x
== -1) && (pt
.y
== -1))
7593 if (uView
== LVS_ICON
)
7595 pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
7596 pt
.y
-= ICON_TOP_PADDING
;
7601 infoPtr
->bAutoarrange
= FALSE
;
7603 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, FALSE
);
7608 * Sets the state of one or many items.
7611 * [I] infoPtr : valid pointer to the listview structure
7612 * [I] nItem : item index
7613 * [I] lpLVItem : item or subitem info
7619 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
)
7621 BOOL bResult
= TRUE
;
7624 lvItem
.iItem
= nItem
;
7625 lvItem
.iSubItem
= 0;
7626 lvItem
.mask
= LVIF_STATE
;
7627 lvItem
.state
= lpLVItem
->state
;
7628 lvItem
.stateMask
= lpLVItem
->stateMask
;
7629 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
7633 /* apply to all items */
7634 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7635 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) bResult
= FALSE
;
7638 bResult
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
7641 * Update selection mark
7643 * Investigation on windows 2k showed that selection mark was updated
7644 * whenever a new selection was made, but if the selected item was
7645 * unselected it was not updated.
7647 * we are probably still not 100% accurate, but this at least sets the
7648 * proper selection mark when it is needed
7651 if (bResult
&& (lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) &&
7652 (infoPtr
->nSelectionMark
== -1))
7655 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
7657 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
7659 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
7661 infoPtr
->nSelectionMark
= i
;
7665 else if (ranges_contain(infoPtr
->selectionRanges
, i
))
7667 infoPtr
->nSelectionMark
= i
;
7678 * Sets the text of an item or subitem.
7681 * [I] hwnd : window handle
7682 * [I] nItem : item index
7683 * [I] lpLVItem : item or subitem info
7684 * [I] isW : TRUE if input is Unicode
7690 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
7694 if (nItem
< 0 && nItem
>= infoPtr
->nItemCount
) return FALSE
;
7696 lvItem
.iItem
= nItem
;
7697 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
7698 lvItem
.mask
= LVIF_TEXT
;
7699 lvItem
.pszText
= lpLVItem
->pszText
;
7700 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
7702 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
7704 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
7709 * Set item index that marks the start of a multiple selection.
7712 * [I] infoPtr : valid pointer to the listview structure
7713 * [I] nIndex : index
7716 * Index number or -1 if there is no selection mark.
7718 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
7720 INT nOldIndex
= infoPtr
->nSelectionMark
;
7722 TRACE("(nIndex=%d)\n", nIndex
);
7724 infoPtr
->nSelectionMark
= nIndex
;
7731 * Sets the text background color.
7734 * [I] infoPtr : valid pointer to the listview structure
7735 * [I] clrTextBk : text background color
7741 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrTextBk
)
7743 TRACE("(clrTextBk=%x)\n", clrTextBk
);
7745 if (infoPtr
->clrTextBk
!= clrTextBk
)
7747 infoPtr
->clrTextBk
= clrTextBk
;
7748 LISTVIEW_InvalidateList(infoPtr
);
7756 * Sets the text foreground color.
7759 * [I] infoPtr : valid pointer to the listview structure
7760 * [I] clrText : text color
7766 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF clrText
)
7768 TRACE("(clrText=%x)\n", clrText
);
7770 if (infoPtr
->clrText
!= clrText
)
7772 infoPtr
->clrText
= clrText
;
7773 LISTVIEW_InvalidateList(infoPtr
);
7781 * Determines which listview item is located at the specified position.
7784 * [I] infoPtr : valid pointer to the listview structure
7785 * [I] hwndNewToolTip : handle to new ToolTip
7790 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
7792 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
7793 infoPtr
->hwndToolTip
= hwndNewToolTip
;
7794 return hwndOldToolTip
;
7799 * sets the Unicode character format flag for the control
7801 * [I] infoPtr :valid pointer to the listview structure
7802 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7805 * Old Unicode Format
7807 static BOOL
LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO
*infoPtr
, BOOL fUnicode
)
7809 BOOL rc
= infoPtr
->notifyFormat
;
7810 infoPtr
->notifyFormat
= (fUnicode
)?NFR_UNICODE
:NFR_ANSI
;
7814 /* LISTVIEW_SetWorkAreas */
7818 * Callback internally used by LISTVIEW_SortItems()
7821 * [I] first : pointer to first ITEM_INFO to compare
7822 * [I] second : pointer to second ITEM_INFO to compare
7823 * [I] lParam : HWND of control
7826 * if first comes before second : negative
7827 * if first comes after second : positive
7828 * if first and second are equivalent : zero
7830 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
7832 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
7833 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
7834 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
7836 /* Forward the call to the client defined callback */
7837 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
7842 * Sorts the listview items.
7845 * [I] infoPtr : valid pointer to the listview structure
7846 * [I] pfnCompare : application-defined value
7847 * [I] lParamSort : pointer to comparison callback
7853 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
, LPARAM lParamSort
)
7855 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7858 LPVOID selectionMarkItem
;
7862 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
7864 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
7866 if (!pfnCompare
) return FALSE
;
7867 if (!infoPtr
->hdpaItems
) return FALSE
;
7869 /* if there are 0 or 1 items, there is no need to sort */
7870 if (infoPtr
->nItemCount
< 2) return TRUE
;
7872 if (infoPtr
->nFocusedItem
>= 0)
7874 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
7875 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
7876 if (lpItem
) lpItem
->state
|= LVIS_FOCUSED
;
7878 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7879 /* clear the lpItem->state for non-selected ones */
7880 /* remove the selection ranges */
7882 infoPtr
->pfnCompare
= pfnCompare
;
7883 infoPtr
->lParamSort
= lParamSort
;
7884 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
7886 /* Adjust selections and indices so that they are the way they should
7887 * be after the sort (otherwise, the list items move around, but
7888 * whatever is at the item's previous original position will be
7891 selectionMarkItem
=(infoPtr
->nSelectionMark
>=0)?DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
):NULL
;
7892 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
7894 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
7895 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
7897 if (lpItem
->state
& LVIS_SELECTED
)
7899 item
.state
= LVIS_SELECTED
;
7900 item
.stateMask
= LVIS_SELECTED
;
7901 LISTVIEW_SetItemState(infoPtr
, i
, &item
);
7903 if (lpItem
->state
& LVIS_FOCUSED
)
7905 infoPtr
->nFocusedItem
= i
;
7906 lpItem
->state
&= ~LVIS_FOCUSED
;
7909 if (selectionMarkItem
!= NULL
)
7910 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
7911 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7913 /* refresh the display */
7914 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
)
7915 LISTVIEW_InvalidateList(infoPtr
);
7922 * Update theme handle after a theme change.
7925 * [I] infoPtr : valid pointer to the listview structure
7929 * FAILURE : something else
7931 static LRESULT
LISTVIEW_ThemeChanged(const LISTVIEW_INFO
*infoPtr
)
7933 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
7934 CloseThemeData(theme
);
7935 OpenThemeData(infoPtr
->hwndSelf
, themeClass
);
7941 * Updates an items or rearranges the listview control.
7944 * [I] infoPtr : valid pointer to the listview structure
7945 * [I] nItem : item index
7951 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
7953 TRACE("(nItem=%d)\n", nItem
);
7955 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
7957 /* rearrange with default alignment style */
7958 if (is_autoarrange(infoPtr
))
7959 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
7961 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
7968 * Draw the track line at the place defined in the infoPtr structure.
7969 * The line is drawn with a XOR pen so drawing the line for the second time
7970 * in the same place erases the line.
7973 * [I] infoPtr : valid pointer to the listview structure
7979 static BOOL
LISTVIEW_DrawTrackLine(const LISTVIEW_INFO
*infoPtr
)
7985 if (infoPtr
->xTrackLine
== -1)
7988 if (!(hdc
= GetDC(infoPtr
->hwndSelf
)))
7990 hOldPen
= SelectObject(hdc
, GetStockObject(BLACK_PEN
));
7991 oldROP
= SetROP2(hdc
, R2_XORPEN
);
7992 MoveToEx(hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.top
, NULL
);
7993 LineTo(hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.bottom
);
7994 SetROP2(hdc
, oldROP
);
7995 SelectObject(hdc
, hOldPen
);
7996 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
8002 * Called when an edit control should be displayed. This function is called after
8003 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8006 * [I] hwnd : Handle to the listview
8007 * [I] uMsg : WM_TIMER (ignored)
8008 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8009 * [I] dwTimer : The elapsed time (ignored)
8014 static VOID CALLBACK
LISTVIEW_DelayedEditItem(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
8016 DELAYED_ITEM_EDIT
*editItem
= (DELAYED_ITEM_EDIT
*)idEvent
;
8017 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
8019 KillTimer(hwnd
, idEvent
);
8020 editItem
->fEnabled
= FALSE
;
8021 /* check if the item is still selected */
8022 if (infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, editItem
->iItem
, LVIS_SELECTED
))
8023 LISTVIEW_EditLabelT(infoPtr
, editItem
->iItem
, TRUE
);
8028 * Creates the listview control - the WM_NCCREATE phase.
8031 * [I] hwnd : window handle
8032 * [I] lpcs : the create parameters
8038 static LRESULT
LISTVIEW_NCCreate(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
8040 LISTVIEW_INFO
*infoPtr
;
8043 TRACE("(lpcs=%p)\n", lpcs
);
8045 /* initialize info pointer */
8046 infoPtr
= Alloc(sizeof(LISTVIEW_INFO
));
8047 if (!infoPtr
) return FALSE
;
8049 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
8051 infoPtr
->hwndSelf
= hwnd
;
8052 infoPtr
->dwStyle
= lpcs
->style
; /* Note: may be changed in WM_CREATE */
8053 /* determine the type of structures to use */
8054 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
8055 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8057 /* initialize color information */
8058 infoPtr
->clrBk
= CLR_NONE
;
8059 infoPtr
->clrText
= CLR_DEFAULT
;
8060 infoPtr
->clrTextBk
= CLR_DEFAULT
;
8061 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
8063 /* set default values */
8064 infoPtr
->nFocusedItem
= -1;
8065 infoPtr
->nSelectionMark
= -1;
8066 infoPtr
->nHotItem
= -1;
8067 infoPtr
->bRedraw
= TRUE
;
8068 infoPtr
->bNoItemMetrics
= TRUE
;
8069 infoPtr
->bDoChangeNotify
= TRUE
;
8070 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
8071 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
8072 infoPtr
->nEditLabelItem
= -1;
8073 infoPtr
->dwHoverTime
= -1; /* default system hover time */
8074 infoPtr
->nMeasureItemHeight
= 0;
8075 infoPtr
->xTrackLine
= -1; /* no track line */
8076 infoPtr
->itemEdit
.fEnabled
= FALSE
;
8078 /* get default font (icon title) */
8079 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
8080 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
8081 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
8082 LISTVIEW_SaveTextMetrics(infoPtr
);
8084 /* allocate memory for the data structure */
8085 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
8086 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
8087 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
8088 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
8089 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
8093 DestroyWindow(infoPtr
->hwndHeader
);
8094 ranges_destroy(infoPtr
->selectionRanges
);
8095 DPA_Destroy(infoPtr
->hdpaItems
);
8096 DPA_Destroy(infoPtr
->hdpaPosX
);
8097 DPA_Destroy(infoPtr
->hdpaPosY
);
8098 DPA_Destroy(infoPtr
->hdpaColumns
);
8105 * Creates the listview control - the WM_CREATE phase. Most of the data is
8106 * already set up in LISTVIEW_NCCreate
8109 * [I] hwnd : window handle
8110 * [I] lpcs : the create parameters
8116 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
8118 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
8119 UINT uView
= lpcs
->style
& LVS_TYPEMASK
;
8121 TRACE("(lpcs=%p)\n", lpcs
);
8123 infoPtr
->dwStyle
= lpcs
->style
;
8124 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
8125 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
8127 if ((uView
== LVS_REPORT
) && (lpcs
->style
& WS_VISIBLE
))
8129 if (LISTVIEW_CreateHeader(infoPtr
) < 0) return -1;
8132 infoPtr
->hwndHeader
= 0;
8134 /* init item size to avoid division by 0 */
8135 LISTVIEW_UpdateItemSize (infoPtr
);
8137 if (uView
== LVS_REPORT
)
8139 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
) && (WS_VISIBLE
& lpcs
->style
))
8141 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
8143 LISTVIEW_UpdateSize(infoPtr
);
8144 LISTVIEW_UpdateScroll(infoPtr
);
8147 OpenThemeData(hwnd
, themeClass
);
8149 /* initialize the icon sizes */
8150 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, uView
!= LVS_ICON
);
8151 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
8157 * Destroys the listview control.
8160 * [I] infoPtr : valid pointer to the listview structure
8166 static LRESULT
LISTVIEW_Destroy(const LISTVIEW_INFO
*infoPtr
)
8168 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
8169 CloseThemeData(theme
);
8175 * Enables the listview control.
8178 * [I] infoPtr : valid pointer to the listview structure
8179 * [I] bEnable : specifies whether to enable or disable the window
8185 static BOOL
LISTVIEW_Enable(const LISTVIEW_INFO
*infoPtr
, BOOL bEnable
)
8187 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
8188 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
8194 * Erases the background of the listview control.
8197 * [I] infoPtr : valid pointer to the listview structure
8198 * [I] hdc : device context handle
8204 static inline BOOL
LISTVIEW_EraseBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
8208 TRACE("(hdc=%p)\n", hdc
);
8210 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
8212 /* for double buffered controls we need to do this during refresh */
8213 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) return FALSE
;
8215 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
8221 * Helper function for LISTVIEW_[HV]Scroll *only*.
8222 * Performs vertical/horizontal scrolling by a give amount.
8225 * [I] infoPtr : valid pointer to the listview structure
8226 * [I] dx : amount of horizontal scroll
8227 * [I] dy : amount of vertical scroll
8229 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
8231 /* now we can scroll the list */
8232 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
8233 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
8234 /* if we have focus, adjust rect */
8235 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
8236 UpdateWindow(infoPtr
->hwndSelf
);
8241 * Performs vertical scrolling.
8244 * [I] infoPtr : valid pointer to the listview structure
8245 * [I] nScrollCode : scroll code
8246 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8247 * [I] hScrollWnd : scrollbar control window handle
8253 * SB_LINEUP/SB_LINEDOWN:
8254 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8255 * for LVS_REPORT is 1 line
8256 * for LVS_LIST cannot occur
8259 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
8260 INT nScrollDiff
, HWND hScrollWnd
)
8262 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8263 INT nOldScrollPos
, nNewScrollPos
;
8264 SCROLLINFO scrollInfo
;
8267 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
8268 debugscrollcode(nScrollCode
), nScrollDiff
);
8270 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8272 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8273 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
8275 is_an_icon
= ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
));
8277 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
8279 nOldScrollPos
= scrollInfo
.nPos
;
8280 switch (nScrollCode
)
8286 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
8290 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
8294 nScrollDiff
= -scrollInfo
.nPage
;
8298 nScrollDiff
= scrollInfo
.nPage
;
8301 case SB_THUMBPOSITION
:
8303 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
8310 /* quit right away if pos isn't changing */
8311 if (nScrollDiff
== 0) return 0;
8313 /* calculate new position, and handle overflows */
8314 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
8315 if (nScrollDiff
> 0) {
8316 if (nNewScrollPos
< nOldScrollPos
||
8317 nNewScrollPos
> scrollInfo
.nMax
)
8318 nNewScrollPos
= scrollInfo
.nMax
;
8320 if (nNewScrollPos
> nOldScrollPos
||
8321 nNewScrollPos
< scrollInfo
.nMin
)
8322 nNewScrollPos
= scrollInfo
.nMin
;
8325 /* set the new position, and reread in case it changed */
8326 scrollInfo
.fMask
= SIF_POS
;
8327 scrollInfo
.nPos
= nNewScrollPos
;
8328 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
8330 /* carry on only if it really changed */
8331 if (nNewScrollPos
== nOldScrollPos
) return 0;
8333 /* now adjust to client coordinates */
8334 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
8335 if (uView
== LVS_REPORT
) nScrollDiff
*= infoPtr
->nItemHeight
;
8337 /* and scroll the window */
8338 scroll_list(infoPtr
, 0, nScrollDiff
);
8345 * Performs horizontal scrolling.
8348 * [I] infoPtr : valid pointer to the listview structure
8349 * [I] nScrollCode : scroll code
8350 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8351 * [I] hScrollWnd : scrollbar control window handle
8357 * SB_LINELEFT/SB_LINERIGHT:
8358 * for LVS_ICON, LVS_SMALLICON 1 pixel
8359 * for LVS_REPORT is 1 pixel
8360 * for LVS_LIST is 1 column --> which is a 1 because the
8361 * scroll is based on columns not pixels
8364 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
8365 INT nScrollDiff
, HWND hScrollWnd
)
8367 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8368 INT nOldScrollPos
, nNewScrollPos
;
8369 SCROLLINFO scrollInfo
;
8371 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
8372 debugscrollcode(nScrollCode
), nScrollDiff
);
8374 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8376 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8377 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
8379 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
8381 nOldScrollPos
= scrollInfo
.nPos
;
8383 switch (nScrollCode
)
8397 nScrollDiff
= -scrollInfo
.nPage
;
8401 nScrollDiff
= scrollInfo
.nPage
;
8404 case SB_THUMBPOSITION
:
8406 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
8413 /* quit right away if pos isn't changing */
8414 if (nScrollDiff
== 0) return 0;
8416 /* calculate new position, and handle overflows */
8417 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
8418 if (nScrollDiff
> 0) {
8419 if (nNewScrollPos
< nOldScrollPos
||
8420 nNewScrollPos
> scrollInfo
.nMax
)
8421 nNewScrollPos
= scrollInfo
.nMax
;
8423 if (nNewScrollPos
> nOldScrollPos
||
8424 nNewScrollPos
< scrollInfo
.nMin
)
8425 nNewScrollPos
= scrollInfo
.nMin
;
8428 /* set the new position, and reread in case it changed */
8429 scrollInfo
.fMask
= SIF_POS
;
8430 scrollInfo
.nPos
= nNewScrollPos
;
8431 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
8433 /* carry on only if it really changed */
8434 if (nNewScrollPos
== nOldScrollPos
) return 0;
8436 if(uView
== LVS_REPORT
)
8437 LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
8439 /* now adjust to client coordinates */
8440 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
8441 if (uView
== LVS_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
8443 /* and scroll the window */
8444 scroll_list(infoPtr
, nScrollDiff
, 0);
8449 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
8451 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8452 INT gcWheelDelta
= 0;
8453 INT pulScrollLines
= 3;
8454 SCROLLINFO scrollInfo
;
8456 TRACE("(wheelDelta=%d)\n", wheelDelta
);
8458 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
8459 gcWheelDelta
-= wheelDelta
;
8461 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8462 scrollInfo
.fMask
= SIF_POS
;
8469 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8470 * should be fixed in the future.
8472 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (gcWheelDelta
< 0) ?
8473 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
, 0);
8477 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
8479 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
8480 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
8481 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, cLineScroll
, 0);
8486 LISTVIEW_HScroll(infoPtr
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0, 0);
8497 * [I] infoPtr : valid pointer to the listview structure
8498 * [I] nVirtualKey : virtual key
8499 * [I] lKeyData : key data
8504 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
8506 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8507 HWND hwndSelf
= infoPtr
->hwndSelf
;
8509 NMLVKEYDOWN nmKeyDown
;
8511 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey
, lKeyData
);
8513 /* send LVN_KEYDOWN notification */
8514 nmKeyDown
.wVKey
= nVirtualKey
;
8515 nmKeyDown
.flags
= 0;
8516 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
8517 if (!IsWindow(hwndSelf
))
8520 switch (nVirtualKey
)
8523 nItem
= infoPtr
->nFocusedItem
;
8524 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
8525 toggle_checkbox_state(infoPtr
, infoPtr
->nFocusedItem
);
8529 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
8531 if (!notify(infoPtr
, NM_RETURN
)) return 0;
8532 if (!notify(infoPtr
, LVN_ITEMACTIVATE
)) return 0;
8537 if (infoPtr
->nItemCount
> 0)
8542 if (infoPtr
->nItemCount
> 0)
8543 nItem
= infoPtr
->nItemCount
- 1;
8547 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
8551 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
8555 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
8559 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
8563 if (uView
== LVS_REPORT
)
8565 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8566 if (infoPtr
->nFocusedItem
== topidx
)
8567 nItem
= topidx
- LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
8572 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
8573 * LISTVIEW_GetCountPerRow(infoPtr
);
8574 if(nItem
< 0) nItem
= 0;
8578 if (uView
== LVS_REPORT
)
8580 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8581 INT cnt
= LISTVIEW_GetCountPerColumn(infoPtr
);
8582 if (infoPtr
->nFocusedItem
== topidx
+ cnt
- 1)
8583 nItem
= infoPtr
->nFocusedItem
+ cnt
- 1;
8585 nItem
= topidx
+ cnt
- 1;
8588 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
8589 * LISTVIEW_GetCountPerRow(infoPtr
);
8590 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
8594 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
|| nVirtualKey
== VK_SPACE
))
8595 LISTVIEW_KeySelection(infoPtr
, nItem
, nVirtualKey
== VK_SPACE
);
8605 * [I] infoPtr : valid pointer to the listview structure
8610 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
8614 /* if we did not have the focus, there's nothing to do */
8615 if (!infoPtr
->bFocus
) return 0;
8617 /* send NM_KILLFOCUS notification */
8618 if (!notify(infoPtr
, NM_KILLFOCUS
)) return 0;
8620 /* if we have a focus rectagle, get rid of it */
8621 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
8623 /* set window focus flag */
8624 infoPtr
->bFocus
= FALSE
;
8626 /* invalidate the selected items before resetting focus flag */
8627 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8634 * Processes double click messages (left mouse button).
8637 * [I] infoPtr : valid pointer to the listview structure
8638 * [I] wKey : key flag
8639 * [I] x,y : mouse coordinate
8644 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8646 LVHITTESTINFO htInfo
;
8648 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8650 /* Cancel the item edition if any */
8651 if (infoPtr
->itemEdit
.fEnabled
)
8653 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
)&infoPtr
->itemEdit
);
8654 infoPtr
->itemEdit
.fEnabled
= FALSE
;
8657 /* send NM_RELEASEDCAPTURE notification */
8658 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8663 /* send NM_DBLCLK notification */
8664 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
8665 if (!notify_click(infoPtr
, NM_DBLCLK
, &htInfo
)) return 0;
8667 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8668 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
8675 * Processes mouse down messages (left mouse button).
8678 * infoPtr [I ] valid pointer to the listview structure
8679 * wKey [I ] key flag
8680 * x,y [I ] mouse coordinate
8685 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8687 LVHITTESTINFO lvHitTestInfo
;
8688 static BOOL bGroupSelect
= TRUE
;
8689 POINT pt
= { x
, y
};
8692 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8694 /* send NM_RELEASEDCAPTURE notification */
8695 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8697 /* set left button down flag and record the click position */
8698 infoPtr
->bLButtonDown
= TRUE
;
8699 infoPtr
->ptClickPos
= pt
;
8700 infoPtr
->bDragging
= FALSE
;
8702 lvHitTestInfo
.pt
.x
= x
;
8703 lvHitTestInfo
.pt
.y
= y
;
8705 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8706 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt
), nItem
);
8707 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8709 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
8711 toggle_checkbox_state(infoPtr
, nItem
);
8715 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
8717 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8718 infoPtr
->nEditLabelItem
= nItem
;
8720 LISTVIEW_SetSelection(infoPtr
, nItem
);
8724 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
8728 if (!LISTVIEW_AddGroupSelection(infoPtr
, nItem
)) return 0;
8729 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8730 infoPtr
->nSelectionMark
= nItem
;
8736 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
8737 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8739 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
8740 infoPtr
->nSelectionMark
= nItem
;
8743 else if (wKey
& MK_CONTROL
)
8747 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
8749 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
8750 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8751 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
8752 infoPtr
->nSelectionMark
= nItem
;
8754 else if (wKey
& MK_SHIFT
)
8756 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
8760 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8761 infoPtr
->nEditLabelItem
= nItem
;
8763 /* set selection (clears other pre-existing selections) */
8764 LISTVIEW_SetSelection(infoPtr
, nItem
);
8768 if (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
)
8769 if(lvHitTestInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&lvHitTestInfo
);
8773 /* remove all selections */
8774 LISTVIEW_DeselectAll(infoPtr
);
8783 * Processes mouse up messages (left mouse button).
8786 * infoPtr [I ] valid pointer to the listview structure
8787 * wKey [I ] key flag
8788 * x,y [I ] mouse coordinate
8793 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8795 LVHITTESTINFO lvHitTestInfo
;
8797 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8799 if (!infoPtr
->bLButtonDown
) return 0;
8801 lvHitTestInfo
.pt
.x
= x
;
8802 lvHitTestInfo
.pt
.y
= y
;
8804 /* send NM_CLICK notification */
8805 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8806 if (!notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
)) return 0;
8808 /* set left button flag */
8809 infoPtr
->bLButtonDown
= FALSE
;
8811 if (infoPtr
->bDragging
)
8813 infoPtr
->bDragging
= FALSE
;
8817 /* if we clicked on a selected item, edit the label */
8818 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
8820 /* we want to make sure the user doesn't want to do a double click. So we will
8821 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8823 infoPtr
->itemEdit
.fEnabled
= TRUE
;
8824 infoPtr
->itemEdit
.iItem
= lvHitTestInfo
.iItem
;
8825 SetTimer(infoPtr
->hwndSelf
,
8826 (UINT_PTR
)&infoPtr
->itemEdit
,
8827 GetDoubleClickTime(),
8828 LISTVIEW_DelayedEditItem
);
8831 if (!infoPtr
->bFocus
)
8832 SetFocus(infoPtr
->hwndSelf
);
8839 * Destroys the listview control (called after WM_DESTROY).
8842 * [I] infoPtr : valid pointer to the listview structure
8847 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
8851 /* delete all items */
8852 LISTVIEW_DeleteAllItems(infoPtr
, TRUE
);
8854 /* destroy data structure */
8855 DPA_Destroy(infoPtr
->hdpaItems
);
8856 DPA_Destroy(infoPtr
->hdpaPosX
);
8857 DPA_Destroy(infoPtr
->hdpaPosY
);
8858 DPA_Destroy(infoPtr
->hdpaColumns
);
8859 ranges_destroy(infoPtr
->selectionRanges
);
8861 /* destroy image lists */
8862 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
8864 if (infoPtr
->himlNormal
)
8865 ImageList_Destroy(infoPtr
->himlNormal
);
8866 if (infoPtr
->himlSmall
)
8867 ImageList_Destroy(infoPtr
->himlSmall
);
8868 if (infoPtr
->himlState
)
8869 ImageList_Destroy(infoPtr
->himlState
);
8872 /* destroy font, bkgnd brush */
8874 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
8875 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
8877 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
8879 /* free listview info pointer*/
8887 * Handles notifications from header.
8890 * [I] infoPtr : valid pointer to the listview structure
8891 * [I] nCtrlId : control identifier
8892 * [I] lpnmh : notification information
8897 static LRESULT
LISTVIEW_HeaderNotification(LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
8899 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8900 HWND hwndSelf
= infoPtr
->hwndSelf
;
8902 TRACE("(lpnmh=%p)\n", lpnmh
);
8904 if (!lpnmh
|| lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
8906 switch (lpnmh
->hdr
.code
)
8911 COLUMN_INFO
*lpColumnInfo
;
8915 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8918 /* remove the old line (if any) */
8919 LISTVIEW_DrawTrackLine(infoPtr
);
8921 /* compute & draw the new line */
8922 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8923 x
= lpColumnInfo
->rcHeader
.left
+ lpnmh
->pitem
->cxy
;
8924 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
8925 infoPtr
->xTrackLine
= x
+ ptOrigin
.x
;
8926 LISTVIEW_DrawTrackLine(infoPtr
);
8932 /* remove the track line (if any) */
8933 LISTVIEW_DrawTrackLine(infoPtr
);
8934 infoPtr
->xTrackLine
= -1;
8938 FIXME("Changing column order not implemented\n");
8941 case HDN_ITEMCHANGINGW
:
8942 case HDN_ITEMCHANGINGA
:
8943 return notify_forward_header(infoPtr
, lpnmh
);
8945 case HDN_ITEMCHANGEDW
:
8946 case HDN_ITEMCHANGEDA
:
8948 COLUMN_INFO
*lpColumnInfo
;
8951 notify_forward_header(infoPtr
, lpnmh
);
8952 if (!IsWindow(hwndSelf
))
8955 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8959 hdi
.mask
= HDI_WIDTH
;
8960 if (!Header_GetItemW(infoPtr
->hwndHeader
, lpnmh
->iItem
, &hdi
)) return 0;
8964 cxy
= lpnmh
->pitem
->cxy
;
8966 /* determine how much we change since the last know position */
8967 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8968 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
8971 lpColumnInfo
->rcHeader
.right
+= dx
;
8972 if (lpnmh
->iItem
+ 1 < DPA_GetPtrCount(infoPtr
->hdpaColumns
))
8973 LISTVIEW_ScrollColumns(infoPtr
, lpnmh
->iItem
+ 1, dx
);
8976 /* only needs to update the scrolls */
8977 infoPtr
->nItemWidth
+= dx
;
8978 LISTVIEW_UpdateScroll(infoPtr
);
8980 LISTVIEW_UpdateItemSize(infoPtr
);
8981 if (uView
== LVS_REPORT
&& is_redrawing(infoPtr
))
8984 RECT rcCol
= lpColumnInfo
->rcHeader
;
8986 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
8987 OffsetRect(&rcCol
, ptOrigin
.x
, 0);
8989 rcCol
.top
= infoPtr
->rcList
.top
;
8990 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
8992 /* resizing left-aligned columns leaves most of the left side untouched */
8993 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
8995 INT nMaxDirty
= infoPtr
->nEllipsisWidth
+ infoPtr
->ntmMaxCharWidth
;
8998 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- nMaxDirty
);
9001 /* when shrinking the last column clear the now unused field */
9002 if (lpnmh
->iItem
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1 && dx
< 0)
9005 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
9011 case HDN_ITEMCLICKW
:
9012 case HDN_ITEMCLICKA
:
9014 /* Handle sorting by Header Column */
9017 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
9019 nmlv
.iSubItem
= lpnmh
->iItem
;
9020 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
9021 notify_forward_header(infoPtr
, lpnmh
);
9025 case HDN_DIVIDERDBLCLICKW
:
9026 case HDN_DIVIDERDBLCLICKA
:
9027 LISTVIEW_SetColumnWidth(infoPtr
, lpnmh
->iItem
, LVSCW_AUTOSIZE
);
9036 * Paint non-client area of control.
9039 * [I] infoPtr : valid pointer to the listview structureof the sender
9040 * [I] region : update region
9043 * TRUE - frame was painted
9044 * FALSE - call default window proc
9046 static BOOL
LISTVIEW_NCPaint(const LISTVIEW_INFO
*infoPtr
, HRGN region
)
9048 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
9052 int cxEdge
= GetSystemMetrics (SM_CXEDGE
),
9053 cyEdge
= GetSystemMetrics (SM_CYEDGE
);
9055 if (!theme
) return FALSE
;
9057 GetWindowRect(infoPtr
->hwndSelf
, &r
);
9059 cliprgn
= CreateRectRgn (r
.left
+ cxEdge
, r
.top
+ cyEdge
,
9060 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
9061 if (region
!= (HRGN
)1)
9062 CombineRgn (cliprgn
, cliprgn
, region
, RGN_AND
);
9063 OffsetRect(&r
, -r
.left
, -r
.top
);
9065 dc
= GetDCEx(infoPtr
->hwndSelf
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
9066 OffsetRect(&r
, -r
.left
, -r
.top
);
9068 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
9069 DrawThemeParentBackground(infoPtr
->hwndSelf
, dc
, &r
);
9070 DrawThemeBackground (theme
, dc
, 0, 0, &r
, 0);
9071 ReleaseDC(infoPtr
->hwndSelf
, dc
);
9073 /* Call default proc to get the scrollbars etc. painted */
9074 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)cliprgn
, 0);
9081 * Determines the type of structure to use.
9084 * [I] infoPtr : valid pointer to the listview structureof the sender
9085 * [I] hwndFrom : listview window handle
9086 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9091 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
9093 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
9095 if (nCommand
== NF_REQUERY
)
9096 infoPtr
->notifyFormat
= SendMessageW(hwndFrom
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
9098 return infoPtr
->notifyFormat
;
9103 * Paints/Repaints the listview control.
9106 * [I] infoPtr : valid pointer to the listview structure
9107 * [I] hdc : device context handle
9112 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
9114 TRACE("(hdc=%p)\n", hdc
);
9116 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
9118 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9120 infoPtr
->bNoItemMetrics
= FALSE
;
9121 LISTVIEW_UpdateItemSize(infoPtr
);
9122 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
9123 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9124 LISTVIEW_UpdateScroll(infoPtr
);
9127 UpdateWindow(infoPtr
->hwndHeader
);
9130 LISTVIEW_Refresh(infoPtr
, hdc
, NULL
);
9135 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
9137 LISTVIEW_Refresh(infoPtr
, hdc
, ps
.fErase
? &ps
.rcPaint
: NULL
);
9138 EndPaint(infoPtr
->hwndSelf
, &ps
);
9147 * Paints/Repaints the listview control.
9150 * [I] infoPtr : valid pointer to the listview structure
9151 * [I] hdc : device context handle
9152 * [I] options : drawing options
9157 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
9159 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc
, options
);
9161 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
9164 if (options
& PRF_ERASEBKGND
)
9165 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
9167 if (options
& PRF_CLIENT
)
9168 LISTVIEW_Paint(infoPtr
, hdc
);
9176 * Processes double click 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_RButtonDblClk(const LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9188 LVHITTESTINFO lvHitTestInfo
;
9190 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9192 /* send NM_RELEASEDCAPTURE notification */
9193 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9195 /* send NM_RDBLCLK notification */
9196 lvHitTestInfo
.pt
.x
= x
;
9197 lvHitTestInfo
.pt
.y
= y
;
9198 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9199 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
9206 * Processes mouse down messages (right mouse button).
9209 * [I] infoPtr : valid pointer to the listview structure
9210 * [I] wKey : key flag
9211 * [I] x,y : mouse coordinate
9216 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9218 LVHITTESTINFO lvHitTestInfo
;
9221 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9223 /* send NM_RELEASEDCAPTURE notification */
9224 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9226 /* make sure the listview control window has the focus */
9227 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
9229 /* set right button down flag */
9230 infoPtr
->bRButtonDown
= TRUE
;
9232 /* determine the index of the selected item */
9233 lvHitTestInfo
.pt
.x
= x
;
9234 lvHitTestInfo
.pt
.y
= y
;
9235 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
9237 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
9239 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
9240 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
9241 !LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
9242 LISTVIEW_SetSelection(infoPtr
, nItem
);
9246 LISTVIEW_DeselectAll(infoPtr
);
9254 * Processes mouse up messages (right mouse button).
9257 * [I] infoPtr : valid pointer to the listview structure
9258 * [I] wKey : key flag
9259 * [I] x,y : mouse coordinate
9264 static LRESULT
LISTVIEW_RButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9266 LVHITTESTINFO lvHitTestInfo
;
9269 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9271 if (!infoPtr
->bRButtonDown
) return 0;
9273 /* set button flag */
9274 infoPtr
->bRButtonDown
= FALSE
;
9276 /* Send NM_RClICK notification */
9277 lvHitTestInfo
.pt
.x
= x
;
9278 lvHitTestInfo
.pt
.y
= y
;
9279 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9280 if (!notify_click(infoPtr
, NM_RCLICK
, &lvHitTestInfo
)) return 0;
9282 /* Change to screen coordinate for WM_CONTEXTMENU */
9283 pt
= lvHitTestInfo
.pt
;
9284 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
9286 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9287 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
9288 (WPARAM
)infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
9299 * [I] infoPtr : valid pointer to the listview structure
9300 * [I] hwnd : window handle of window containing the cursor
9301 * [I] nHittest : hit-test code
9302 * [I] wMouseMsg : ideintifier of the mouse message
9305 * TRUE if cursor is set
9308 static BOOL
LISTVIEW_SetCursor(const LISTVIEW_INFO
*infoPtr
, HWND hwnd
, UINT nHittest
, UINT wMouseMsg
)
9310 LVHITTESTINFO lvHitTestInfo
;
9312 if(!(LISTVIEW_isHotTracking(infoPtr
))) return FALSE
;
9314 if(!infoPtr
->hHotCursor
) return FALSE
;
9316 GetCursorPos(&lvHitTestInfo
.pt
);
9317 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) return FALSE
;
9319 SetCursor(infoPtr
->hHotCursor
);
9329 * [I] infoPtr : valid pointer to the listview structure
9330 * [I] hwndLoseFocus : handle of previously focused window
9335 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
9337 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
9339 /* if we have the focus already, there's nothing to do */
9340 if (infoPtr
->bFocus
) return 0;
9342 /* send NM_SETFOCUS notification */
9343 if (!notify(infoPtr
, NM_SETFOCUS
)) return 0;
9345 /* set window focus flag */
9346 infoPtr
->bFocus
= TRUE
;
9348 /* put the focus rect back on */
9349 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
9351 /* redraw all visible selected items */
9352 LISTVIEW_InvalidateSelectedItems(infoPtr
);
9362 * [I] infoPtr : valid pointer to the listview structure
9363 * [I] fRedraw : font handle
9364 * [I] fRedraw : redraw flag
9369 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
9371 HFONT oldFont
= infoPtr
->hFont
;
9373 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
9375 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
9376 if (infoPtr
->hFont
== oldFont
) return 0;
9378 LISTVIEW_SaveTextMetrics(infoPtr
);
9380 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
)
9382 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
9383 LISTVIEW_UpdateSize(infoPtr
);
9384 LISTVIEW_UpdateScroll(infoPtr
);
9387 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
9394 * Message handling for WM_SETREDRAW.
9395 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9398 * [I] infoPtr : valid pointer to the listview structure
9399 * [I] bRedraw: state of redraw flag
9402 * DefWinProc return value
9404 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
9406 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr
->bRedraw
, bRedraw
);
9408 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9409 if ((infoPtr
->bRedraw
&& bRedraw
) || (!infoPtr
->bRedraw
&& !bRedraw
)) return 0;
9411 infoPtr
->bRedraw
= bRedraw
;
9413 if(!bRedraw
) return 0;
9415 if (is_autoarrange(infoPtr
))
9416 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9417 LISTVIEW_UpdateScroll(infoPtr
);
9419 /* despite what the WM_SETREDRAW docs says, apps expect us
9420 * to invalidate the listview here... stupid! */
9421 LISTVIEW_InvalidateList(infoPtr
);
9428 * Resizes the listview control. This function processes WM_SIZE
9429 * messages. At this time, the width and height are not used.
9432 * [I] infoPtr : valid pointer to the listview structure
9433 * [I] Width : new width
9434 * [I] Height : new height
9439 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
9441 RECT rcOld
= infoPtr
->rcList
;
9443 TRACE("(width=%d, height=%d)\n", Width
, Height
);
9445 LISTVIEW_UpdateSize(infoPtr
);
9446 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
9448 /* do not bother with display related stuff if we're not redrawing */
9449 if (!is_redrawing(infoPtr
)) return 0;
9451 if (is_autoarrange(infoPtr
))
9452 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9454 LISTVIEW_UpdateScroll(infoPtr
);
9456 /* refresh all only for lists whose height changed significantly */
9457 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
&&
9458 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
9459 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
9460 LISTVIEW_InvalidateList(infoPtr
);
9467 * Sets the size information.
9470 * [I] infoPtr : valid pointer to the listview structure
9475 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
9477 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9479 TRACE("uView=%d, rcList(old)=%s\n", uView
, wine_dbgstr_rect(&infoPtr
->rcList
));
9481 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
9483 if (uView
== LVS_LIST
)
9485 /* Apparently the "LIST" style is supposed to have the same
9486 * number of items in a column even if there is no scroll bar.
9487 * Since if a scroll bar already exists then the bottom is already
9488 * reduced, only reduce if the scroll bar does not currently exist.
9489 * The "2" is there to mimic the native control. I think it may be
9490 * related to either padding or edges. (GLA 7/2002)
9492 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_HSCROLL
))
9493 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
9494 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
9496 else if (uView
== LVS_REPORT
)
9501 hl
.prc
= &infoPtr
->rcList
;
9503 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9504 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp
.flags
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
9505 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9506 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
9507 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9508 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
9510 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
9511 infoPtr
->rcList
.top
+= (infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
) ? 2 : 0;
9514 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr
->rcList
));
9519 * Processes WM_STYLECHANGED messages.
9522 * [I] infoPtr : valid pointer to the listview structure
9523 * [I] wStyleType : window style type (normal or extended)
9524 * [I] lpss : window style information
9529 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
9530 const STYLESTRUCT
*lpss
)
9532 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
9533 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
9536 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9537 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
9539 if (wStyleType
!= GWL_STYLE
) return 0;
9541 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9542 /* what if LVS_OWNERDATA changed? */
9543 /* or LVS_SINGLESEL */
9544 /* or LVS_SORT{AS,DES}CENDING */
9546 infoPtr
->dwStyle
= lpss
->styleNew
;
9548 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
9549 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
9550 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
9552 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
9553 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
9554 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
9556 if (uNewView
!= uOldView
)
9558 SIZE oldIconSize
= infoPtr
->iconSize
;
9561 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9562 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
9564 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
9565 SetRectEmpty(&infoPtr
->rcFocus
);
9567 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
9568 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
9570 if (uNewView
== LVS_ICON
)
9572 if ((infoPtr
->iconSize
.cx
!= oldIconSize
.cx
) || (infoPtr
->iconSize
.cy
!= oldIconSize
.cy
))
9574 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9575 oldIconSize
.cx
, oldIconSize
.cy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
9576 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
9579 else if (uNewView
== LVS_REPORT
)
9584 LISTVIEW_CreateHeader( infoPtr
);
9586 hl
.prc
= &infoPtr
->rcList
;
9588 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9589 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9590 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
9591 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9594 LISTVIEW_UpdateItemSize(infoPtr
);
9597 if (uNewView
== LVS_REPORT
)
9599 if ((lpss
->styleOld
^ lpss
->styleNew
) & LVS_NOCOLUMNHEADER
)
9601 if (lpss
->styleNew
& LVS_NOCOLUMNHEADER
)
9603 /* Turn off the header control */
9604 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
9605 TRACE("Hide header control, was 0x%08x\n", style
);
9606 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
| HDS_HIDDEN
);
9608 /* Turn on the header control */
9609 if ((style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
)) & HDS_HIDDEN
)
9611 TRACE("Show header control, was 0x%08x\n", style
);
9612 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, (style
& ~HDS_HIDDEN
) | WS_VISIBLE
);
9618 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
9619 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
9620 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9622 /* update the size of the client area */
9623 LISTVIEW_UpdateSize(infoPtr
);
9625 /* add scrollbars if needed */
9626 LISTVIEW_UpdateScroll(infoPtr
);
9628 /* invalidate client area + erase background */
9629 LISTVIEW_InvalidateList(infoPtr
);
9636 * Processes WM_SHOWWINDOW messages.
9639 * [I] infoPtr : valid pointer to the listview structure
9640 * [I] bShown : window is being shown (FALSE when hidden)
9641 * [I] iStatus : window show status
9646 static LRESULT
LISTVIEW_ShowWindow(LISTVIEW_INFO
*infoPtr
, BOOL bShown
, INT iStatus
)
9648 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9650 /* header delayed creation */
9651 if ((uView
== LVS_REPORT
) && bShown
)
9653 LISTVIEW_CreateHeader(infoPtr
);
9655 if (!(LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
))
9656 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
9664 * Window procedure of the listview control.
9667 static LRESULT WINAPI
9668 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9670 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9672 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg
, wParam
, lParam
);
9674 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
9675 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9679 case LVM_APPROXIMATEVIEWRECT
:
9680 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
9681 LOWORD(lParam
), HIWORD(lParam
));
9683 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
9685 /* case LVM_CANCELEDITLABEL: */
9687 case LVM_CREATEDRAGIMAGE
:
9688 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9690 case LVM_DELETEALLITEMS
:
9691 return LISTVIEW_DeleteAllItems(infoPtr
, FALSE
);
9693 case LVM_DELETECOLUMN
:
9694 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
9696 case LVM_DELETEITEM
:
9697 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
9699 case LVM_EDITLABELW
:
9700 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, TRUE
);
9702 case LVM_EDITLABELA
:
9703 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, FALSE
);
9705 /* case LVM_ENABLEGROUPVIEW: */
9707 case LVM_ENSUREVISIBLE
:
9708 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
9711 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
9714 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
9716 case LVM_GETBKCOLOR
:
9717 return infoPtr
->clrBk
;
9719 /* case LVM_GETBKIMAGE: */
9721 case LVM_GETCALLBACKMASK
:
9722 return infoPtr
->uCallbackMask
;
9724 case LVM_GETCOLUMNA
:
9725 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9727 case LVM_GETCOLUMNW
:
9728 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9730 case LVM_GETCOLUMNORDERARRAY
:
9731 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9733 case LVM_GETCOLUMNWIDTH
:
9734 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
9736 case LVM_GETCOUNTPERPAGE
:
9737 return LISTVIEW_GetCountPerPage(infoPtr
);
9739 case LVM_GETEDITCONTROL
:
9740 return (LRESULT
)infoPtr
->hwndEdit
;
9742 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
9743 return infoPtr
->dwLvExStyle
;
9745 /* case LVM_GETGROUPINFO: */
9747 /* case LVM_GETGROUPMETRICS: */
9750 return (LRESULT
)infoPtr
->hwndHeader
;
9752 case LVM_GETHOTCURSOR
:
9753 return (LRESULT
)infoPtr
->hHotCursor
;
9755 case LVM_GETHOTITEM
:
9756 return infoPtr
->nHotItem
;
9758 case LVM_GETHOVERTIME
:
9759 return infoPtr
->dwHoverTime
;
9761 case LVM_GETIMAGELIST
:
9762 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
9764 /* case LVM_GETINSERTMARK: */
9766 /* case LVM_GETINSERTMARKCOLOR: */
9768 /* case LVM_GETINSERTMARKRECT: */
9770 case LVM_GETISEARCHSTRINGA
:
9771 case LVM_GETISEARCHSTRINGW
:
9772 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9776 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9779 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9781 case LVM_GETITEMCOUNT
:
9782 return infoPtr
->nItemCount
;
9784 case LVM_GETITEMPOSITION
:
9785 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9787 case LVM_GETITEMRECT
:
9788 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
9790 case LVM_GETITEMSPACING
:
9791 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
9793 case LVM_GETITEMSTATE
:
9794 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
9796 case LVM_GETITEMTEXTA
:
9797 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9799 case LVM_GETITEMTEXTW
:
9800 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9802 case LVM_GETNEXTITEM
:
9803 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
9805 case LVM_GETNUMBEROFWORKAREAS
:
9806 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9810 if (!lParam
) return FALSE
;
9811 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
||
9812 (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
) return FALSE
;
9813 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
9816 /* case LVM_GETOUTLINECOLOR: */
9818 /* case LVM_GETSELECTEDCOLUMN: */
9820 case LVM_GETSELECTEDCOUNT
:
9821 return LISTVIEW_GetSelectedCount(infoPtr
);
9823 case LVM_GETSELECTIONMARK
:
9824 return infoPtr
->nSelectionMark
;
9826 case LVM_GETSTRINGWIDTHA
:
9827 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, FALSE
);
9829 case LVM_GETSTRINGWIDTHW
:
9830 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, TRUE
);
9832 case LVM_GETSUBITEMRECT
:
9833 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
9835 case LVM_GETTEXTBKCOLOR
:
9836 return infoPtr
->clrTextBk
;
9838 case LVM_GETTEXTCOLOR
:
9839 return infoPtr
->clrText
;
9841 /* case LVM_GETTILEINFO: */
9843 /* case LVM_GETTILEVIEWINFO: */
9845 case LVM_GETTOOLTIPS
:
9846 if( !infoPtr
->hwndToolTip
)
9847 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
9848 return (LRESULT
)infoPtr
->hwndToolTip
;
9850 case LVM_GETTOPINDEX
:
9851 return LISTVIEW_GetTopIndex(infoPtr
);
9853 case LVM_GETUNICODEFORMAT
:
9854 return (infoPtr
->notifyFormat
== NFR_UNICODE
);
9856 /* case LVM_GETVIEW: */
9858 case LVM_GETVIEWRECT
:
9859 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
9861 case LVM_GETWORKAREAS
:
9862 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9865 /* case LVM_HASGROUP: */
9868 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, FALSE
);
9870 case LVM_INSERTCOLUMNA
:
9871 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9873 case LVM_INSERTCOLUMNW
:
9874 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9876 /* case LVM_INSERTGROUP: */
9878 /* case LVM_INSERTGROUPSORTED: */
9880 case LVM_INSERTITEMA
:
9881 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9883 case LVM_INSERTITEMW
:
9884 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9886 /* case LVM_INSERTMARKHITTEST: */
9888 /* case LVM_ISGROUPVIEWENABLED: */
9890 /* case LVM_MAPIDTOINDEX: */
9892 /* case LVM_MAPINDEXTOID: */
9894 /* case LVM_MOVEGROUP: */
9896 /* case LVM_MOVEITEMTOGROUP: */
9898 case LVM_REDRAWITEMS
:
9899 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9901 /* case LVM_REMOVEALLGROUPS: */
9903 /* case LVM_REMOVEGROUP: */
9906 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9908 case LVM_SETBKCOLOR
:
9909 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
9911 /* case LVM_SETBKIMAGE: */
9913 case LVM_SETCALLBACKMASK
:
9914 infoPtr
->uCallbackMask
= (UINT
)wParam
;
9917 case LVM_SETCOLUMNA
:
9918 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9920 case LVM_SETCOLUMNW
:
9921 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9923 case LVM_SETCOLUMNORDERARRAY
:
9924 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9926 case LVM_SETCOLUMNWIDTH
:
9927 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
9929 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
9930 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
9932 /* case LVM_SETGROUPINFO: */
9934 /* case LVM_SETGROUPMETRICS: */
9936 case LVM_SETHOTCURSOR
:
9937 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
9939 case LVM_SETHOTITEM
:
9940 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
9942 case LVM_SETHOVERTIME
:
9943 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)wParam
);
9945 case LVM_SETICONSPACING
:
9946 return LISTVIEW_SetIconSpacing(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
9948 case LVM_SETIMAGELIST
:
9949 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
9951 /* case LVM_SETINFOTIP: */
9953 /* case LVM_SETINSERTMARK: */
9955 /* case LVM_SETINSERTMARKCOLOR: */
9958 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9961 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9963 case LVM_SETITEMCOUNT
:
9964 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
9966 case LVM_SETITEMPOSITION
:
9969 pt
.x
= (short)LOWORD(lParam
);
9970 pt
.y
= (short)HIWORD(lParam
);
9971 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, pt
);
9974 case LVM_SETITEMPOSITION32
:
9975 if (lParam
== 0) return FALSE
;
9976 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, *((POINT
*)lParam
));
9978 case LVM_SETITEMSTATE
:
9979 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
9981 case LVM_SETITEMTEXTA
:
9982 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9984 case LVM_SETITEMTEXTW
:
9985 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9987 /* case LVM_SETOUTLINECOLOR: */
9989 /* case LVM_SETSELECTEDCOLUMN: */
9991 case LVM_SETSELECTIONMARK
:
9992 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
9994 case LVM_SETTEXTBKCOLOR
:
9995 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
9997 case LVM_SETTEXTCOLOR
:
9998 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
10000 /* case LVM_SETTILEINFO: */
10002 /* case LVM_SETTILEVIEWINFO: */
10004 /* case LVM_SETTILEWIDTH: */
10006 case LVM_SETTOOLTIPS
:
10007 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
10009 case LVM_SETUNICODEFORMAT
:
10010 return LISTVIEW_SetUnicodeFormat(infoPtr
, wParam
);
10012 /* case LVM_SETVIEW: */
10014 /* case LVM_SETWORKAREAS: */
10016 /* case LVM_SORTGROUPS: */
10018 case LVM_SORTITEMS
:
10019 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
);
10021 /* LVM_SORTITEMSEX: */
10023 case LVM_SUBITEMHITTEST
:
10024 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
10027 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
10030 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
10033 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
10036 return LISTVIEW_NCCreate(hwnd
, (LPCREATESTRUCTW
)lParam
);
10039 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
10042 return LISTVIEW_Destroy(infoPtr
);
10045 return LISTVIEW_Enable(infoPtr
, (BOOL
)wParam
);
10047 case WM_ERASEBKGND
:
10048 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
10050 case WM_GETDLGCODE
:
10051 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
10054 return (LRESULT
)infoPtr
->hFont
;
10057 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
10060 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
10063 return LISTVIEW_KillFocus(infoPtr
);
10065 case WM_LBUTTONDBLCLK
:
10066 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10068 case WM_LBUTTONDOWN
:
10069 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10072 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10075 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10077 case WM_MOUSEHOVER
:
10078 return LISTVIEW_MouseHover(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10081 return LISTVIEW_NCDestroy(infoPtr
);
10084 if (LISTVIEW_NCPaint(infoPtr
, (HRGN
)wParam
))
10089 if (lParam
&& ((LPNMHDR
)lParam
)->hwndFrom
== infoPtr
->hwndHeader
)
10090 return LISTVIEW_HeaderNotification(infoPtr
, (LPNMHEADERW
)lParam
);
10093 case WM_NOTIFYFORMAT
:
10094 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
10096 case WM_PRINTCLIENT
:
10097 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
10100 return LISTVIEW_Paint(infoPtr
, (HDC
)wParam
);
10102 case WM_RBUTTONDBLCLK
:
10103 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10105 case WM_RBUTTONDOWN
:
10106 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10109 return LISTVIEW_RButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10112 if(LISTVIEW_SetCursor(infoPtr
, (HWND
)wParam
, LOWORD(lParam
), HIWORD(lParam
)))
10117 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
10120 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
10123 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
10125 case WM_SHOWWINDOW
:
10126 LISTVIEW_ShowWindow(infoPtr
, (BOOL
)wParam
, (INT
)lParam
);
10127 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10130 return LISTVIEW_Size(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
10132 case WM_STYLECHANGED
:
10133 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
10135 case WM_SYSCOLORCHANGE
:
10136 COMCTL32_RefreshSysColors();
10139 /* case WM_TIMER: */
10140 case WM_THEMECHANGED
:
10141 return LISTVIEW_ThemeChanged(infoPtr
);
10144 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
10146 case WM_MOUSEWHEEL
:
10147 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
10148 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10149 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
10151 case WM_WINDOWPOSCHANGED
:
10152 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
10154 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
10155 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
10156 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
10158 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
10160 MEASUREITEMSTRUCT mis
;
10161 mis
.CtlType
= ODT_LISTVIEW
;
10162 mis
.CtlID
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
10166 mis
.itemHeight
= infoPtr
->nItemHeight
;
10167 SendMessageW(infoPtr
->hwndNotify
, WM_MEASUREITEM
, mis
.CtlID
, (LPARAM
)&mis
);
10168 if (infoPtr
->nItemHeight
!= max(mis
.itemHeight
, 1))
10169 infoPtr
->nMeasureItemHeight
= infoPtr
->nItemHeight
= max(mis
.itemHeight
, 1);
10172 LISTVIEW_UpdateSize(infoPtr
);
10173 LISTVIEW_UpdateScroll(infoPtr
);
10175 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10177 /* case WM_WININICHANGE: */
10180 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
) && !COMCTL32_IsReflectedMessage(uMsg
))
10181 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg
, wParam
, lParam
);
10184 /* call default window procedure */
10185 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10192 * Registers the window class.
10200 void LISTVIEW_Register(void)
10202 WNDCLASSW wndClass
;
10204 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
10205 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
10206 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
10207 wndClass
.cbClsExtra
= 0;
10208 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
10209 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
10210 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
10211 wndClass
.lpszClassName
= WC_LISTVIEWW
;
10212 RegisterClassW(&wndClass
);
10217 * Unregisters the window class.
10225 void LISTVIEW_Unregister(void)
10227 UnregisterClassW(WC_LISTVIEWW
, NULL
);
10232 * Handle any WM_COMMAND messages
10235 * [I] infoPtr : valid pointer to the listview structure
10236 * [I] wParam : the first message parameter
10237 * [I] lParam : the second message parameter
10242 static LRESULT
LISTVIEW_Command(const LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
10244 switch (HIWORD(wParam
))
10249 * Adjust the edit window size
10251 WCHAR buffer
[1024];
10252 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
10253 HFONT hFont
, hOldFont
= 0;
10257 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
10258 GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
10259 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
10261 /* Select font to get the right dimension of the string */
10262 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
10265 hOldFont
= SelectObject(hdc
, hFont
);
10268 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
10270 TEXTMETRICW textMetric
;
10272 /* Add Extra spacing for the next character */
10273 GetTextMetricsW(hdc
, &textMetric
);
10274 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10282 rect
.bottom
- rect
.top
,
10283 SWP_DRAWFRAME
|SWP_NOMOVE
);
10286 SelectObject(hdc
, hOldFont
);
10288 ReleaseDC(infoPtr
->hwndEdit
, hdc
);
10294 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);
10303 * Subclassed edit control windproc function
10306 * [I] hwnd : the edit window handle
10307 * [I] uMsg : the message that is to be processed
10308 * [I] wParam : first message parameter
10309 * [I] lParam : second message parameter
10310 * [I] isW : TRUE if input is Unicode
10315 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
10317 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
10318 BOOL cancel
= FALSE
;
10320 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10321 hwnd
, uMsg
, wParam
, lParam
, isW
);
10325 case WM_GETDLGCODE
:
10326 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
10333 WNDPROC editProc
= infoPtr
->EditWndProc
;
10334 infoPtr
->EditWndProc
= 0;
10335 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
10336 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10340 if (VK_ESCAPE
== (INT
)wParam
)
10345 else if (VK_RETURN
== (INT
)wParam
)
10349 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10352 /* kill the edit */
10353 if (infoPtr
->hwndEdit
)
10355 LPWSTR buffer
= NULL
;
10357 infoPtr
->hwndEdit
= 0;
10360 DWORD len
= isW
? GetWindowTextLengthW(hwnd
) : GetWindowTextLengthA(hwnd
);
10364 if ( (buffer
= Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))) )
10366 if (isW
) GetWindowTextW(hwnd
, buffer
, len
+1);
10367 else GetWindowTextA(hwnd
, (CHAR
*)buffer
, len
+1);
10371 LISTVIEW_EndEditLabelT(infoPtr
, buffer
, isW
);
10376 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
10382 * Subclassed edit control Unicode windproc function
10385 * [I] hwnd : the edit window handle
10386 * [I] uMsg : the message that is to be processed
10387 * [I] wParam : first message parameter
10388 * [I] lParam : second message parameter
10392 static LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10394 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
10399 * Subclassed edit control ANSI windproc function
10402 * [I] hwnd : the edit window handle
10403 * [I] uMsg : the message that is to be processed
10404 * [I] wParam : first message parameter
10405 * [I] lParam : second message parameter
10409 static LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10411 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
10416 * Creates a subclassed edit control
10419 * [I] infoPtr : valid pointer to the listview structure
10420 * [I] text : initial text for the edit
10421 * [I] style : the window style
10422 * [I] isW : TRUE if input is Unicode
10426 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, DWORD style
,
10427 INT x
, INT y
, INT width
, INT height
, BOOL isW
)
10429 WCHAR editName
[5] = { 'E', 'd', 'i', 't', '\0' };
10434 TEXTMETRICW textMetric
;
10435 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
10437 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text
, isW
), isW
);
10439 style
|= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
;
10440 hdc
= GetDC(infoPtr
->hwndSelf
);
10442 /* Select the font to get appropriate metric dimensions */
10443 if(infoPtr
->hFont
!= 0)
10444 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
10446 /*Get String Length in pixels */
10447 GetTextExtentPoint32W(hdc
, text
, lstrlenW(text
), &sz
);
10449 /*Add Extra spacing for the next character */
10450 GetTextMetricsW(hdc
, &textMetric
);
10451 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10453 if(infoPtr
->hFont
!= 0)
10454 SelectObject(hdc
, hOldFont
);
10456 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
10458 hedit
= CreateWindowW(editName
, text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
10460 hedit
= CreateWindowA("Edit", (LPCSTR
)text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
10462 if (!hedit
) return 0;
10464 infoPtr
->EditWndProc
= (WNDPROC
)
10465 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
10466 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
10468 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);