4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
35 * Default Message Processing
36 * -- EN_KILLFOCUS should be handled in WM_COMMAND
37 * -- WM_CREATE: create the icon and small icon image lists at this point only if
38 * the LVS_SHAREIMAGELISTS style is not specified.
39 * -- WM_ERASEBKGND: forward this message to the parent window if the bkgnd
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_[GS]etColumnOrderArray stubs
58 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
59 * -- LISTVIEW_SetIconSpacing is incomplete
60 * -- LISTVIEW_SortItems is broken
61 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
64 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
65 * linear in the number of items in the list, and this is
66 * unacceptable for large lists.
67 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
68 * instead of inserting in the right spot
69 * -- we should keep an ordered array of coordinates in iconic mode
70 * this would allow to frame items (iterator_frameditems),
71 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
79 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
86 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
89 * -- LVS_TYPESTYLEMASK
92 * -- LVS_EX_BORDERSELECT
94 * -- LVS_EX_HEADERDRAGDROP
97 * -- LVS_EX_MULTIWORKAREAS
99 * -- LVS_EX_SIMPLESELECT
100 * -- LVS_EX_TWOCLICKACTIVATE
101 * -- LVS_EX_UNDERLINECOLD
102 * -- LVS_EX_UNDERLINEHOT
105 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
108 * -- LVN_MARQUEEBEGIN
114 * -- LVM_CANCELEDITLABEL
115 * -- LVM_ENABLEGROUPVIEW
116 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
117 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
118 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
119 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
120 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
121 * -- LVM_GETINSERTMARKRECT
122 * -- LVM_GETNUMBEROFWORKAREAS
123 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
124 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
125 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
126 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
127 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
128 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
129 * -- LVM_GETVIEW, LVM_SETVIEW
130 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
131 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
132 * -- LVM_INSERTGROUPSORTED
133 * -- LVM_INSERTMARKHITTEST
134 * -- LVM_ISGROUPVIEWENABLED
135 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
137 * -- LVM_MOVEITEMTOGROUP
139 * -- LVM_SETTILEWIDTH
144 * -- ListView_GetCheckSate, ListView_SetCheckState
145 * -- ListView_GetHoverTime, ListView_SetHoverTime
146 * -- ListView_GetISearchString
147 * -- ListView_GetNumberOfWorkAreas
148 * -- ListView_GetOrigin
149 * -- ListView_GetTextBkColor
150 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
151 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
152 * -- ListView_SortItemsEx
157 * Known differences in message stream from native control (not known if
158 * these differences cause problems):
159 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
160 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
161 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
162 * processing for "USEDOUBLECLICKTIME".
166 #include "wine/port.h"
181 #include "commctrl.h"
182 #include "comctl32.h"
185 #include "wine/debug.h"
186 #include "wine/unicode.h"
188 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
190 /* make sure you set this to 0 for production use! */
191 #define DEBUG_RANGES 1
193 typedef struct tagCOLUMN_INFO
195 RECT rcHeader
; /* tracks the header's rectangle */
196 int fmt
; /* same as LVCOLUMN.fmt */
199 typedef struct tagITEMHDR
203 } ITEMHDR
, *LPITEMHDR
;
205 typedef struct tagSUBITEM_INFO
211 typedef struct tagITEM_INFO
219 typedef struct tagRANGE
225 typedef struct tagRANGES
230 typedef struct tagITERATOR
239 typedef struct tagDELAYED_ITEM_EDIT
245 typedef struct tagLISTVIEW_INFO
252 HIMAGELIST himlNormal
;
253 HIMAGELIST himlSmall
;
254 HIMAGELIST himlState
;
258 POINT ptClickPos
; /* point where the user clicked */
259 BOOL bNoItemMetrics
; /* flags if item metrics are not yet computed */
262 RANGES selectionRanges
;
267 RECT rcList
; /* This rectangle is really the window
268 * client rectangle possibly reduced by the
269 * horizontal scroll bar and/or header - see
270 * LISTVIEW_UpdateSize. This rectangle offset
271 * by the LISTVIEW_GetOrigin value is in
272 * client coordinates */
281 INT ntmHeight
; /* Some cached metrics of the font used */
282 INT ntmMaxCharWidth
; /* by the listview to draw items */
284 BOOL bRedraw
; /* Turns on/off repaints & invalidations */
285 BOOL bAutoarrange
; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
287 BOOL bDoChangeNotify
; /* send change notification messages? */
290 DWORD dwStyle
; /* the cached window GWL_STYLE */
291 DWORD dwLvExStyle
; /* extended listview style */
292 INT nItemCount
; /* the number of items in the list */
293 HDPA hdpaItems
; /* array ITEM_INFO pointers */
294 HDPA hdpaPosX
; /* maintains the (X, Y) coordinates of the */
295 HDPA hdpaPosY
; /* items in LVS_ICON, and LVS_SMALLICON modes */
296 HDPA hdpaColumns
; /* array of COLUMN_INFO pointers */
297 POINT currIconPos
; /* this is the position next icon will be placed */
298 PFNLVCOMPARE pfnCompare
;
306 DWORD cditemmode
; /* Keep the custom draw flags for an item/row */
308 DWORD lastKeyPressTimestamp
;
310 INT nSearchParamLength
;
311 WCHAR szSearchParam
[ MAX_PATH
];
313 INT nMeasureItemHeight
;
314 INT xTrackLine
; /* The x coefficient of the track line or -1 if none */
315 DELAYED_ITEM_EDIT itemEdit
; /* Pointer to this structure will be the timer ID */
321 /* How many we debug buffer to allocate */
322 #define DEBUG_BUFFERS 20
323 /* The size of a single debug bbuffer */
324 #define DEBUG_BUFFER_SIZE 256
326 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
327 #define SB_INTERNAL -1
329 /* maximum size of a label */
330 #define DISP_TEXT_SIZE 512
332 /* padding for items in list and small icon display modes */
333 #define WIDTH_PADDING 12
335 /* padding for items in list, report and small icon display modes */
336 #define HEIGHT_PADDING 1
338 /* offset of items in report display mode */
339 #define REPORT_MARGINX 2
341 /* padding for icon in large icon display mode
342 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
343 * that HITTEST will see.
344 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
345 * ICON_TOP_PADDING - sum of the two above.
346 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
347 * LABEL_HOR_PADDING - between text and sides of box
348 * LABEL_VERT_PADDING - between bottom of text and end of box
350 * ICON_LR_PADDING - additional width above icon size.
351 * ICON_LR_HALF - half of the above value
353 #define ICON_TOP_PADDING_NOTHITABLE 2
354 #define ICON_TOP_PADDING_HITABLE 2
355 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
356 #define ICON_BOTTOM_PADDING 4
357 #define LABEL_HOR_PADDING 5
358 #define LABEL_VERT_PADDING 7
359 #define ICON_LR_PADDING 16
360 #define ICON_LR_HALF (ICON_LR_PADDING/2)
362 /* default label width for items in list and small icon display modes */
363 #define DEFAULT_LABEL_WIDTH 40
365 /* default column width for items in list display mode */
366 #define DEFAULT_COLUMN_WIDTH 128
368 /* Size of "line" scroll for V & H scrolls */
369 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
371 /* Padding between image and label */
372 #define IMAGE_PADDING 2
374 /* Padding behind the label */
375 #define TRAILING_LABEL_PADDING 12
376 #define TRAILING_HEADER_PADDING 11
378 /* Border for the icon caption */
379 #define CAPTION_BORDER 2
381 /* Standard DrawText flags */
382 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
383 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
384 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
386 /* Image index from state */
387 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
389 /* The time in milliseconds to reset the search in the list */
390 #define KEY_DELAY 450
392 /* Dump the LISTVIEW_INFO structure to the debug channel */
393 #define LISTVIEW_DUMP(iP) do { \
394 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
395 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
396 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
397 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
398 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
399 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
400 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
401 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
402 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
403 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
406 static const WCHAR themeClass
[] = {'L','i','s','t','V','i','e','w',0};
409 * forward declarations
411 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
412 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*, INT
, LPRECT
);
413 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*, INT
, LPPOINT
);
414 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*, INT
, LPPOINT
);
415 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*, INT
, LPRECT
);
416 static INT
LISTVIEW_GetLabelWidth(const LISTVIEW_INFO
*, INT
);
417 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*, LPPOINT
);
418 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*, LPRECT
);
419 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*, INT
);
420 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*, LVITEMW
*, BOOL
);
421 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO
*);
422 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*, INT
);
423 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
424 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*, INT
, BOOL
);
425 static LRESULT
LISTVIEW_Command(const LISTVIEW_INFO
*, WPARAM
, LPARAM
);
426 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*, PFNLVCOMPARE
, LPARAM
);
427 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
428 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
);
429 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*, INT
, UINT
);
430 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, const LVITEMW
*);
431 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
432 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
433 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*);
434 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
435 static HWND
CreateEditLabelT(LISTVIEW_INFO
*, LPCWSTR
, DWORD
, INT
, INT
, INT
, INT
, BOOL
);
436 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*, INT
, HIMAGELIST
);
437 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*, LPLVHITTESTINFO
, BOOL
, BOOL
);
439 /******** Text handling functions *************************************/
441 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
442 * text string. The string may be ANSI or Unicode, in which case
443 * the boolean isW tells us the type of the string.
445 * The name of the function tell what type of strings it expects:
446 * W: Unicode, T: ANSI/Unicode - function of isW
449 static inline BOOL
is_textW(LPCWSTR text
)
451 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
454 static inline BOOL
is_textT(LPCWSTR text
, BOOL isW
)
456 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
457 return is_textW(text
);
460 static inline int textlenT(LPCWSTR text
, BOOL isW
)
462 return !is_textT(text
, isW
) ? 0 :
463 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
466 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
469 if (isSrcW
) lstrcpynW(dest
, src
, max
);
470 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
472 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
473 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
476 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
478 LPWSTR wstr
= (LPWSTR
)text
;
480 if (!isW
&& is_textT(text
, isW
))
482 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
483 wstr
= Alloc(len
* sizeof(WCHAR
));
484 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
486 TRACE(" wstr=%s\n", text
== LPSTR_TEXTCALLBACKW
? "(callback)" : debugstr_w(wstr
));
490 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
492 if (!isW
&& is_textT(wstr
, isW
)) Free (wstr
);
496 * dest is a pointer to a Unicode string
497 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
499 static BOOL
textsetptrT(LPWSTR
*dest
, LPCWSTR src
, BOOL isW
)
503 if (src
== LPSTR_TEXTCALLBACKW
)
505 if (is_textW(*dest
)) Free(*dest
);
506 *dest
= LPSTR_TEXTCALLBACKW
;
510 LPWSTR pszText
= textdupTtoW(src
, isW
);
511 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
512 bResult
= Str_SetPtrW(dest
, pszText
);
513 textfreeT(pszText
, isW
);
519 * compares a Unicode to a Unicode/ANSI text string
521 static inline int textcmpWT(LPCWSTR aw
, LPCWSTR bt
, BOOL isW
)
523 if (!aw
) return bt
? -1 : 0;
524 if (!bt
) return aw
? 1 : 0;
525 if (aw
== LPSTR_TEXTCALLBACKW
)
526 return bt
== LPSTR_TEXTCALLBACKW
? 0 : -1;
527 if (bt
!= LPSTR_TEXTCALLBACKW
)
529 LPWSTR bw
= textdupTtoW(bt
, isW
);
530 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
538 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
542 n
= min(min(n
, lstrlenW(s1
)), lstrlenW(s2
));
543 res
= CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
);
544 return res
? res
- sizeof(WCHAR
) : res
;
547 /******** Debugging functions *****************************************/
549 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
551 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
552 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
555 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
557 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
558 n
= min(textlenT(text
, isW
), n
);
559 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
562 static char* debug_getbuf(void)
564 static int index
= 0;
565 static char buffers
[DEBUG_BUFFERS
][DEBUG_BUFFER_SIZE
];
566 return buffers
[index
++ % DEBUG_BUFFERS
];
569 static inline const char* debugrange(const RANGE
*lprng
)
571 if (!lprng
) return "(null)";
572 return wine_dbg_sprintf("[%d, %d]", lprng
->lower
, lprng
->upper
);
575 static const char* debugscrollinfo(const SCROLLINFO
*pScrollInfo
)
577 char* buf
= debug_getbuf(), *text
= buf
;
578 int len
, size
= DEBUG_BUFFER_SIZE
;
580 if (pScrollInfo
== NULL
) return "(null)";
581 len
= snprintf(buf
, size
, "{cbSize=%d, ", pScrollInfo
->cbSize
);
582 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
583 if (pScrollInfo
->fMask
& SIF_RANGE
)
584 len
= snprintf(buf
, size
, "nMin=%d, nMax=%d, ", pScrollInfo
->nMin
, pScrollInfo
->nMax
);
586 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
587 if (pScrollInfo
->fMask
& SIF_PAGE
)
588 len
= snprintf(buf
, size
, "nPage=%u, ", pScrollInfo
->nPage
);
590 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
591 if (pScrollInfo
->fMask
& SIF_POS
)
592 len
= snprintf(buf
, size
, "nPos=%d, ", pScrollInfo
->nPos
);
594 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
595 if (pScrollInfo
->fMask
& SIF_TRACKPOS
)
596 len
= snprintf(buf
, size
, "nTrackPos=%d, ", pScrollInfo
->nTrackPos
);
598 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
601 buf
= text
+ strlen(text
);
603 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
607 static const char* debugnmlistview(const NMLISTVIEW
*plvnm
)
609 if (!plvnm
) return "(null)";
610 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
611 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
612 plvnm
->iItem
, plvnm
->iSubItem
, plvnm
->uNewState
, plvnm
->uOldState
,
613 plvnm
->uChanged
, wine_dbgstr_point(&plvnm
->ptAction
), plvnm
->lParam
);
616 static const char* debuglvitem_t(const LVITEMW
*lpLVItem
, BOOL isW
)
618 char* buf
= debug_getbuf(), *text
= buf
;
619 int len
, size
= DEBUG_BUFFER_SIZE
;
621 if (lpLVItem
== NULL
) return "(null)";
622 len
= snprintf(buf
, size
, "{iItem=%d, iSubItem=%d, ", lpLVItem
->iItem
, lpLVItem
->iSubItem
);
623 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
624 if (lpLVItem
->mask
& LVIF_STATE
)
625 len
= snprintf(buf
, size
, "state=%x, stateMask=%x, ", lpLVItem
->state
, lpLVItem
->stateMask
);
627 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
628 if (lpLVItem
->mask
& LVIF_TEXT
)
629 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem
->pszText
, isW
, 80), lpLVItem
->cchTextMax
);
631 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
632 if (lpLVItem
->mask
& LVIF_IMAGE
)
633 len
= snprintf(buf
, size
, "iImage=%d, ", lpLVItem
->iImage
);
635 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
636 if (lpLVItem
->mask
& LVIF_PARAM
)
637 len
= snprintf(buf
, size
, "lParam=%lx, ", lpLVItem
->lParam
);
639 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
640 if (lpLVItem
->mask
& LVIF_INDENT
)
641 len
= snprintf(buf
, size
, "iIndent=%d, ", lpLVItem
->iIndent
);
643 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
646 buf
= text
+ strlen(text
);
648 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
652 static const char* debuglvcolumn_t(const LVCOLUMNW
*lpColumn
, BOOL isW
)
654 char* buf
= debug_getbuf(), *text
= buf
;
655 int len
, size
= DEBUG_BUFFER_SIZE
;
657 if (lpColumn
== NULL
) return "(null)";
658 len
= snprintf(buf
, size
, "{");
659 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
660 if (lpColumn
->mask
& LVCF_SUBITEM
)
661 len
= snprintf(buf
, size
, "iSubItem=%d, ", lpColumn
->iSubItem
);
663 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
664 if (lpColumn
->mask
& LVCF_FMT
)
665 len
= snprintf(buf
, size
, "fmt=%x, ", lpColumn
->fmt
);
667 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
668 if (lpColumn
->mask
& LVCF_WIDTH
)
669 len
= snprintf(buf
, size
, "cx=%d, ", lpColumn
->cx
);
671 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
672 if (lpColumn
->mask
& LVCF_TEXT
)
673 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn
->pszText
, isW
, 80), lpColumn
->cchTextMax
);
675 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
676 if (lpColumn
->mask
& LVCF_IMAGE
)
677 len
= snprintf(buf
, size
, "iImage=%d, ", lpColumn
->iImage
);
679 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
680 if (lpColumn
->mask
& LVCF_ORDER
)
681 len
= snprintf(buf
, size
, "iOrder=%d, ", lpColumn
->iOrder
);
683 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
686 buf
= text
+ strlen(text
);
688 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
692 static const char* debuglvhittestinfo(const LVHITTESTINFO
*lpht
)
694 if (!lpht
) return "(null)";
696 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
697 wine_dbgstr_point(&lpht
->pt
), lpht
->flags
, lpht
->iItem
, lpht
->iSubItem
);
700 /* Return the corresponding text for a given scroll value */
701 static inline LPCSTR
debugscrollcode(int nScrollCode
)
705 case SB_LINELEFT
: return "SB_LINELEFT";
706 case SB_LINERIGHT
: return "SB_LINERIGHT";
707 case SB_PAGELEFT
: return "SB_PAGELEFT";
708 case SB_PAGERIGHT
: return "SB_PAGERIGHT";
709 case SB_THUMBPOSITION
: return "SB_THUMBPOSITION";
710 case SB_THUMBTRACK
: return "SB_THUMBTRACK";
711 case SB_ENDSCROLL
: return "SB_ENDSCROLL";
712 case SB_INTERNAL
: return "SB_INTERNAL";
713 default: return "unknown";
718 /******** Notification functions i************************************/
720 static LRESULT
notify_forward_header(const LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
722 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
723 (WPARAM
)lpnmh
->hdr
.idFrom
, (LPARAM
)lpnmh
);
726 static LRESULT
notify_hdr(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
730 TRACE("(code=%d)\n", code
);
732 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
733 pnmh
->idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
735 result
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, pnmh
->idFrom
, (LPARAM
)pnmh
);
737 TRACE(" <= %ld\n", result
);
742 static inline BOOL
notify(const LISTVIEW_INFO
*infoPtr
, INT code
)
745 HWND hwnd
= infoPtr
->hwndSelf
;
746 notify_hdr(infoPtr
, code
, &nmh
);
747 return IsWindow(hwnd
);
750 static inline void notify_itemactivate(const LISTVIEW_INFO
*infoPtr
, const LVHITTESTINFO
*htInfo
)
761 item
.mask
= LVIF_PARAM
|LVIF_STATE
;
762 item
.iItem
= htInfo
->iItem
;
764 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) {
765 nmia
.lParam
= item
.lParam
;
766 nmia
.uOldState
= item
.state
;
767 nmia
.uNewState
= item
.state
| LVIS_ACTIVATING
;
768 nmia
.uChanged
= LVIF_STATE
;
771 nmia
.iItem
= htInfo
->iItem
;
772 nmia
.iSubItem
= htInfo
->iSubItem
;
773 nmia
.ptAction
= htInfo
->pt
;
775 if (GetKeyState(VK_SHIFT
) & 0x8000) nmia
.uKeyFlags
|= LVKF_SHIFT
;
776 if (GetKeyState(VK_CONTROL
) & 0x8000) nmia
.uKeyFlags
|= LVKF_CONTROL
;
777 if (GetKeyState(VK_MENU
) & 0x8000) nmia
.uKeyFlags
|= LVKF_ALT
;
779 notify_hdr(infoPtr
, LVN_ITEMACTIVATE
, (LPNMHDR
)&nmia
);
782 static inline LRESULT
notify_listview(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
784 TRACE("(code=%d, plvnm=%s)\n", code
, debugnmlistview(plvnm
));
785 return notify_hdr(infoPtr
, code
, (LPNMHDR
)plvnm
);
788 static BOOL
notify_click(const LISTVIEW_INFO
*infoPtr
, INT code
, const LVHITTESTINFO
*lvht
)
792 HWND hwnd
= infoPtr
->hwndSelf
;
794 TRACE("code=%d, lvht=%s\n", code
, debuglvhittestinfo(lvht
));
795 ZeroMemory(&nmlv
, sizeof(nmlv
));
796 nmlv
.iItem
= lvht
->iItem
;
797 nmlv
.iSubItem
= lvht
->iSubItem
;
798 nmlv
.ptAction
= lvht
->pt
;
799 item
.mask
= LVIF_PARAM
;
800 item
.iItem
= lvht
->iItem
;
802 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
803 notify_listview(infoPtr
, code
, &nmlv
);
804 return IsWindow(hwnd
);
807 static BOOL
notify_deleteitem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
811 HWND hwnd
= infoPtr
->hwndSelf
;
813 ZeroMemory(&nmlv
, sizeof (NMLISTVIEW
));
815 item
.mask
= LVIF_PARAM
;
818 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
819 notify_listview(infoPtr
, LVN_DELETEITEM
, &nmlv
);
820 return IsWindow(hwnd
);
823 static int get_ansi_notification(UINT unicodeNotificationCode
)
825 switch (unicodeNotificationCode
)
827 case LVN_BEGINLABELEDITW
: return LVN_BEGINLABELEDITA
;
828 case LVN_ENDLABELEDITW
: return LVN_ENDLABELEDITA
;
829 case LVN_GETDISPINFOW
: return LVN_GETDISPINFOA
;
830 case LVN_SETDISPINFOW
: return LVN_SETDISPINFOA
;
831 case LVN_ODFINDITEMW
: return LVN_ODFINDITEMA
;
832 case LVN_GETINFOTIPW
: return LVN_GETINFOTIPA
;
834 ERR("unknown notification %x\n", unicodeNotificationCode
);
840 Send notification. depends on dispinfoW having same
841 structure as dispinfoA.
842 infoPtr : listview struct
843 notificationCode : *Unicode* notification code
844 pdi : dispinfo structure (can be unicode or ansi)
845 isW : TRUE if dispinfo is Unicode
847 static BOOL
notify_dispinfoT(const LISTVIEW_INFO
*infoPtr
, UINT notificationCode
, LPNMLVDISPINFOW pdi
, BOOL isW
)
849 BOOL bResult
= FALSE
;
850 BOOL convertToAnsi
= FALSE
, convertToUnicode
= FALSE
;
851 INT cchTempBufMax
= 0, savCchTextMax
= 0;
853 LPWSTR pszTempBuf
= NULL
, savPszText
= NULL
;
855 if ((pdi
->item
.mask
& LVIF_TEXT
) && is_textT(pdi
->item
.pszText
, isW
))
857 convertToAnsi
= (isW
&& infoPtr
->notifyFormat
== NFR_ANSI
);
858 convertToUnicode
= (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
);
861 if (convertToAnsi
|| convertToUnicode
)
863 if (notificationCode
!= LVN_GETDISPINFOW
)
865 cchTempBufMax
= convertToUnicode
?
866 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
867 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
871 cchTempBufMax
= pdi
->item
.cchTextMax
;
872 *pdi
->item
.pszText
= 0; /* make sure we don't process garbage */
875 pszTempBuf
= Alloc( (convertToUnicode
? sizeof(WCHAR
) : sizeof(CHAR
)) * cchTempBufMax
);
876 if (!pszTempBuf
) return FALSE
;
878 if (convertToUnicode
)
879 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
880 pszTempBuf
, cchTempBufMax
);
882 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) pszTempBuf
,
883 cchTempBufMax
, NULL
, NULL
);
885 savCchTextMax
= pdi
->item
.cchTextMax
;
886 savPszText
= pdi
->item
.pszText
;
887 pdi
->item
.pszText
= pszTempBuf
;
888 pdi
->item
.cchTextMax
= cchTempBufMax
;
891 if (infoPtr
->notifyFormat
== NFR_ANSI
)
892 realNotifCode
= get_ansi_notification(notificationCode
);
894 realNotifCode
= notificationCode
;
895 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi
->item
, infoPtr
->notifyFormat
!= NFR_ANSI
));
896 bResult
= notify_hdr(infoPtr
, realNotifCode
, &pdi
->hdr
);
898 if (convertToUnicode
|| convertToAnsi
)
900 if (convertToUnicode
) /* note : pointer can be changed by app ! */
901 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) savPszText
,
902 savCchTextMax
, NULL
, NULL
);
904 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
905 savPszText
, savCchTextMax
);
906 pdi
->item
.pszText
= savPszText
; /* restores our buffer */
907 pdi
->item
.cchTextMax
= savCchTextMax
;
913 static void customdraw_fill(NMLVCUSTOMDRAW
*lpnmlvcd
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
,
914 const RECT
*rcBounds
, const LVITEMW
*lplvItem
)
916 ZeroMemory(lpnmlvcd
, sizeof(NMLVCUSTOMDRAW
));
917 lpnmlvcd
->nmcd
.hdc
= hdc
;
918 lpnmlvcd
->nmcd
.rc
= *rcBounds
;
919 lpnmlvcd
->clrTextBk
= infoPtr
->clrTextBk
;
920 lpnmlvcd
->clrText
= infoPtr
->clrText
;
921 if (!lplvItem
) return;
922 lpnmlvcd
->nmcd
.dwItemSpec
= lplvItem
->iItem
+ 1;
923 lpnmlvcd
->iSubItem
= lplvItem
->iSubItem
;
924 if (lplvItem
->state
& LVIS_SELECTED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_SELECTED
;
925 if (lplvItem
->state
& LVIS_FOCUSED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_FOCUS
;
926 if (lplvItem
->iItem
== infoPtr
->nHotItem
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_HOT
;
927 lpnmlvcd
->nmcd
.lItemlParam
= lplvItem
->lParam
;
930 static inline DWORD
notify_customdraw (const LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, NMLVCUSTOMDRAW
*lpnmlvcd
)
932 BOOL isForItem
= (lpnmlvcd
->nmcd
.dwItemSpec
!= 0);
935 lpnmlvcd
->nmcd
.dwDrawStage
= dwDrawStage
;
936 if (isForItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_ITEM
;
937 if (lpnmlvcd
->iSubItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_SUBITEM
;
938 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
--;
939 result
= notify_hdr(infoPtr
, NM_CUSTOMDRAW
, &lpnmlvcd
->nmcd
.hdr
);
940 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
++;
944 static void prepaint_setup (const LISTVIEW_INFO
*infoPtr
, HDC hdc
, NMLVCUSTOMDRAW
*lpnmlvcd
, BOOL SubItem
)
946 if (lpnmlvcd
->clrTextBk
== CLR_DEFAULT
)
947 lpnmlvcd
->clrTextBk
= comctl32_color
.clrWindow
;
948 if (lpnmlvcd
->clrText
== CLR_DEFAULT
)
949 lpnmlvcd
->clrText
= comctl32_color
.clrWindowText
;
951 /* apparently, for selected items, we have to override the returned values */
954 if (lpnmlvcd
->nmcd
.uItemState
& CDIS_SELECTED
)
958 lpnmlvcd
->clrTextBk
= comctl32_color
.clrHighlight
;
959 lpnmlvcd
->clrText
= comctl32_color
.clrHighlightText
;
961 else if (infoPtr
->dwStyle
& LVS_SHOWSELALWAYS
)
963 lpnmlvcd
->clrTextBk
= comctl32_color
.clr3dFace
;
964 lpnmlvcd
->clrText
= comctl32_color
.clrBtnText
;
969 /* Set the text attributes */
970 if (lpnmlvcd
->clrTextBk
!= CLR_NONE
)
972 SetBkMode(hdc
, OPAQUE
);
973 SetBkColor(hdc
,lpnmlvcd
->clrTextBk
);
976 SetBkMode(hdc
, TRANSPARENT
);
977 SetTextColor(hdc
, lpnmlvcd
->clrText
);
980 static inline DWORD
notify_postpaint (const LISTVIEW_INFO
*infoPtr
, NMLVCUSTOMDRAW
*lpnmlvcd
)
982 return notify_customdraw(infoPtr
, CDDS_POSTPAINT
, lpnmlvcd
);
985 /******** Item iterator functions **********************************/
987 static RANGES
ranges_create(int count
);
988 static void ranges_destroy(RANGES ranges
);
989 static BOOL
ranges_add(RANGES ranges
, RANGE range
);
990 static BOOL
ranges_del(RANGES ranges
, RANGE range
);
991 static void ranges_dump(RANGES ranges
);
993 static inline BOOL
ranges_additem(RANGES ranges
, INT nItem
)
995 RANGE range
= { nItem
, nItem
+ 1 };
997 return ranges_add(ranges
, range
);
1000 static inline BOOL
ranges_delitem(RANGES ranges
, INT nItem
)
1002 RANGE range
= { nItem
, nItem
+ 1 };
1004 return ranges_del(ranges
, range
);
1008 * ITERATOR DOCUMENTATION
1010 * The iterator functions allow for easy, and convenient iteration
1011 * over items of interest in the list. Typically, you create a
1012 * iterator, use it, and destroy it, as such:
1015 * iterator_xxxitems(&i, ...);
1016 * while (iterator_{prev,next}(&i)
1018 * //code which uses i.nItem
1020 * iterator_destroy(&i);
1022 * where xxx is either: framed, or visible.
1023 * Note that it is important that the code destroys the iterator
1024 * after it's done with it, as the creation of the iterator may
1025 * allocate memory, which thus needs to be freed.
1027 * You can iterate both forwards, and backwards through the list,
1028 * by using iterator_next or iterator_prev respectively.
1030 * Lower numbered items are draw on top of higher number items in
1031 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1032 * items may overlap). So, to test items, you should use
1034 * which lists the items top to bottom (in Z-order).
1035 * For drawing items, you should use
1037 * which lists the items bottom to top (in Z-order).
1038 * If you keep iterating over the items after the end-of-items
1039 * marker (-1) is returned, the iterator will start from the
1040 * beginning. Typically, you don't need to test for -1,
1041 * because iterator_{next,prev} will return TRUE if more items
1042 * are to be iterated over, or FALSE otherwise.
1044 * Note: the iterator is defined to be bidirectional. That is,
1045 * any number of prev followed by any number of next, or
1046 * five versa, should leave the iterator at the same item:
1047 * prev * n, next * n = next * n, prev * n
1049 * The iterator has a notion of an out-of-order, special item,
1050 * which sits at the start of the list. This is used in
1051 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1052 * which needs to be first, as it may overlap other items.
1054 * The code is a bit messy because we have:
1055 * - a special item to deal with
1056 * - simple range, or composite range
1058 * If you find bugs, or want to add features, please make sure you
1059 * always check/modify *both* iterator_prev, and iterator_next.
1063 * This function iterates through the items in increasing order,
1064 * but prefixed by the special item, then -1. That is:
1065 * special, 1, 2, 3, ..., n, -1.
1066 * Each item is listed only once.
1068 static inline BOOL
iterator_next(ITERATOR
* i
)
1072 i
->nItem
= i
->nSpecial
;
1073 if (i
->nItem
!= -1) return TRUE
;
1075 if (i
->nItem
== i
->nSpecial
)
1077 if (i
->ranges
) i
->index
= 0;
1083 if (i
->nItem
== i
->nSpecial
) i
->nItem
++;
1084 if (i
->nItem
< i
->range
.upper
) return TRUE
;
1089 if (i
->index
< DPA_GetPtrCount(i
->ranges
->hdpa
))
1090 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, i
->index
++);
1093 else if (i
->nItem
>= i
->range
.upper
) goto end
;
1095 i
->nItem
= i
->range
.lower
;
1096 if (i
->nItem
>= 0) goto testitem
;
1103 * This function iterates through the items in decreasing order,
1104 * followed by the special item, then -1. That is:
1105 * n, n-1, ..., 3, 2, 1, special, -1.
1106 * Each item is listed only once.
1108 static inline BOOL
iterator_prev(ITERATOR
* i
)
1115 if (i
->ranges
) i
->index
= DPA_GetPtrCount(i
->ranges
->hdpa
);
1118 if (i
->nItem
== i
->nSpecial
)
1126 if (i
->nItem
== i
->nSpecial
) i
->nItem
--;
1127 if (i
->nItem
>= i
->range
.lower
) return TRUE
;
1133 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, --i
->index
);
1136 else if (!start
&& i
->nItem
< i
->range
.lower
) goto end
;
1138 i
->nItem
= i
->range
.upper
;
1139 if (i
->nItem
> 0) goto testitem
;
1141 return (i
->nItem
= i
->nSpecial
) != -1;
1144 static RANGE
iterator_range(const ITERATOR
*i
)
1148 if (!i
->ranges
) return i
->range
;
1150 if (DPA_GetPtrCount(i
->ranges
->hdpa
) > 0)
1152 range
.lower
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, 0)).lower
;
1153 range
.upper
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, DPA_GetPtrCount(i
->ranges
->hdpa
) - 1)).upper
;
1155 else range
.lower
= range
.upper
= 0;
1161 * Releases resources associated with this ierator.
1163 static inline void iterator_destroy(const ITERATOR
*i
)
1165 ranges_destroy(i
->ranges
);
1169 * Create an empty iterator.
1171 static inline BOOL
iterator_empty(ITERATOR
* i
)
1173 ZeroMemory(i
, sizeof(*i
));
1174 i
->nItem
= i
->nSpecial
= i
->range
.lower
= i
->range
.upper
= -1;
1179 * Create an iterator over a range.
1181 static inline BOOL
iterator_rangeitems(ITERATOR
* i
, RANGE range
)
1189 * Create an iterator over a bunch of ranges.
1190 * Please note that the iterator will take ownership of the ranges,
1191 * and will free them upon destruction.
1193 static inline BOOL
iterator_rangesitems(ITERATOR
* i
, RANGES ranges
)
1201 * Creates an iterator over the items which intersect lprc.
1203 static BOOL
iterator_frameditems(ITERATOR
* i
, const LISTVIEW_INFO
* infoPtr
, const RECT
*lprc
)
1205 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1206 RECT frame
= *lprc
, rcItem
, rcTemp
;
1209 /* in case we fail, we want to return an empty iterator */
1210 if (!iterator_empty(i
)) return FALSE
;
1212 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1214 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc
));
1215 OffsetRect(&frame
, -Origin
.x
, -Origin
.y
);
1217 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
1221 if (uView
== LVS_ICON
&& infoPtr
->nFocusedItem
!= -1)
1223 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcItem
);
1224 if (IntersectRect(&rcTemp
, &rcItem
, lprc
))
1225 i
->nSpecial
= infoPtr
->nFocusedItem
;
1227 if (!(iterator_rangesitems(i
, ranges_create(50)))) return FALSE
;
1228 /* to do better here, we need to have PosX, and PosY sorted */
1229 TRACE("building icon ranges:\n");
1230 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
1232 rcItem
.left
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1233 rcItem
.top
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1234 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1235 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1236 if (IntersectRect(&rcTemp
, &rcItem
, &frame
))
1237 ranges_additem(i
->ranges
, nItem
);
1241 else if (uView
== LVS_REPORT
)
1245 if (frame
.left
>= infoPtr
->nItemWidth
) return TRUE
;
1246 if (frame
.top
>= infoPtr
->nItemHeight
* infoPtr
->nItemCount
) return TRUE
;
1248 range
.lower
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1249 range
.upper
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, infoPtr
->nItemCount
- 1) + 1;
1250 if (range
.upper
<= range
.lower
) return TRUE
;
1251 if (!iterator_rangeitems(i
, range
)) return FALSE
;
1252 TRACE(" report=%s\n", debugrange(&i
->range
));
1256 INT nPerCol
= max((infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
, 1);
1257 INT nFirstRow
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1258 INT nLastRow
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, nPerCol
- 1);
1259 INT nFirstCol
= max(frame
.left
/ infoPtr
->nItemWidth
, 0);
1260 INT nLastCol
= min((frame
.right
- 1) / infoPtr
->nItemWidth
, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1261 INT lower
= nFirstCol
* nPerCol
+ nFirstRow
;
1265 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1266 nPerCol
, nFirstRow
, nLastRow
, nFirstCol
, nLastCol
, lower
);
1268 if (nLastCol
< nFirstCol
|| nLastRow
< nFirstRow
) return TRUE
;
1270 if (!(iterator_rangesitems(i
, ranges_create(nLastCol
- nFirstCol
+ 1)))) return FALSE
;
1271 TRACE("building list ranges:\n");
1272 for (nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++)
1274 item_range
.lower
= nCol
* nPerCol
+ nFirstRow
;
1275 if(item_range
.lower
>= infoPtr
->nItemCount
) break;
1276 item_range
.upper
= min(nCol
* nPerCol
+ nLastRow
+ 1, infoPtr
->nItemCount
);
1277 TRACE(" list=%s\n", debugrange(&item_range
));
1278 ranges_add(i
->ranges
, item_range
);
1286 * Creates an iterator over the items which intersect the visible region of hdc.
1288 static BOOL
iterator_visibleitems(ITERATOR
*i
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1290 POINT Origin
, Position
;
1291 RECT rcItem
, rcClip
;
1294 rgntype
= GetClipBox(hdc
, &rcClip
);
1295 if (rgntype
== NULLREGION
) return iterator_empty(i
);
1296 if (!iterator_frameditems(i
, infoPtr
, &rcClip
)) return FALSE
;
1297 if (rgntype
== SIMPLEREGION
) return TRUE
;
1299 /* first deal with the special item */
1300 if (i
->nSpecial
!= -1)
1302 LISTVIEW_GetItemBox(infoPtr
, i
->nSpecial
, &rcItem
);
1303 if (!RectVisible(hdc
, &rcItem
)) i
->nSpecial
= -1;
1306 /* if we can't deal with the region, we'll just go with the simple range */
1307 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1308 TRACE("building visible range:\n");
1309 if (!i
->ranges
&& i
->range
.lower
< i
->range
.upper
)
1311 if (!(i
->ranges
= ranges_create(50))) return TRUE
;
1312 if (!ranges_add(i
->ranges
, i
->range
))
1314 ranges_destroy(i
->ranges
);
1320 /* now delete the invisible items from the list */
1321 while(iterator_next(i
))
1323 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
1324 rcItem
.left
= Position
.x
+ Origin
.x
;
1325 rcItem
.top
= Position
.y
+ Origin
.y
;
1326 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1327 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1328 if (!RectVisible(hdc
, &rcItem
))
1329 ranges_delitem(i
->ranges
, i
->nItem
);
1331 /* the iterator should restart on the next iterator_next */
1337 /******** Misc helper functions ************************************/
1339 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
1340 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
1342 if (isW
) return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
1343 else return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
1346 static inline BOOL
is_autoarrange(const LISTVIEW_INFO
*infoPtr
)
1348 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1350 return ((infoPtr
->dwStyle
& LVS_AUTOARRANGE
) || infoPtr
->bAutoarrange
) &&
1351 (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
);
1354 static void toggle_checkbox_state(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1356 DWORD state
= STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_STATEIMAGEMASK
));
1357 if(state
== 1 || state
== 2)
1361 lvitem
.state
= INDEXTOSTATEIMAGEMASK(state
);
1362 lvitem
.stateMask
= LVIS_STATEIMAGEMASK
;
1363 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvitem
);
1367 /******** Internal API functions ************************************/
1369 static inline COLUMN_INFO
* LISTVIEW_GetColumnInfo(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
)
1371 static COLUMN_INFO mainItem
;
1373 if (nSubItem
== 0 && DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0) return &mainItem
;
1374 assert (nSubItem
>= 0 && nSubItem
< DPA_GetPtrCount(infoPtr
->hdpaColumns
));
1375 return DPA_GetPtr(infoPtr
->hdpaColumns
, nSubItem
);
1378 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
, LPRECT lprc
)
1380 *lprc
= LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->rcHeader
;
1383 static inline BOOL
LISTVIEW_GetItemW(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
1385 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
1388 /* Listview invalidation functions: use _only_ these functions to invalidate */
1390 static inline BOOL
is_redrawing(const LISTVIEW_INFO
*infoPtr
)
1392 return infoPtr
->bRedraw
;
1395 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO
*infoPtr
, const RECT
* rect
)
1397 if(!is_redrawing(infoPtr
)) return;
1398 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect
));
1399 InvalidateRect(infoPtr
->hwndSelf
, rect
, TRUE
);
1402 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
1406 if(!is_redrawing(infoPtr
)) return;
1407 LISTVIEW_GetItemBox(infoPtr
, nItem
, &rcBox
);
1408 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1411 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
)
1413 POINT Origin
, Position
;
1416 if(!is_redrawing(infoPtr
)) return;
1417 assert ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
);
1418 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1419 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
1420 LISTVIEW_GetHeaderRect(infoPtr
, nSubItem
, &rcBox
);
1422 rcBox
.bottom
= infoPtr
->nItemHeight
;
1423 OffsetRect(&rcBox
, Origin
.x
+ Position
.x
, Origin
.y
+ Position
.y
);
1424 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1427 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO
*infoPtr
)
1429 LISTVIEW_InvalidateRect(infoPtr
, NULL
);
1432 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
1436 if(!is_redrawing(infoPtr
)) return;
1437 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
1438 rcCol
.top
= infoPtr
->rcList
.top
;
1439 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
1440 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
1445 * Retrieves the number of items that can fit vertically in the client area.
1448 * [I] infoPtr : valid pointer to the listview structure
1451 * Number of items per row.
1453 static inline INT
LISTVIEW_GetCountPerRow(const LISTVIEW_INFO
*infoPtr
)
1455 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1457 return max(nListWidth
/infoPtr
->nItemWidth
, 1);
1462 * Retrieves the number of items that can fit horizontally in the client
1466 * [I] infoPtr : valid pointer to the listview structure
1469 * Number of items per column.
1471 static inline INT
LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO
*infoPtr
)
1473 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1475 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
1479 /*************************************************************************
1480 * LISTVIEW_ProcessLetterKeys
1482 * Processes keyboard messages generated by pressing the letter keys
1484 * What this does is perform a case insensitive search from the
1485 * current position with the following quirks:
1486 * - If two chars or more are pressed in quick succession we search
1487 * for the corresponding string (e.g. 'abc').
1488 * - If there is a delay we wipe away the current search string and
1489 * restart with just that char.
1490 * - If the user keeps pressing the same character, whether slowly or
1491 * fast, so that the search string is entirely composed of this
1492 * character ('aaaaa' for instance), then we search for first item
1493 * that starting with that character.
1494 * - If the user types the above character in quick succession, then
1495 * we must also search for the corresponding string ('aaaaa'), and
1496 * go to that string if there is a match.
1499 * [I] hwnd : handle to the window
1500 * [I] charCode : the character code, the actual character
1501 * [I] keyData : key data
1509 * - The current implementation has a list of characters it will
1510 * accept and it ignores everything else. In particular it will
1511 * ignore accentuated characters which seems to match what
1512 * Windows does. But I'm not sure it makes sense to follow
1514 * - We don't sound a beep when the search fails.
1518 * TREEVIEW_ProcessLetterKeys
1520 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
1525 WCHAR buffer
[MAX_PATH
];
1526 DWORD lastKeyPressTimestamp
= infoPtr
->lastKeyPressTimestamp
;
1528 /* simple parameter checking */
1529 if (!charCode
|| !keyData
) return 0;
1531 /* only allow the valid WM_CHARs through */
1532 if (!isalnumW(charCode
) &&
1533 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
1534 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
1535 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
1536 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
1537 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
1538 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
1539 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
1540 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
1541 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
1544 /* if there's one item or less, there is no where to go */
1545 if (infoPtr
->nItemCount
<= 1) return 0;
1547 /* update the search parameters */
1548 infoPtr
->lastKeyPressTimestamp
= GetTickCount();
1549 if (infoPtr
->lastKeyPressTimestamp
- lastKeyPressTimestamp
< KEY_DELAY
) {
1550 if (infoPtr
->nSearchParamLength
< MAX_PATH
)
1551 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++]=charCode
;
1552 if (infoPtr
->charCode
!= charCode
)
1553 infoPtr
->charCode
= charCode
= 0;
1555 infoPtr
->charCode
=charCode
;
1556 infoPtr
->szSearchParam
[0]=charCode
;
1557 infoPtr
->nSearchParamLength
=1;
1558 /* Redundant with the 1 char string */
1562 /* and search from the current position */
1564 if (infoPtr
->nFocusedItem
>= 0) {
1565 endidx
=infoPtr
->nFocusedItem
;
1567 /* if looking for single character match,
1568 * then we must always move forward
1570 if (infoPtr
->nSearchParamLength
== 1)
1573 endidx
=infoPtr
->nItemCount
;
1577 /* Let application handle this for virtual listview */
1578 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
1583 ZeroMemory(&lvfi
, sizeof(lvfi
));
1584 lvfi
.flags
= (LVFI_WRAP
| LVFI_PARTIAL
);
1585 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
] = '\0';
1586 lvfi
.psz
= infoPtr
->szSearchParam
;
1590 nItem
= notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
1593 LISTVIEW_KeySelection(infoPtr
, nItem
);
1599 if (idx
== infoPtr
->nItemCount
) {
1600 if (endidx
== infoPtr
->nItemCount
|| endidx
== 0)
1606 item
.mask
= LVIF_TEXT
;
1609 item
.pszText
= buffer
;
1610 item
.cchTextMax
= MAX_PATH
;
1611 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
1613 /* check for a match */
1614 if (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,infoPtr
->nSearchParamLength
) == 0) {
1617 } else if ( (charCode
!= 0) && (nItem
== -1) && (nItem
!= infoPtr
->nFocusedItem
) &&
1618 (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,1) == 0) ) {
1619 /* This would work but we must keep looking for a longer match */
1623 } while (idx
!= endidx
);
1626 LISTVIEW_KeySelection(infoPtr
, nItem
);
1631 /*************************************************************************
1632 * LISTVIEW_UpdateHeaderSize [Internal]
1634 * Function to resize the header control
1637 * [I] hwnd : handle to a window
1638 * [I] nNewScrollPos : scroll pos to set
1643 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
1648 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
1650 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
1651 point
[0].x
= winRect
.left
;
1652 point
[0].y
= winRect
.top
;
1653 point
[1].x
= winRect
.right
;
1654 point
[1].y
= winRect
.bottom
;
1656 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
1657 point
[0].x
= -nNewScrollPos
;
1658 point
[1].x
+= nNewScrollPos
;
1660 SetWindowPos(infoPtr
->hwndHeader
,0,
1661 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
1662 (infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
|
1663 SWP_NOZORDER
| SWP_NOACTIVATE
);
1668 * Update the scrollbars. This functions should be called whenever
1669 * the content, size or view changes.
1672 * [I] infoPtr : valid pointer to the listview structure
1677 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO
*infoPtr
)
1679 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1680 SCROLLINFO horzInfo
, vertInfo
;
1683 if ((infoPtr
->dwStyle
& LVS_NOSCROLL
) || !is_redrawing(infoPtr
)) return;
1685 ZeroMemory(&horzInfo
, sizeof(SCROLLINFO
));
1686 horzInfo
.cbSize
= sizeof(SCROLLINFO
);
1687 horzInfo
.nPage
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1689 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1690 if (uView
== LVS_LIST
)
1692 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
1693 horzInfo
.nMax
= (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
;
1695 /* scroll by at least one column per page */
1696 if(horzInfo
.nPage
< infoPtr
->nItemWidth
)
1697 horzInfo
.nPage
= infoPtr
->nItemWidth
;
1699 horzInfo
.nPage
/= infoPtr
->nItemWidth
;
1701 else if (uView
== LVS_REPORT
)
1703 horzInfo
.nMax
= infoPtr
->nItemWidth
;
1705 else /* LVS_ICON, or LVS_SMALLICON */
1709 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) horzInfo
.nMax
= rcView
.right
- rcView
.left
;
1712 horzInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1713 horzInfo
.nMax
= max(horzInfo
.nMax
- 1, 0);
1714 dx
= GetScrollPos(infoPtr
->hwndSelf
, SB_HORZ
);
1715 dx
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
, TRUE
);
1716 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo
));
1718 /* Setting the horizontal scroll can change the listview size
1719 * (and potentially everything else) so we need to recompute
1720 * everything again for the vertical scroll
1723 ZeroMemory(&vertInfo
, sizeof(SCROLLINFO
));
1724 vertInfo
.cbSize
= sizeof(SCROLLINFO
);
1725 vertInfo
.nPage
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1727 if (uView
== LVS_REPORT
)
1729 vertInfo
.nMax
= infoPtr
->nItemCount
;
1731 /* scroll by at least one page */
1732 if(vertInfo
.nPage
< infoPtr
->nItemHeight
)
1733 vertInfo
.nPage
= infoPtr
->nItemHeight
;
1735 if (infoPtr
->nItemHeight
> 0)
1736 vertInfo
.nPage
/= infoPtr
->nItemHeight
;
1738 else if (uView
!= LVS_LIST
) /* LVS_ICON, or LVS_SMALLICON */
1742 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) vertInfo
.nMax
= rcView
.bottom
- rcView
.top
;
1745 vertInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1746 vertInfo
.nMax
= max(vertInfo
.nMax
- 1, 0);
1747 dy
= GetScrollPos(infoPtr
->hwndSelf
, SB_VERT
);
1748 dy
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &vertInfo
, TRUE
);
1749 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo
));
1751 /* Change of the range may have changed the scroll pos. If so move the content */
1752 if (dx
!= 0 || dy
!= 0)
1755 listRect
= infoPtr
->rcList
;
1756 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &listRect
, &listRect
, 0, 0,
1757 SW_ERASE
| SW_INVALIDATE
);
1760 /* Update the Header Control */
1761 if (uView
== LVS_REPORT
)
1763 horzInfo
.fMask
= SIF_POS
;
1764 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
);
1765 LISTVIEW_UpdateHeaderSize(infoPtr
, horzInfo
.nPos
);
1772 * Shows/hides the focus rectangle.
1775 * [I] infoPtr : valid pointer to the listview structure
1776 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1781 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO
*infoPtr
, BOOL fShow
)
1783 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1786 TRACE("fShow=%d, nItem=%d\n", fShow
, infoPtr
->nFocusedItem
);
1788 if (infoPtr
->nFocusedItem
< 0) return;
1790 /* we need some gymnastics in ICON mode to handle large items */
1791 if ( (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
1795 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcBox
);
1796 if ((rcBox
.bottom
- rcBox
.top
) > infoPtr
->nItemHeight
)
1798 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1803 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) return;
1805 /* for some reason, owner draw should work only in report mode */
1806 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
1811 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
1812 HFONT hOldFont
= SelectObject(hdc
, hFont
);
1814 item
.iItem
= infoPtr
->nFocusedItem
;
1816 item
.mask
= LVIF_PARAM
;
1817 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto done
;
1819 ZeroMemory(&dis
, sizeof(dis
));
1820 dis
.CtlType
= ODT_LISTVIEW
;
1821 dis
.CtlID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1822 dis
.itemID
= item
.iItem
;
1823 dis
.itemAction
= ODA_FOCUS
;
1824 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
1825 dis
.hwndItem
= infoPtr
->hwndSelf
;
1827 LISTVIEW_GetItemBox(infoPtr
, dis
.itemID
, &dis
.rcItem
);
1828 dis
.itemData
= item
.lParam
;
1830 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
1832 SelectObject(hdc
, hOldFont
);
1836 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
1839 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1843 * Invalidates all visible selected items.
1845 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO
*infoPtr
)
1849 iterator_frameditems(&i
, infoPtr
, &infoPtr
->rcList
);
1850 while(iterator_next(&i
))
1852 if (LISTVIEW_GetItemState(infoPtr
, i
.nItem
, LVIS_SELECTED
))
1853 LISTVIEW_InvalidateItem(infoPtr
, i
.nItem
);
1855 iterator_destroy(&i
);
1860 * DESCRIPTION: [INTERNAL]
1861 * Computes an item's (left,top) corner, relative to rcView.
1862 * That is, the position has NOT been made relative to the Origin.
1863 * This is deliberate, to avoid computing the Origin over, and
1864 * over again, when this function is called in a loop. Instead,
1865 * one can factor the computation of the Origin before the loop,
1866 * and offset the value returned by this function, on every iteration.
1869 * [I] infoPtr : valid pointer to the listview structure
1870 * [I] nItem : item number
1871 * [O] lpptOrig : item top, left corner
1876 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
1878 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1880 assert(nItem
>= 0 && nItem
< infoPtr
->nItemCount
);
1882 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1884 lpptPosition
->x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1885 lpptPosition
->y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1887 else if (uView
== LVS_LIST
)
1889 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
1890 lpptPosition
->x
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
1891 lpptPosition
->y
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
1893 else /* LVS_REPORT */
1895 lpptPosition
->x
= 0;
1896 lpptPosition
->y
= nItem
* infoPtr
->nItemHeight
;
1901 * DESCRIPTION: [INTERNAL]
1902 * Compute the rectangles of an item. This is to localize all
1903 * the computations in one place. If you are not interested in some
1904 * of these values, simply pass in a NULL -- the function is smart
1905 * enough to compute only what's necessary. The function computes
1906 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1907 * one, the BOX rectangle. This rectangle is very cheap to compute,
1908 * and is guaranteed to contain all the other rectangles. Computing
1909 * the ICON rect is also cheap, but all the others are potentially
1910 * expensive. This gives an easy and effective optimization when
1911 * searching (like point inclusion, or rectangle intersection):
1912 * first test against the BOX, and if TRUE, test against the desired
1914 * If the function does not have all the necessary information
1915 * to computed the requested rectangles, will crash with a
1916 * failed assertion. This is done so we catch all programming
1917 * errors, given that the function is called only from our code.
1919 * We have the following 'special' meanings for a few fields:
1920 * * If LVIS_FOCUSED is set, we assume the item has the focus
1921 * This is important in ICON mode, where it might get a larger
1922 * then usual rectangle
1924 * Please note that subitem support works only in REPORT mode.
1927 * [I] infoPtr : valid pointer to the listview structure
1928 * [I] lpLVItem : item to compute the measures for
1929 * [O] lprcBox : ptr to Box rectangle
1930 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1931 * [0] lprcSelectBox : ptr to select box rectangle
1932 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1933 * [O] lprcIcon : ptr to Icon rectangle
1934 * Same as LVM_GETITEMRECT with LVIR_ICON
1935 * [O] lprcStateIcon: ptr to State Icon rectangle
1936 * [O] lprcLabel : ptr to Label rectangle
1937 * Same as LVM_GETITEMRECT with LVIR_LABEL
1942 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
,
1943 LPRECT lprcBox
, LPRECT lprcSelectBox
,
1944 LPRECT lprcIcon
, LPRECT lprcStateIcon
, LPRECT lprcLabel
)
1946 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1947 BOOL doSelectBox
= FALSE
, doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
1948 RECT Box
, SelectBox
, Icon
, Label
;
1949 COLUMN_INFO
*lpColumnInfo
= NULL
;
1950 SIZE labelSize
= { 0, 0 };
1952 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem
, TRUE
));
1954 /* Be smart and try to figure out the minimum we have to do */
1955 if (lpLVItem
->iSubItem
) assert(uView
== LVS_REPORT
);
1956 if (uView
== LVS_ICON
&& (lprcBox
|| lprcLabel
))
1958 assert((lpLVItem
->mask
& LVIF_STATE
) && (lpLVItem
->stateMask
& LVIS_FOCUSED
));
1959 if (lpLVItem
->state
& LVIS_FOCUSED
) oversizedBox
= doLabel
= TRUE
;
1961 if (lprcSelectBox
) doSelectBox
= TRUE
;
1962 if (lprcLabel
) doLabel
= TRUE
;
1963 if (doLabel
|| lprcIcon
|| lprcStateIcon
) doIcon
= TRUE
;
1970 /************************************************************/
1971 /* compute the box rectangle (it should be cheap to do) */
1972 /************************************************************/
1973 if (lpLVItem
->iSubItem
|| uView
== LVS_REPORT
)
1974 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpLVItem
->iSubItem
);
1976 if (lpLVItem
->iSubItem
)
1978 Box
= lpColumnInfo
->rcHeader
;
1983 Box
.right
= infoPtr
->nItemWidth
;
1986 Box
.bottom
= infoPtr
->nItemHeight
;
1988 /******************************************************************/
1989 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
1990 /******************************************************************/
1993 LONG state_width
= 0;
1995 if (infoPtr
->himlState
&& lpLVItem
->iSubItem
== 0)
1996 state_width
= infoPtr
->iconStateSize
.cx
;
1998 if (uView
== LVS_ICON
)
2000 Icon
.left
= Box
.left
+ state_width
;
2001 if (infoPtr
->himlNormal
)
2002 Icon
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
- state_width
) / 2;
2003 Icon
.top
= Box
.top
+ ICON_TOP_PADDING
;
2004 Icon
.right
= Icon
.left
;
2005 Icon
.bottom
= Icon
.top
;
2006 if (infoPtr
->himlNormal
)
2008 Icon
.right
+= infoPtr
->iconSize
.cx
;
2009 Icon
.bottom
+= infoPtr
->iconSize
.cy
;
2012 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2014 Icon
.left
= Box
.left
+ state_width
;
2016 if (uView
== LVS_REPORT
)
2017 Icon
.left
+= REPORT_MARGINX
;
2020 Icon
.right
= Icon
.left
;
2021 if (infoPtr
->himlSmall
&&
2022 (!lpColumnInfo
|| lpLVItem
->iSubItem
== 0 || (lpColumnInfo
->fmt
& LVCFMT_IMAGE
) ||
2023 ((infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
) && lpLVItem
->iImage
!= I_IMAGECALLBACK
)))
2024 Icon
.right
+= infoPtr
->iconSize
.cx
;
2025 Icon
.bottom
= Icon
.top
+ infoPtr
->iconSize
.cy
;
2027 if(lprcIcon
) *lprcIcon
= Icon
;
2028 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon
));
2030 /* TODO: is this correct? */
2033 lprcStateIcon
->left
= Icon
.left
- state_width
;
2034 lprcStateIcon
->right
= Icon
.left
;
2035 lprcStateIcon
->top
= Icon
.top
;
2036 lprcStateIcon
->bottom
= lprcStateIcon
->top
+ infoPtr
->iconSize
.cy
;
2037 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon
));
2040 else Icon
.right
= 0;
2042 /************************************************************/
2043 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2044 /************************************************************/
2047 /* calculate how far to the right can the label stretch */
2048 Label
.right
= Box
.right
;
2049 if (uView
== LVS_REPORT
)
2051 if (lpLVItem
->iSubItem
== 0) Label
= lpColumnInfo
->rcHeader
;
2054 if (lpLVItem
->iSubItem
|| ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && uView
== LVS_REPORT
))
2056 labelSize
.cx
= infoPtr
->nItemWidth
;
2057 labelSize
.cy
= infoPtr
->nItemHeight
;
2061 /* we need the text in non owner draw mode */
2062 assert(lpLVItem
->mask
& LVIF_TEXT
);
2063 if (is_textT(lpLVItem
->pszText
, TRUE
))
2065 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2066 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2067 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2071 /* compute rough rectangle where the label will go */
2072 SetRectEmpty(&rcText
);
2073 rcText
.right
= infoPtr
->nItemWidth
- TRAILING_LABEL_PADDING
;
2074 rcText
.bottom
= infoPtr
->nItemHeight
;
2075 if (uView
== LVS_ICON
)
2076 rcText
.bottom
-= ICON_TOP_PADDING
+ infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2078 /* now figure out the flags */
2079 if (uView
== LVS_ICON
)
2080 uFormat
= oversizedBox
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
;
2082 uFormat
= LV_SL_DT_FLAGS
;
2084 DrawTextW (hdc
, lpLVItem
->pszText
, -1, &rcText
, uFormat
| DT_CALCRECT
);
2086 labelSize
.cx
= min(rcText
.right
- rcText
.left
+ TRAILING_LABEL_PADDING
, infoPtr
->nItemWidth
);
2087 labelSize
.cy
= rcText
.bottom
- rcText
.top
;
2089 SelectObject(hdc
, hOldFont
);
2090 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2094 if (uView
== LVS_ICON
)
2096 Label
.left
= Box
.left
+ (infoPtr
->nItemWidth
- labelSize
.cx
) / 2;
2097 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
2098 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2099 Label
.right
= Label
.left
+ labelSize
.cx
;
2100 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2101 if (!oversizedBox
&& labelSize
.cy
> infoPtr
->ntmHeight
)
2103 labelSize
.cy
= min(Box
.bottom
- Label
.top
, labelSize
.cy
);
2104 labelSize
.cy
/= infoPtr
->ntmHeight
;
2105 labelSize
.cy
= max(labelSize
.cy
, 1);
2106 labelSize
.cy
*= infoPtr
->ntmHeight
;
2108 Label
.bottom
= Label
.top
+ labelSize
.cy
+ HEIGHT_PADDING
;
2110 else if (uView
== LVS_REPORT
)
2112 Label
.left
= Icon
.right
;
2113 Label
.top
= Box
.top
;
2114 Label
.right
= lpColumnInfo
->rcHeader
.right
;
2115 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2117 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2119 Label
.left
= Icon
.right
;
2120 Label
.top
= Box
.top
;
2121 Label
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2122 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2125 if (lprcLabel
) *lprcLabel
= Label
;
2126 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label
));
2129 /************************************************************/
2130 /* compute STATEICON bounding box */
2131 /************************************************************/
2134 if (uView
== LVS_REPORT
)
2136 SelectBox
.left
= Icon
.right
; /* FIXME: should be Icon.left */
2137 SelectBox
.top
= Box
.top
;
2138 SelectBox
.bottom
= Box
.bottom
;
2139 if (lpLVItem
->iSubItem
== 0)
2141 /* we need the indent in report mode */
2142 assert(lpLVItem
->mask
& LVIF_INDENT
);
2143 SelectBox
.left
+= infoPtr
->iconSize
.cx
* lpLVItem
->iIndent
;
2145 SelectBox
.right
= min(SelectBox
.left
+ labelSize
.cx
, Label
.right
);
2149 UnionRect(&SelectBox
, &Icon
, &Label
);
2151 if (lprcSelectBox
) *lprcSelectBox
= SelectBox
;
2152 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox
));
2155 /* Fix the Box if necessary */
2158 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
2159 else *lprcBox
= Box
;
2161 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box
));
2165 * DESCRIPTION: [INTERNAL]
2168 * [I] infoPtr : valid pointer to the listview structure
2169 * [I] nItem : item number
2170 * [O] lprcBox : ptr to Box rectangle
2175 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprcBox
)
2177 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2178 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2179 POINT Position
, Origin
;
2182 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
2183 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
2185 /* Be smart and try to figure out the minimum we have to do */
2187 if (uView
== LVS_ICON
&& infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
2188 lvItem
.mask
|= LVIF_TEXT
;
2189 lvItem
.iItem
= nItem
;
2190 lvItem
.iSubItem
= 0;
2191 lvItem
.pszText
= szDispText
;
2192 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2193 if (lvItem
.mask
) LISTVIEW_GetItemW(infoPtr
, &lvItem
);
2194 if (uView
== LVS_ICON
)
2196 lvItem
.mask
|= LVIF_STATE
;
2197 lvItem
.stateMask
= LVIS_FOCUSED
;
2198 lvItem
.state
= (lvItem
.mask
& LVIF_TEXT
? LVIS_FOCUSED
: 0);
2200 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprcBox
, 0, 0, 0, 0);
2202 OffsetRect(lprcBox
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
2208 * Returns the current icon position, and advances it along the top.
2209 * The returned position is not offset by Origin.
2212 * [I] infoPtr : valid pointer to the listview structure
2213 * [O] lpPos : will get the current icon position
2218 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2220 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2222 *lpPos
= infoPtr
->currIconPos
;
2224 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2225 if (infoPtr
->currIconPos
.x
+ infoPtr
->nItemWidth
<= nListWidth
) return;
2227 infoPtr
->currIconPos
.x
= 0;
2228 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2234 * Returns the current icon position, and advances it down the left edge.
2235 * The returned position is not offset by Origin.
2238 * [I] infoPtr : valid pointer to the listview structure
2239 * [O] lpPos : will get the current icon position
2244 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2246 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2248 *lpPos
= infoPtr
->currIconPos
;
2250 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2251 if (infoPtr
->currIconPos
.y
+ infoPtr
->nItemHeight
<= nListHeight
) return;
2253 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2254 infoPtr
->currIconPos
.y
= 0;
2260 * Moves an icon to the specified position.
2261 * It takes care of invalidating the item, etc.
2264 * [I] infoPtr : valid pointer to the listview structure
2265 * [I] nItem : the item to move
2266 * [I] lpPos : the new icon position
2267 * [I] isNew : flags the item as being new
2273 static BOOL
LISTVIEW_MoveIconTo(const LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*lppt
, BOOL isNew
)
2279 old
.x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2280 old
.y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2282 if (lppt
->x
== old
.x
&& lppt
->y
== old
.y
) return TRUE
;
2283 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2286 /* Allocating a POINTER for every item is too resource intensive,
2287 * so we'll keep the (x,y) in different arrays */
2288 if (!DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)(LONG_PTR
)lppt
->x
)) return FALSE
;
2289 if (!DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)(LONG_PTR
)lppt
->y
)) return FALSE
;
2291 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2298 * Arranges listview items in icon display mode.
2301 * [I] infoPtr : valid pointer to the listview structure
2302 * [I] nAlignCode : alignment code
2308 static BOOL
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
2310 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2311 void (*next_pos
)(LISTVIEW_INFO
*, LPPOINT
);
2315 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
) return FALSE
;
2317 TRACE("nAlignCode=%d\n", nAlignCode
);
2319 if (nAlignCode
== LVA_DEFAULT
)
2321 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
) nAlignCode
= LVA_ALIGNLEFT
;
2322 else nAlignCode
= LVA_ALIGNTOP
;
2327 case LVA_ALIGNLEFT
: next_pos
= LISTVIEW_NextIconPosLeft
; break;
2328 case LVA_ALIGNTOP
: next_pos
= LISTVIEW_NextIconPosTop
; break;
2329 case LVA_SNAPTOGRID
: next_pos
= LISTVIEW_NextIconPosTop
; break; /* FIXME */
2330 default: return FALSE
;
2333 infoPtr
->bAutoarrange
= TRUE
;
2334 infoPtr
->currIconPos
.x
= infoPtr
->currIconPos
.y
= 0;
2335 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2337 next_pos(infoPtr
, &pos
);
2338 LISTVIEW_MoveIconTo(infoPtr
, i
, &pos
, FALSE
);
2346 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2349 * [I] infoPtr : valid pointer to the listview structure
2350 * [O] lprcView : bounding rectangle
2356 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2360 SetRectEmpty(lprcView
);
2362 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
2366 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2368 x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, i
);
2369 y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, i
);
2370 lprcView
->right
= max(lprcView
->right
, x
);
2371 lprcView
->bottom
= max(lprcView
->bottom
, y
);
2373 if (infoPtr
->nItemCount
> 0)
2375 lprcView
->right
+= infoPtr
->nItemWidth
;
2376 lprcView
->bottom
+= infoPtr
->nItemHeight
;
2381 y
= LISTVIEW_GetCountPerColumn(infoPtr
);
2382 x
= infoPtr
->nItemCount
/ y
;
2383 if (infoPtr
->nItemCount
% y
) x
++;
2384 lprcView
->right
= x
* infoPtr
->nItemWidth
;
2385 lprcView
->bottom
= y
* infoPtr
->nItemHeight
;
2389 lprcView
->right
= infoPtr
->nItemWidth
;
2390 lprcView
->bottom
= infoPtr
->nItemCount
* infoPtr
->nItemHeight
;
2397 * Retrieves the bounding rectangle of all the items.
2400 * [I] infoPtr : valid pointer to the listview structure
2401 * [O] lprcView : bounding rectangle
2407 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2411 TRACE("(lprcView=%p)\n", lprcView
);
2413 if (!lprcView
) return FALSE
;
2415 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
2416 LISTVIEW_GetAreaRect(infoPtr
, lprcView
);
2417 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
2419 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView
));
2426 * Retrieves the subitem pointer associated with the subitem index.
2429 * [I] hdpaSubItems : DPA handle for a specific item
2430 * [I] nSubItem : index of subitem
2433 * SUCCESS : subitem pointer
2436 static SUBITEM_INFO
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
, INT nSubItem
)
2438 SUBITEM_INFO
*lpSubItem
;
2441 /* we should binary search here if need be */
2442 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
2444 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
2445 if (lpSubItem
->iSubItem
== nSubItem
)
2455 * Calculates the desired item width.
2458 * [I] infoPtr : valid pointer to the listview structure
2461 * The desired item width.
2463 static INT
LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO
*infoPtr
)
2465 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2468 TRACE("uView=%d\n", uView
);
2470 if (uView
== LVS_ICON
)
2471 nItemWidth
= infoPtr
->iconSpacing
.cx
;
2472 else if (uView
== LVS_REPORT
)
2476 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2478 LISTVIEW_GetHeaderRect(infoPtr
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, &rcHeader
);
2479 nItemWidth
= rcHeader
.right
;
2482 else /* LVS_SMALLICON, or LVS_LIST */
2486 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2487 nItemWidth
= max(LISTVIEW_GetLabelWidth(infoPtr
, i
), nItemWidth
);
2489 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
2490 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
2492 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
+ WIDTH_PADDING
);
2495 return max(nItemWidth
, 1);
2500 * Calculates the desired item height.
2503 * [I] infoPtr : valid pointer to the listview structure
2506 * The desired item height.
2508 static INT
LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO
*infoPtr
)
2510 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2513 TRACE("uView=%d\n", uView
);
2515 if (uView
== LVS_ICON
)
2516 nItemHeight
= infoPtr
->iconSpacing
.cy
;
2519 nItemHeight
= infoPtr
->ntmHeight
;
2520 if (uView
== LVS_REPORT
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
2522 if (infoPtr
->himlState
)
2523 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
2524 if (infoPtr
->himlSmall
)
2525 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
2526 if (infoPtr
->himlState
|| infoPtr
->himlSmall
)
2527 nItemHeight
+= HEIGHT_PADDING
;
2528 if (infoPtr
->nMeasureItemHeight
> 0)
2529 nItemHeight
= infoPtr
->nMeasureItemHeight
;
2532 return max(nItemHeight
, 1);
2537 * Updates the width, and height of an item.
2540 * [I] infoPtr : valid pointer to the listview structure
2545 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO
*infoPtr
)
2547 infoPtr
->nItemWidth
= LISTVIEW_CalculateItemWidth(infoPtr
);
2548 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
2554 * Retrieves and saves important text metrics info for the current
2558 * [I] infoPtr : valid pointer to the listview structure
2561 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
2563 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2564 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2565 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2569 if (GetTextMetricsW(hdc
, &tm
))
2571 infoPtr
->ntmHeight
= tm
.tmHeight
;
2572 infoPtr
->ntmMaxCharWidth
= tm
.tmMaxCharWidth
;
2575 if (GetTextExtentPoint32A(hdc
, "...", 3, &sz
))
2576 infoPtr
->nEllipsisWidth
= sz
.cx
;
2578 SelectObject(hdc
, hOldFont
);
2579 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2581 TRACE("tmHeight=%d\n", infoPtr
->ntmHeight
);
2586 * A compare function for ranges
2589 * [I] range1 : pointer to range 1;
2590 * [I] range2 : pointer to range 2;
2594 * > 0 : if range 1 > range 2
2595 * < 0 : if range 2 > range 1
2596 * = 0 : if range intersects range 2
2598 static INT CALLBACK
ranges_cmp(LPVOID range1
, LPVOID range2
, LPARAM flags
)
2602 if (((RANGE
*)range1
)->upper
<= ((RANGE
*)range2
)->lower
)
2604 else if (((RANGE
*)range2
)->upper
<= ((RANGE
*)range1
)->lower
)
2609 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1
), debugrange(range2
), cmp
);
2615 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2617 #define ranges_check(ranges, desc) do { } while(0)
2620 static void ranges_assert(RANGES ranges
, LPCSTR desc
, const char *func
, int line
)
2625 TRACE("*** Checking %s:%d:%s ***\n", func
, line
, desc
);
2627 assert (DPA_GetPtrCount(ranges
->hdpa
) >= 0);
2628 ranges_dump(ranges
);
2629 prev
= DPA_GetPtr(ranges
->hdpa
, 0);
2630 if (DPA_GetPtrCount(ranges
->hdpa
) > 0)
2631 assert (prev
->lower
>= 0 && prev
->lower
< prev
->upper
);
2632 for (i
= 1; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2634 curr
= DPA_GetPtr(ranges
->hdpa
, i
);
2635 assert (prev
->upper
<= curr
->lower
);
2636 assert (curr
->lower
< curr
->upper
);
2639 TRACE("--- Done checking---\n");
2642 static RANGES
ranges_create(int count
)
2644 RANGES ranges
= Alloc(sizeof(struct tagRANGES
));
2645 if (!ranges
) return NULL
;
2646 ranges
->hdpa
= DPA_Create(count
);
2647 if (ranges
->hdpa
) return ranges
;
2652 static void ranges_clear(RANGES ranges
)
2656 for(i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2657 Free(DPA_GetPtr(ranges
->hdpa
, i
));
2658 DPA_DeleteAllPtrs(ranges
->hdpa
);
2662 static void ranges_destroy(RANGES ranges
)
2664 if (!ranges
) return;
2665 ranges_clear(ranges
);
2666 DPA_Destroy(ranges
->hdpa
);
2670 static RANGES
ranges_clone(RANGES ranges
)
2675 if (!(clone
= ranges_create(DPA_GetPtrCount(ranges
->hdpa
)))) goto fail
;
2677 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2679 RANGE
*newrng
= Alloc(sizeof(RANGE
));
2680 if (!newrng
) goto fail
;
2681 *newrng
= *((RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
));
2682 DPA_SetPtr(clone
->hdpa
, i
, newrng
);
2687 TRACE ("clone failed\n");
2688 ranges_destroy(clone
);
2692 static RANGES
ranges_diff(RANGES ranges
, RANGES sub
)
2696 for (i
= 0; i
< DPA_GetPtrCount(sub
->hdpa
); i
++)
2697 ranges_del(ranges
, *((RANGE
*)DPA_GetPtr(sub
->hdpa
, i
)));
2702 static void ranges_dump(RANGES ranges
)
2706 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2707 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges
->hdpa
, i
)));
2710 static inline BOOL
ranges_contain(RANGES ranges
, INT nItem
)
2712 RANGE srchrng
= { nItem
, nItem
+ 1 };
2714 TRACE("(nItem=%d)\n", nItem
);
2715 ranges_check(ranges
, "before contain");
2716 return DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
) != -1;
2719 static INT
ranges_itemcount(RANGES ranges
)
2723 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2725 RANGE
*sel
= DPA_GetPtr(ranges
->hdpa
, i
);
2726 count
+= sel
->upper
- sel
->lower
;
2732 static BOOL
ranges_shift(RANGES ranges
, INT nItem
, INT delta
, INT nUpper
)
2734 RANGE srchrng
= { nItem
, nItem
+ 1 }, *chkrng
;
2737 index
= DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2738 if (index
== -1) return TRUE
;
2740 for (; index
< DPA_GetPtrCount(ranges
->hdpa
); index
++)
2742 chkrng
= DPA_GetPtr(ranges
->hdpa
, index
);
2743 if (chkrng
->lower
>= nItem
)
2744 chkrng
->lower
= max(min(chkrng
->lower
+ delta
, nUpper
- 1), 0);
2745 if (chkrng
->upper
> nItem
)
2746 chkrng
->upper
= max(min(chkrng
->upper
+ delta
, nUpper
), 0);
2751 static BOOL
ranges_add(RANGES ranges
, RANGE range
)
2756 TRACE("(%s)\n", debugrange(&range
));
2757 ranges_check(ranges
, "before add");
2759 /* try find overlapping regions first */
2760 srchrgn
.lower
= range
.lower
- 1;
2761 srchrgn
.upper
= range
.upper
+ 1;
2762 index
= DPA_Search(ranges
->hdpa
, &srchrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
);
2768 TRACE("Adding new range\n");
2770 /* create the brand new range to insert */
2771 newrgn
= Alloc(sizeof(RANGE
));
2772 if(!newrgn
) goto fail
;
2775 /* figure out where to insert it */
2776 index
= DPA_Search(ranges
->hdpa
, newrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2777 TRACE("index=%d\n", index
);
2778 if (index
== -1) index
= 0;
2780 /* and get it over with */
2781 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2789 RANGE
*chkrgn
, *mrgrgn
;
2790 INT fromindex
, mergeindex
;
2792 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2793 TRACE("Merge with %s @%d\n", debugrange(chkrgn
), index
);
2795 chkrgn
->lower
= min(range
.lower
, chkrgn
->lower
);
2796 chkrgn
->upper
= max(range
.upper
, chkrgn
->upper
);
2798 TRACE("New range %s @%d\n", debugrange(chkrgn
), index
);
2800 /* merge now common ranges */
2802 srchrgn
.lower
= chkrgn
->lower
- 1;
2803 srchrgn
.upper
= chkrgn
->upper
+ 1;
2807 mergeindex
= DPA_Search(ranges
->hdpa
, &srchrgn
, fromindex
, ranges_cmp
, 0, 0);
2808 if (mergeindex
== -1) break;
2809 if (mergeindex
== index
)
2811 fromindex
= index
+ 1;
2815 TRACE("Merge with index %i\n", mergeindex
);
2817 mrgrgn
= DPA_GetPtr(ranges
->hdpa
, mergeindex
);
2818 chkrgn
->lower
= min(chkrgn
->lower
, mrgrgn
->lower
);
2819 chkrgn
->upper
= max(chkrgn
->upper
, mrgrgn
->upper
);
2821 DPA_DeletePtr(ranges
->hdpa
, mergeindex
);
2822 if (mergeindex
< index
) index
--;
2826 ranges_check(ranges
, "after add");
2830 ranges_check(ranges
, "failed add");
2834 static BOOL
ranges_del(RANGES ranges
, RANGE range
)
2839 TRACE("(%s)\n", debugrange(&range
));
2840 ranges_check(ranges
, "before del");
2842 /* we don't use DPAS_SORTED here, since we need *
2843 * to find the first overlapping range */
2844 index
= DPA_Search(ranges
->hdpa
, &range
, 0, ranges_cmp
, 0, 0);
2847 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2849 TRACE("Matches range %s @%d\n", debugrange(chkrgn
), index
);
2851 /* case 1: Same range */
2852 if ( (chkrgn
->upper
== range
.upper
) &&
2853 (chkrgn
->lower
== range
.lower
) )
2855 DPA_DeletePtr(ranges
->hdpa
, index
);
2858 /* case 2: engulf */
2859 else if ( (chkrgn
->upper
<= range
.upper
) &&
2860 (chkrgn
->lower
>= range
.lower
) )
2862 DPA_DeletePtr(ranges
->hdpa
, index
);
2864 /* case 3: overlap upper */
2865 else if ( (chkrgn
->upper
<= range
.upper
) &&
2866 (chkrgn
->lower
< range
.lower
) )
2868 chkrgn
->upper
= range
.lower
;
2870 /* case 4: overlap lower */
2871 else if ( (chkrgn
->upper
> range
.upper
) &&
2872 (chkrgn
->lower
>= range
.lower
) )
2874 chkrgn
->lower
= range
.upper
;
2877 /* case 5: fully internal */
2880 RANGE tmprgn
= *chkrgn
, *newrgn
;
2882 if (!(newrgn
= Alloc(sizeof(RANGE
)))) goto fail
;
2883 newrgn
->lower
= chkrgn
->lower
;
2884 newrgn
->upper
= range
.lower
;
2885 chkrgn
->lower
= range
.upper
;
2886 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2895 index
= DPA_Search(ranges
->hdpa
, &range
, index
, ranges_cmp
, 0, 0);
2898 ranges_check(ranges
, "after del");
2902 ranges_check(ranges
, "failed del");
2908 * Removes all selection ranges
2911 * [I] infoPtr : valid pointer to the listview structure
2912 * [I] toSkip : item range to skip removing the selection
2918 static BOOL
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO
*infoPtr
, RANGES toSkip
)
2927 lvItem
.stateMask
= LVIS_SELECTED
;
2929 /* need to clone the DPA because callbacks can change it */
2930 if (!(clone
= ranges_clone(infoPtr
->selectionRanges
))) return FALSE
;
2931 iterator_rangesitems(&i
, ranges_diff(clone
, toSkip
));
2932 while(iterator_next(&i
))
2933 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &lvItem
);
2934 /* note that the iterator destructor will free the cloned range */
2935 iterator_destroy(&i
);
2940 static inline BOOL
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2944 if (!(toSkip
= ranges_create(1))) return FALSE
;
2945 if (nItem
!= -1) ranges_additem(toSkip
, nItem
);
2946 LISTVIEW_DeselectAllSkipItems(infoPtr
, toSkip
);
2947 ranges_destroy(toSkip
);
2951 static inline BOOL
LISTVIEW_DeselectAll(LISTVIEW_INFO
*infoPtr
)
2953 return LISTVIEW_DeselectAllSkipItem(infoPtr
, -1);
2958 * Retrieves the number of items that are marked as selected.
2961 * [I] infoPtr : valid pointer to the listview structure
2964 * Number of items selected.
2966 static INT
LISTVIEW_GetSelectedCount(const LISTVIEW_INFO
*infoPtr
)
2968 INT nSelectedCount
= 0;
2970 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
2973 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2975 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
2980 nSelectedCount
= ranges_itemcount(infoPtr
->selectionRanges
);
2982 TRACE("nSelectedCount=%d\n", nSelectedCount
);
2983 return nSelectedCount
;
2988 * Manages the item focus.
2991 * [I] infoPtr : valid pointer to the listview structure
2992 * [I] nItem : item index
2995 * TRUE : focused item changed
2996 * FALSE : focused item has NOT changed
2998 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3000 INT oldFocus
= infoPtr
->nFocusedItem
;
3003 if (nItem
== infoPtr
->nFocusedItem
) return FALSE
;
3005 lvItem
.state
= nItem
== -1 ? 0 : LVIS_FOCUSED
;
3006 lvItem
.stateMask
= LVIS_FOCUSED
;
3007 LISTVIEW_SetItemState(infoPtr
, nItem
== -1 ? infoPtr
->nFocusedItem
: nItem
, &lvItem
);
3009 return oldFocus
!= infoPtr
->nFocusedItem
;
3012 /* Helper function for LISTVIEW_ShiftIndices *only* */
3013 static INT
shift_item(const LISTVIEW_INFO
*infoPtr
, INT nShiftItem
, INT nItem
, INT direction
)
3015 if (nShiftItem
< nItem
) return nShiftItem
;
3017 if (nShiftItem
> nItem
) return nShiftItem
+ direction
;
3019 if (direction
> 0) return nShiftItem
+ direction
;
3021 return min(nShiftItem
, infoPtr
->nItemCount
- 1);
3026 * Updates the various indices after an item has been inserted or deleted.
3029 * [I] infoPtr : valid pointer to the listview structure
3030 * [I] nItem : item index
3031 * [I] direction : Direction of shift, +1 or -1.
3036 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
3041 /* temporarily disable change notification while shifting items */
3042 bOldChange
= infoPtr
->bDoChangeNotify
;
3043 infoPtr
->bDoChangeNotify
= FALSE
;
3045 TRACE("Shifting %iu, %i steps\n", nItem
, direction
);
3047 ranges_shift(infoPtr
->selectionRanges
, nItem
, direction
, infoPtr
->nItemCount
);
3049 assert(abs(direction
) == 1);
3051 infoPtr
->nSelectionMark
= shift_item(infoPtr
, infoPtr
->nSelectionMark
, nItem
, direction
);
3053 nNewFocus
= shift_item(infoPtr
, infoPtr
->nFocusedItem
, nItem
, direction
);
3054 if (nNewFocus
!= infoPtr
->nFocusedItem
)
3055 LISTVIEW_SetItemFocus(infoPtr
, nNewFocus
);
3057 /* But we are not supposed to modify nHotItem! */
3059 infoPtr
->bDoChangeNotify
= bOldChange
;
3065 * Adds a block of selections.
3068 * [I] infoPtr : valid pointer to the listview structure
3069 * [I] nItem : item index
3072 * Whether the window is still valid.
3074 static BOOL
LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3076 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
3077 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
3078 HWND hwndSelf
= infoPtr
->hwndSelf
;
3079 NMLVODSTATECHANGE nmlv
;
3084 /* Temporarily disable change notification
3085 * If the control is LVS_OWNERDATA, we need to send
3086 * only one LVN_ODSTATECHANGED notification.
3087 * See MSDN documentation for LVN_ITEMCHANGED.
3089 bOldChange
= infoPtr
->bDoChangeNotify
;
3090 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3092 if (nFirst
== -1) nFirst
= nItem
;
3094 item
.state
= LVIS_SELECTED
;
3095 item
.stateMask
= LVIS_SELECTED
;
3097 for (i
= nFirst
; i
<= nLast
; i
++)
3098 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
3100 ZeroMemory(&nmlv
, sizeof(nmlv
));
3101 nmlv
.iFrom
= nFirst
;
3104 nmlv
.uOldState
= item
.state
;
3106 notify_hdr(infoPtr
, LVN_ODSTATECHANGED
, (LPNMHDR
)&nmlv
);
3107 if (!IsWindow(hwndSelf
))
3109 infoPtr
->bDoChangeNotify
= bOldChange
;
3116 * Sets a single group selection.
3119 * [I] infoPtr : valid pointer to the listview structure
3120 * [I] nItem : item index
3125 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3127 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3133 if (!(selection
= ranges_create(100))) return;
3135 item
.state
= LVIS_SELECTED
;
3136 item
.stateMask
= LVIS_SELECTED
;
3138 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
3140 if (infoPtr
->nSelectionMark
== -1)
3142 infoPtr
->nSelectionMark
= nItem
;
3143 ranges_additem(selection
, nItem
);
3149 sel
.lower
= min(infoPtr
->nSelectionMark
, nItem
);
3150 sel
.upper
= max(infoPtr
->nSelectionMark
, nItem
) + 1;
3151 ranges_add(selection
, sel
);
3156 RECT rcItem
, rcSel
, rcSelMark
;
3159 rcItem
.left
= LVIR_BOUNDS
;
3160 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return;
3161 rcSelMark
.left
= LVIR_BOUNDS
;
3162 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) return;
3163 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
3164 iterator_frameditems(&i
, infoPtr
, &rcSel
);
3165 while(iterator_next(&i
))
3167 LISTVIEW_GetItemPosition(infoPtr
, i
.nItem
, &ptItem
);
3168 if (PtInRect(&rcSel
, ptItem
)) ranges_additem(selection
, i
.nItem
);
3170 iterator_destroy(&i
);
3173 bOldChange
= infoPtr
->bDoChangeNotify
;
3174 infoPtr
->bDoChangeNotify
= FALSE
;
3176 LISTVIEW_DeselectAllSkipItems(infoPtr
, selection
);
3179 iterator_rangesitems(&i
, selection
);
3180 while(iterator_next(&i
))
3181 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &item
);
3182 /* this will also destroy the selection */
3183 iterator_destroy(&i
);
3185 infoPtr
->bDoChangeNotify
= bOldChange
;
3187 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3192 * Sets a single selection.
3195 * [I] infoPtr : valid pointer to the listview structure
3196 * [I] nItem : item index
3201 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3205 TRACE("nItem=%d\n", nItem
);
3207 LISTVIEW_DeselectAllSkipItem(infoPtr
, nItem
);
3209 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3210 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3211 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3213 infoPtr
->nSelectionMark
= nItem
;
3218 * Set selection(s) with keyboard.
3221 * [I] infoPtr : valid pointer to the listview structure
3222 * [I] nItem : item index
3225 * SUCCESS : TRUE (needs to be repainted)
3226 * FAILURE : FALSE (nothing has changed)
3228 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3230 /* FIXME: pass in the state */
3231 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
3232 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
3233 BOOL bResult
= FALSE
;
3235 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem
, wShift
, wCtrl
);
3236 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3238 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
3241 LISTVIEW_SetSelection(infoPtr
, nItem
);
3248 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3253 lvItem
.state
= ~LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3254 lvItem
.stateMask
= LVIS_SELECTED
;
3255 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3257 if (lvItem
.state
& LVIS_SELECTED
)
3258 infoPtr
->nSelectionMark
= nItem
;
3260 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3265 LISTVIEW_SetSelection(infoPtr
, nItem
);
3268 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3271 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3275 static BOOL
LISTVIEW_GetItemAtPt(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, POINT pt
)
3277 LVHITTESTINFO lvHitTestInfo
;
3279 ZeroMemory(&lvHitTestInfo
, sizeof(lvHitTestInfo
));
3280 lvHitTestInfo
.pt
.x
= pt
.x
;
3281 lvHitTestInfo
.pt
.y
= pt
.y
;
3283 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
3285 lpLVItem
->mask
= LVIF_PARAM
;
3286 lpLVItem
->iItem
= lvHitTestInfo
.iItem
;
3287 lpLVItem
->iSubItem
= 0;
3289 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
3292 static inline BOOL
LISTVIEW_isHotTracking(const LISTVIEW_INFO
*infoPtr
)
3294 return ((infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) ||
3295 (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
) ||
3296 (infoPtr
->dwLvExStyle
& LVS_EX_TWOCLICKACTIVATE
));
3301 * Called when the mouse is being actively tracked and has hovered for a specified
3305 * [I] infoPtr : valid pointer to the listview structure
3306 * [I] fwKeys : key indicator
3307 * [I] x,y : mouse position
3310 * 0 if the message was processed, non-zero if there was an error
3313 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3314 * over the item for a certain period of time.
3317 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3319 if (LISTVIEW_isHotTracking(infoPtr
))
3327 if (LISTVIEW_GetItemAtPt(infoPtr
, &item
, pt
))
3328 LISTVIEW_SetSelection(infoPtr
, item
.iItem
);
3336 * Called whenever WM_MOUSEMOVE is received.
3339 * [I] infoPtr : valid pointer to the listview structure
3340 * [I] fwKeys : key indicator
3341 * [I] x,y : mouse position
3344 * 0 if the message is processed, non-zero if there was an error
3346 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3348 TRACKMOUSEEVENT trackinfo
;
3350 if (!(fwKeys
& MK_LBUTTON
))
3351 infoPtr
->bLButtonDown
= FALSE
;
3353 if (infoPtr
->bLButtonDown
)
3357 WORD wDragWidth
= GetSystemMetrics(SM_CXDRAG
);
3358 WORD wDragHeight
= GetSystemMetrics(SM_CYDRAG
);
3360 rect
.left
= infoPtr
->ptClickPos
.x
- wDragWidth
;
3361 rect
.right
= infoPtr
->ptClickPos
.x
+ wDragWidth
;
3362 rect
.top
= infoPtr
->ptClickPos
.y
- wDragHeight
;
3363 rect
.bottom
= infoPtr
->ptClickPos
.y
+ wDragHeight
;
3368 if (!PtInRect(&rect
, tmp
))
3370 LVHITTESTINFO lvHitTestInfo
;
3373 lvHitTestInfo
.pt
= infoPtr
->ptClickPos
;
3374 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
3376 ZeroMemory(&nmlv
, sizeof(nmlv
));
3377 nmlv
.iItem
= lvHitTestInfo
.iItem
;
3378 nmlv
.ptAction
= infoPtr
->ptClickPos
;
3380 if (!infoPtr
->bDragging
)
3382 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
3383 infoPtr
->bDragging
= TRUE
;
3390 infoPtr
->bLButtonDown
= FALSE
;
3392 /* see if we are supposed to be tracking mouse hovering */
3393 if (LISTVIEW_isHotTracking(infoPtr
)) {
3394 /* fill in the trackinfo struct */
3395 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3396 trackinfo
.dwFlags
= TME_QUERY
;
3397 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
3398 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
3400 /* see if we are already tracking this hwnd */
3401 _TrackMouseEvent(&trackinfo
);
3403 if(!(trackinfo
.dwFlags
& TME_HOVER
)) {
3404 trackinfo
.dwFlags
= TME_HOVER
;
3406 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3407 _TrackMouseEvent(&trackinfo
);
3416 * Tests whether the item is assignable to a list with style lStyle
3418 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
3420 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
3421 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
3422 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
3430 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3433 * [I] infoPtr : valid pointer to the listview structure
3434 * [I] lpLVItem : valid pointer to new item attributes
3435 * [I] isNew : the item being set is being inserted
3436 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3437 * [O] bChanged : will be set to TRUE if the item really changed
3443 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
3445 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3453 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
3455 if (lpLVItem
->mask
== 0) return TRUE
;
3457 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
3459 /* a virtual listview only stores selection and focus */
3460 if (lpLVItem
->mask
& ~LVIF_STATE
)
3466 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3467 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
3471 /* we need to get the lParam and state of the item */
3472 item
.iItem
= lpLVItem
->iItem
;
3473 item
.iSubItem
= lpLVItem
->iSubItem
;
3474 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
3475 item
.stateMask
= ~0;
3478 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
3480 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
3481 /* determine what fields will change */
3482 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
))
3483 uChanged
|= LVIF_STATE
;
3485 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
3486 uChanged
|= LVIF_IMAGE
;
3488 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
3489 uChanged
|= LVIF_PARAM
;
3491 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
3492 uChanged
|= LVIF_INDENT
;
3494 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
3495 uChanged
|= LVIF_TEXT
;
3497 TRACE("uChanged=0x%x\n", uChanged
);
3498 if (!uChanged
) return TRUE
;
3501 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
3502 nmlv
.iItem
= lpLVItem
->iItem
;
3503 nmlv
.uNewState
= (item
.state
& ~lpLVItem
->stateMask
) | (lpLVItem
->state
& lpLVItem
->stateMask
);
3504 nmlv
.uOldState
= item
.state
;
3505 nmlv
.uChanged
= uChanged
;
3506 nmlv
.lParam
= item
.lParam
;
3508 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3509 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3511 if(lpItem
&& !isNew
&& infoPtr
->bDoChangeNotify
)
3513 HWND hwndSelf
= infoPtr
->hwndSelf
;
3515 if (notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
3517 if (!IsWindow(hwndSelf
))
3521 /* copy information */
3522 if (lpLVItem
->mask
& LVIF_TEXT
)
3523 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3525 if (lpLVItem
->mask
& LVIF_IMAGE
)
3526 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
3528 if (lpLVItem
->mask
& LVIF_PARAM
)
3529 lpItem
->lParam
= lpLVItem
->lParam
;
3531 if (lpLVItem
->mask
& LVIF_INDENT
)
3532 lpItem
->iIndent
= lpLVItem
->iIndent
;
3534 if (uChanged
& LVIF_STATE
)
3536 if (lpItem
&& (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& ~(LVIS_FOCUSED
| LVIS_SELECTED
)))
3538 lpItem
->state
&= ~lpLVItem
->stateMask
;
3539 lpItem
->state
|= (lpLVItem
->state
& lpLVItem
->stateMask
);
3541 if (lpLVItem
->state
& lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3543 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
3544 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3546 else if (lpLVItem
->stateMask
& LVIS_SELECTED
)
3547 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3549 /* if we are asked to change focus, and we manage it, do it */
3550 if (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
3552 if (lpLVItem
->state
& LVIS_FOCUSED
)
3554 LISTVIEW_SetItemFocus(infoPtr
, -1);
3555 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
3556 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, uView
== LVS_LIST
);
3558 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
3559 infoPtr
->nFocusedItem
= -1;
3563 /* if we're inserting the item, we're done */
3564 if (isNew
) return TRUE
;
3566 /* send LVN_ITEMCHANGED notification */
3567 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
3568 if (infoPtr
->bDoChangeNotify
) notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
3575 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3578 * [I] infoPtr : valid pointer to the listview structure
3579 * [I] lpLVItem : valid pointer to new subitem attributes
3580 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3581 * [O] bChanged : will be set to TRUE if the item really changed
3587 static BOOL
set_sub_item(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
3590 SUBITEM_INFO
*lpSubItem
;
3592 /* we do not support subitems for virtual listviews */
3593 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
3595 /* set subitem only if column is present */
3596 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
3598 /* First do some sanity checks */
3599 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3600 particularly useful. We currently do not actually do anything with
3601 the flag on subitems.
3603 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
)) return FALSE
;
3604 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
))) return TRUE
;
3606 /* get the subitem structure, and create it if not there */
3607 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3608 assert (hdpaSubItems
);
3610 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
3613 SUBITEM_INFO
*tmpSubItem
;
3616 lpSubItem
= Alloc(sizeof(SUBITEM_INFO
));
3617 if (!lpSubItem
) return FALSE
;
3618 /* we could binary search here, if need be...*/
3619 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
3621 tmpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
3622 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
3624 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
3629 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
3630 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
3634 if (lpLVItem
->mask
& LVIF_IMAGE
)
3635 if (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
)
3637 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
3641 if (lpLVItem
->mask
& LVIF_TEXT
)
3642 if (lpSubItem
->hdr
.pszText
!= lpLVItem
->pszText
)
3644 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3653 * Sets item attributes.
3656 * [I] infoPtr : valid pointer to the listview structure
3657 * [I] lpLVItem : new item attributes
3658 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3664 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, LVITEMW
*lpLVItem
, BOOL isW
)
3666 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3667 HWND hwndSelf
= infoPtr
->hwndSelf
;
3668 LPWSTR pszText
= NULL
;
3669 BOOL bResult
, bChanged
= FALSE
;
3671 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
3673 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
3676 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3677 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_textW(lpLVItem
->pszText
))
3679 pszText
= lpLVItem
->pszText
;
3680 lpLVItem
->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
3683 /* actually set the fields */
3684 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
3686 if (lpLVItem
->iSubItem
)
3687 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
3689 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
3690 if (!IsWindow(hwndSelf
))
3693 /* redraw item, if necessary */
3694 if (bChanged
&& !infoPtr
->bIsDrawing
)
3696 /* this little optimization eliminates some nasty flicker */
3697 if ( uView
== LVS_REPORT
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
3698 !(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) &&
3699 lpLVItem
->iSubItem
> 0 && lpLVItem
->iSubItem
<= DPA_GetPtrCount(infoPtr
->hdpaColumns
) )
3700 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
3702 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
3707 textfreeT(lpLVItem
->pszText
, isW
);
3708 lpLVItem
->pszText
= pszText
;
3716 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3719 * [I] infoPtr : valid pointer to the listview structure
3724 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*infoPtr
)
3726 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3728 SCROLLINFO scrollInfo
;
3730 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3731 scrollInfo
.fMask
= SIF_POS
;
3733 if (uView
== LVS_LIST
)
3735 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
3736 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
3738 else if (uView
== LVS_REPORT
)
3740 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3741 nItem
= scrollInfo
.nPos
;
3745 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3746 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
3749 TRACE("nItem=%d\n", nItem
);
3757 * Erases the background of the given rectangle
3760 * [I] infoPtr : valid pointer to the listview structure
3761 * [I] hdc : device context handle
3762 * [I] lprcBox : clipping rectangle
3768 static inline BOOL
LISTVIEW_FillBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
3770 if (!infoPtr
->hBkBrush
) return FALSE
;
3772 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, wine_dbgstr_rect(lprcBox
), infoPtr
->hBkBrush
);
3774 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
3782 * [I] infoPtr : valid pointer to the listview structure
3783 * [I] hdc : device context handle
3784 * [I] nItem : item index
3785 * [I] nSubItem : subitem index
3786 * [I] pos : item position in client coordinates
3787 * [I] cdmode : custom draw mode
3793 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, INT nSubItem
, POINT pos
, DWORD cdmode
)
3795 UINT uFormat
, uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3796 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
3797 static WCHAR szCallback
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3798 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
3800 RECT rcSelect
, rcBox
, rcIcon
, rcLabel
, rcStateIcon
;
3801 NMLVCUSTOMDRAW nmlvcd
;
3806 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc
, nItem
, nSubItem
, wine_dbgstr_point(&pos
));
3808 /* get information needed for drawing the item */
3809 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_PARAM
;
3810 if (nSubItem
== 0) lvItem
.mask
|= LVIF_STATE
;
3811 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
3812 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
;
3813 lvItem
.iItem
= nItem
;
3814 lvItem
.iSubItem
= nSubItem
;
3817 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3818 lvItem
.pszText
= szDispText
;
3819 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
3820 if (nSubItem
> 0 && (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3821 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3822 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= szCallback
;
3823 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3825 /* now check if we need to update the focus rectangle */
3826 lprcFocus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
3828 if (!lprcFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
3829 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcSelect
, &rcIcon
, &rcStateIcon
, &rcLabel
);
3830 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
3831 OffsetRect(&rcSelect
, pos
.x
, pos
.y
);
3832 OffsetRect(&rcIcon
, pos
.x
, pos
.y
);
3833 OffsetRect(&rcStateIcon
, pos
.x
, pos
.y
);
3834 OffsetRect(&rcLabel
, pos
.x
, pos
.y
);
3835 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3836 wine_dbgstr_rect(&rcBox
), wine_dbgstr_rect(&rcSelect
),
3837 wine_dbgstr_rect(&rcIcon
), wine_dbgstr_rect(&rcLabel
));
3839 /* fill in the custom draw structure */
3840 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
3842 hOldFont
= GetCurrentObject(hdc
, OBJ_FONT
);
3843 if (nSubItem
> 0) cdmode
= infoPtr
->cditemmode
;
3844 if (cdmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3845 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3846 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3847 if (nSubItem
== 0) infoPtr
->cditemmode
= cdsubitemmode
;
3848 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3849 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3850 if (nSubItem
== 0 && cdsubitemmode
== CDRF_NOTIFYITEMDRAW
)
3852 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
3853 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3855 if (nSubItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
3856 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
3857 else if ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) == FALSE
)
3858 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, TRUE
);
3860 /* in full row select, subitems, will just use main item's colors */
3861 if (nSubItem
&& uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3862 nmlvcd
.clrTextBk
= CLR_NONE
;
3865 if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && (nSubItem
== 0))
3867 UINT uStateImage
= STATEIMAGEINDEX(lvItem
.state
);
3870 TRACE("uStateImage=%d\n", uStateImage
);
3871 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
,
3872 rcStateIcon
.left
, rcStateIcon
.top
, ILD_NORMAL
);
3877 himl
= (uView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
3878 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
))
3880 TRACE("iImage=%d\n", lvItem
.iImage
);
3881 ImageList_DrawEx(himl
, lvItem
.iImage
, hdc
, rcIcon
.left
, rcIcon
.top
,
3882 rcIcon
.right
- rcIcon
.left
, rcIcon
.bottom
- rcIcon
.top
, infoPtr
->clrBk
, CLR_DEFAULT
,
3883 (lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
) ? ILD_SELECTED
: ILD_NORMAL
);
3886 /* Don't bother painting item being edited */
3887 if (infoPtr
->hwndEdit
&& nItem
== infoPtr
->nEditLabelItem
&& nSubItem
== 0) goto postpaint
;
3889 /* FIXME: temporary hack */
3890 rcSelect
.left
= rcLabel
.left
;
3892 /* draw the selection background, if we're drawing the main item */
3895 /* in icon mode, the label rect is really what we want to draw the
3897 if (uView
== LVS_ICON
)
3900 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3901 rcSelect
.right
= rcBox
.right
;
3903 if (nmlvcd
.clrTextBk
!= CLR_NONE
)
3904 ExtTextOutW(hdc
, rcSelect
.left
, rcSelect
.top
, ETO_OPAQUE
, &rcSelect
, 0, 0, 0);
3905 if(lprcFocus
) *lprcFocus
= rcSelect
;
3908 /* figure out the text drawing flags */
3909 uFormat
= (uView
== LVS_ICON
? (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
3910 if (uView
== LVS_ICON
)
3911 uFormat
= (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
3914 switch (LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
3916 case LVCFMT_RIGHT
: uFormat
|= DT_RIGHT
; break;
3917 case LVCFMT_CENTER
: uFormat
|= DT_CENTER
; break;
3918 default: uFormat
|= DT_LEFT
;
3921 if (!(uFormat
& (DT_RIGHT
| DT_CENTER
)))
3923 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
3924 else rcLabel
.left
+= LABEL_HOR_PADDING
;
3926 else if (uFormat
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
3928 /* for GRIDLINES reduce the bottom so the text formats correctly */
3929 if (uView
== LVS_REPORT
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
3932 DrawTextW(hdc
, lvItem
.pszText
, -1, &rcLabel
, uFormat
);
3935 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
3936 notify_postpaint(infoPtr
, &nmlvcd
);
3937 if (cdsubitemmode
& CDRF_NEWFONT
)
3938 SelectObject(hdc
, hOldFont
);
3944 * Draws listview items when in owner draw mode.
3947 * [I] infoPtr : valid pointer to the listview structure
3948 * [I] hdc : device context handle
3953 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3955 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
3956 DWORD cditemmode
= CDRF_DODEFAULT
;
3957 NMLVCUSTOMDRAW nmlvcd
;
3958 POINT Origin
, Position
;
3964 ZeroMemory(&dis
, sizeof(dis
));
3966 /* Get scroll info once before loop */
3967 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3969 /* iterate through the invalidated rows */
3970 while(iterator_next(i
))
3972 item
.iItem
= i
->nItem
;
3974 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
3975 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
3976 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
3978 dis
.CtlType
= ODT_LISTVIEW
;
3980 dis
.itemID
= item
.iItem
;
3981 dis
.itemAction
= ODA_DRAWENTIRE
;
3983 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
3984 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
3985 dis
.hwndItem
= infoPtr
->hwndSelf
;
3987 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
3988 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
3989 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
3990 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
3991 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
3992 dis
.itemData
= item
.lParam
;
3994 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), wine_dbgstr_rect(&dis
.rcItem
));
3997 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3998 * structure for the rest. of the paint cycle
4000 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
4001 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
4002 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4004 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
4006 prepaint_setup (infoPtr
, hdc
, &nmlvcd
, FALSE
);
4007 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
4010 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
4011 notify_postpaint(infoPtr
, &nmlvcd
);
4017 * Draws listview items when in report display mode.
4020 * [I] infoPtr : valid pointer to the listview structure
4021 * [I] hdc : device context handle
4022 * [I] cdmode : custom draw mode
4027 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4030 RECT rcClip
, rcItem
;
4031 POINT Origin
, Position
;
4037 /* figure out what to draw */
4038 rgntype
= GetClipBox(hdc
, &rcClip
);
4039 if (rgntype
== NULLREGION
) return;
4041 /* Get scroll info once before loop */
4042 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4044 /* narrow down the columns we need to paint */
4045 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
4047 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
4048 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
4050 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
4052 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
4053 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
4055 iterator_rangeitems(&j
, colRange
);
4057 /* in full row select, we _have_ to draw the main item */
4058 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4061 /* iterate through the invalidated rows */
4062 while(iterator_next(i
))
4064 /* iterate through the invalidated columns */
4065 while(iterator_next(&j
))
4067 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4068 Position
.x
+= Origin
.x
;
4069 Position
.y
+= Origin
.y
;
4071 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
4073 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4075 rcItem
.bottom
= infoPtr
->nItemHeight
;
4076 OffsetRect(&rcItem
, Position
.x
, Position
.y
);
4077 if (!RectVisible(hdc
, &rcItem
)) continue;
4080 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, j
.nItem
, Position
, cdmode
);
4083 iterator_destroy(&j
);
4088 * Draws the gridlines if necessary when in report display mode.
4091 * [I] infoPtr : valid pointer to the listview structure
4092 * [I] hdc : device context handle
4097 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
4102 RECT rcClip
, rcItem
;
4109 /* figure out what to draw */
4110 rgntype
= GetClipBox(hdc
, &rcClip
);
4111 if (rgntype
== NULLREGION
) return;
4113 /* Get scroll info once before loop */
4114 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4116 /* narrow down the columns we need to paint */
4117 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
4119 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
4120 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
4122 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
4124 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
4125 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
4127 iterator_rangeitems(&j
, colRange
);
4129 if ((hPen
= CreatePen( PS_SOLID
, 1, comctl32_color
.clr3dFace
)))
4131 hOldPen
= SelectObject ( hdc
, hPen
);
4133 /* draw the vertical lines for the columns */
4134 iterator_rangeitems(&j
, colRange
);
4135 while(iterator_next(&j
))
4137 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4138 if (rcItem
.left
== 0) continue; /* skip first column */
4139 rcItem
.left
+= Origin
.x
;
4140 rcItem
.right
+= Origin
.x
;
4141 rcItem
.top
= infoPtr
->rcList
.top
;
4142 rcItem
.bottom
= infoPtr
->rcList
.bottom
;
4143 TRACE("vert col=%d, rcItem=%s\n", j
.nItem
, wine_dbgstr_rect(&rcItem
));
4144 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4145 LineTo (hdc
, rcItem
.left
, rcItem
.bottom
);
4147 iterator_destroy(&j
);
4149 /* draw the horizontial lines for the rows */
4150 itemheight
= LISTVIEW_CalculateItemHeight(infoPtr
);
4151 rcItem
.left
= infoPtr
->rcList
.left
+ Origin
.x
;
4152 rcItem
.right
= infoPtr
->rcList
.right
+ Origin
.x
;
4153 rcItem
.bottom
= rcItem
.top
= Origin
.y
- 1;
4154 MoveToEx(hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4155 LineTo(hdc
, rcItem
.right
, rcItem
.top
);
4156 for(y
=itemheight
-1+Origin
.y
; y
<=infoPtr
->rcList
.bottom
; y
+=itemheight
)
4158 rcItem
.bottom
= rcItem
.top
= y
;
4159 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem
));
4160 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4161 LineTo (hdc
, rcItem
.right
, rcItem
.top
);
4164 SelectObject( hdc
, hOldPen
);
4165 DeleteObject( hPen
);
4171 * Draws listview items when in list display mode.
4174 * [I] infoPtr : valid pointer to the listview structure
4175 * [I] hdc : device context handle
4176 * [I] cdmode : custom draw mode
4181 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4183 POINT Origin
, Position
;
4185 /* Get scroll info once before loop */
4186 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4188 while(iterator_prev(i
))
4190 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4191 Position
.x
+= Origin
.x
;
4192 Position
.y
+= Origin
.y
;
4194 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, 0, Position
, cdmode
);
4201 * Draws listview items.
4204 * [I] infoPtr : valid pointer to the listview structure
4205 * [I] hdc : device context handle
4206 * [I] prcErase : rect to be erased before refresh (may be NULL)
4211 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*prcErase
)
4213 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4214 COLORREF oldTextColor
= 0, oldBkColor
= 0, oldClrTextBk
, oldClrText
;
4215 NMLVCUSTOMDRAW nmlvcd
;
4222 HBITMAP hbmp
= NULL
;
4224 LISTVIEW_DUMP(infoPtr
);
4226 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
4227 TRACE("double buffering\n");
4229 hdc
= CreateCompatibleDC(hdcOrig
);
4231 ERR("Failed to create DC for backbuffer\n");
4234 hbmp
= CreateCompatibleBitmap(hdcOrig
, infoPtr
->rcList
.right
,
4235 infoPtr
->rcList
.bottom
);
4237 ERR("Failed to create bitmap for backbuffer\n");
4242 SelectObject(hdc
, hbmp
);
4243 SelectObject(hdc
, infoPtr
->hFont
);
4245 /* Save dc values we're gonna trash while drawing
4246 * FIXME: Should be done in LISTVIEW_DrawItem() */
4247 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
4248 oldBkMode
= GetBkMode(hdc
);
4249 oldBkColor
= GetBkColor(hdc
);
4250 oldTextColor
= GetTextColor(hdc
);
4253 infoPtr
->bIsDrawing
= TRUE
;
4256 LISTVIEW_FillBkgnd(infoPtr
, hdc
, prcErase
);
4257 } else if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
4258 /* If no erasing was done (usually because RedrawWindow was called
4259 * with RDW_INVALIDATE only) we need to copy the old contents into
4260 * the backbuffer before continuing. */
4261 BitBlt(hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
4262 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
4263 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
4264 hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
4267 /* FIXME: Shouldn't need to do this */
4268 oldClrTextBk
= infoPtr
->clrTextBk
;
4269 oldClrText
= infoPtr
->clrText
;
4271 infoPtr
->cditemmode
= CDRF_DODEFAULT
;
4273 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
4274 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
4275 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4276 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
4277 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
4279 /* Use these colors to draw the items */
4280 infoPtr
->clrTextBk
= nmlvcd
.clrTextBk
;
4281 infoPtr
->clrText
= nmlvcd
.clrText
;
4283 /* nothing to draw */
4284 if(infoPtr
->nItemCount
== 0) goto enddraw
;
4286 /* figure out what we need to draw */
4287 iterator_visibleitems(&i
, infoPtr
, hdc
);
4289 /* send cache hint notification */
4290 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4292 RANGE range
= iterator_range(&i
);
4295 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
4296 nmlv
.iFrom
= range
.lower
;
4297 nmlv
.iTo
= range
.upper
- 1;
4298 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
4301 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
4302 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
4305 if (uView
== LVS_REPORT
)
4306 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
4307 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4308 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
4310 /* if we have a focus rect, draw it */
4311 if (infoPtr
->bFocus
)
4312 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
4314 iterator_destroy(&i
);
4317 /* For LVS_EX_GRIDLINES go and draw lines */
4318 /* This includes the case where there were *no* items */
4319 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
&&
4320 infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
4321 LISTVIEW_RefreshReportGrid(infoPtr
, hdc
);
4323 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
4324 notify_postpaint(infoPtr
, &nmlvcd
);
4326 infoPtr
->clrTextBk
= oldClrTextBk
;
4327 infoPtr
->clrText
= oldClrText
;
4330 BitBlt(hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
4331 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
4332 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
4333 hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
4338 SelectObject(hdc
, hOldFont
);
4339 SetBkMode(hdc
, oldBkMode
);
4340 SetBkColor(hdc
, oldBkColor
);
4341 SetTextColor(hdc
, oldTextColor
);
4344 infoPtr
->bIsDrawing
= FALSE
;
4350 * Calculates the approximate width and height of a given number of items.
4353 * [I] infoPtr : valid pointer to the listview structure
4354 * [I] nItemCount : number of items
4355 * [I] wWidth : width
4356 * [I] wHeight : height
4359 * Returns a DWORD. The width in the low word and the height in high word.
4361 static DWORD
LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
4362 WORD wWidth
, WORD wHeight
)
4364 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4365 INT nItemCountPerColumn
= 1;
4366 INT nColumnCount
= 0;
4367 DWORD dwViewRect
= 0;
4369 if (nItemCount
== -1)
4370 nItemCount
= infoPtr
->nItemCount
;
4372 if (uView
== LVS_LIST
)
4374 if (wHeight
== 0xFFFF)
4376 /* use current height */
4377 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4380 if (wHeight
< infoPtr
->nItemHeight
)
4381 wHeight
= infoPtr
->nItemHeight
;
4385 if (infoPtr
->nItemHeight
> 0)
4387 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
4388 if (nItemCountPerColumn
== 0)
4389 nItemCountPerColumn
= 1;
4391 if (nItemCount
% nItemCountPerColumn
!= 0)
4392 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
4394 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
4398 /* Microsoft padding magic */
4399 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
4400 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
4402 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4404 else if (uView
== LVS_REPORT
)
4408 if (infoPtr
->nItemCount
> 0)
4410 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
4411 wWidth
= rcBox
.right
- rcBox
.left
;
4412 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
4416 /* use current height and width */
4417 if (wHeight
== 0xffff)
4418 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4419 if (wWidth
== 0xffff)
4420 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
4423 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4425 else if (uView
== LVS_SMALLICON
)
4426 FIXME("uView == LVS_SMALLICON: not implemented\n");
4427 else if (uView
== LVS_ICON
)
4428 FIXME("uView == LVS_ICON: not implemented\n");
4436 * Create a drag image list for the specified item.
4439 * [I] infoPtr : valid pointer to the listview structure
4440 * [I] iItem : index of item
4441 * [O] lppt : Upperr-left corner of the image
4444 * Returns a handle to the image list if successful, NULL otherwise.
4446 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
4452 HBITMAP hbmp
, hOldbmp
;
4453 HIMAGELIST dragList
= 0;
4454 TRACE("iItem=%d Count=%d\n", iItem
, infoPtr
->nItemCount
);
4456 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
)
4459 rcItem
.left
= LVIR_BOUNDS
;
4460 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
4463 lppt
->x
= rcItem
.left
;
4464 lppt
->y
= rcItem
.top
;
4466 size
.cx
= rcItem
.right
- rcItem
.left
;
4467 size
.cy
= rcItem
.bottom
- rcItem
.top
;
4469 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
4470 hdc
= CreateCompatibleDC(hdcOrig
);
4471 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
4472 hOldbmp
= SelectObject(hdc
, hbmp
);
4474 rcItem
.left
= rcItem
.top
= 0;
4475 rcItem
.right
= size
.cx
;
4476 rcItem
.bottom
= size
.cy
;
4477 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
4480 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, 0, pos
, infoPtr
->cditemmode
))
4482 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
4483 SelectObject(hdc
, hOldbmp
);
4484 ImageList_Add(dragList
, hbmp
, 0);
4487 SelectObject(hdc
, hOldbmp
);
4491 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
4493 TRACE("ret=%p\n", dragList
);
4501 * Removes all listview items and subitems.
4504 * [I] infoPtr : valid pointer to the listview structure
4510 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
, BOOL destroy
)
4513 HDPA hdpaSubItems
= NULL
;
4520 /* we do it directly, to avoid notifications */
4521 ranges_clear(infoPtr
->selectionRanges
);
4522 infoPtr
->nSelectionMark
= -1;
4523 infoPtr
->nFocusedItem
= -1;
4524 SetRectEmpty(&infoPtr
->rcFocus
);
4525 /* But we are supposed to leave nHotItem as is! */
4528 /* send LVN_DELETEALLITEMS notification */
4529 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4531 bSuppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
4533 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
4535 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4537 /* send LVN_DELETEITEM notification, if not suppressed
4538 and if it is not a virtual listview */
4539 if (!bSuppress
) notify_deleteitem(infoPtr
, i
);
4540 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
4541 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
4543 hdrItem
= DPA_GetPtr(hdpaSubItems
, j
);
4544 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4547 DPA_Destroy(hdpaSubItems
);
4548 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
4550 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
4551 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
4552 infoPtr
->nItemCount
--;
4557 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4558 LISTVIEW_UpdateScroll(infoPtr
);
4560 LISTVIEW_InvalidateList(infoPtr
);
4567 * Scrolls, and updates the columns, when a column is changing width.
4570 * [I] infoPtr : valid pointer to the listview structure
4571 * [I] nColumn : column to scroll
4572 * [I] dx : amount of scroll, in pixels
4577 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
4579 COLUMN_INFO
*lpColumnInfo
;
4584 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
4585 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
4586 rcCol
= lpColumnInfo
->rcHeader
;
4587 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
4588 rcCol
.left
= rcCol
.right
;
4590 /* adjust the other columns */
4591 for (nCol
= nColumn
; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
4593 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
4594 lpColumnInfo
->rcHeader
.left
+= dx
;
4595 lpColumnInfo
->rcHeader
.right
+= dx
;
4598 /* do not update screen if not in report mode */
4599 if (!is_redrawing(infoPtr
) || (infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return;
4601 /* if we have a focus, we must first erase the focus rect */
4602 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
4604 /* Need to reset the item width when inserting a new column */
4605 infoPtr
->nItemWidth
+= dx
;
4607 LISTVIEW_UpdateScroll(infoPtr
);
4608 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
4610 /* scroll to cover the deleted column, and invalidate for redraw */
4611 rcOld
= infoPtr
->rcList
;
4612 rcOld
.left
= ptOrigin
.x
+ rcCol
.left
+ dx
;
4613 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4615 /* we can restore focus now */
4616 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
4621 * Removes a column from the listview control.
4624 * [I] infoPtr : valid pointer to the listview structure
4625 * [I] nColumn : column index
4631 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4635 TRACE("nColumn=%d\n", nColumn
);
4637 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0
4638 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4640 /* While the MSDN specifically says that column zero should not be deleted,
4641 what actually happens is that the column itself is deleted but no items or subitems
4645 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
4647 if (!Header_DeleteItem(infoPtr
->hwndHeader
, nColumn
))
4650 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
4651 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
4653 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
4655 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
4657 INT nItem
, nSubItem
, i
;
4659 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
4661 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
4664 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4666 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
4667 if (lpSubItem
->iSubItem
== nColumn
)
4670 lpDelItem
= lpSubItem
;
4672 else if (lpSubItem
->iSubItem
> nColumn
)
4674 lpSubItem
->iSubItem
--;
4678 /* if we found our subitem, zapp it */
4682 if (is_textW(lpDelItem
->hdr
.pszText
))
4683 Free(lpDelItem
->hdr
.pszText
);
4688 /* free dpa memory */
4689 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
4694 /* update the other column info */
4695 LISTVIEW_UpdateItemSize(infoPtr
);
4696 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
4697 LISTVIEW_InvalidateList(infoPtr
);
4699 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
4706 * Invalidates the listview after an item's insertion or deletion.
4709 * [I] infoPtr : valid pointer to the listview structure
4710 * [I] nItem : item index
4711 * [I] dir : -1 if deleting, 1 if inserting
4716 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
4718 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4719 INT nPerCol
, nItemCol
, nItemRow
;
4723 /* if we don't refresh, what's the point of scrolling? */
4724 if (!is_redrawing(infoPtr
)) return;
4726 assert (abs(dir
) == 1);
4728 /* arrange icons if autoarrange is on */
4729 if (is_autoarrange(infoPtr
))
4731 BOOL arrange
= TRUE
;
4732 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
4733 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
4734 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4737 /* scrollbars need updating */
4738 LISTVIEW_UpdateScroll(infoPtr
);
4740 /* figure out the item's position */
4741 if (uView
== LVS_REPORT
)
4742 nPerCol
= infoPtr
->nItemCount
+ 1;
4743 else if (uView
== LVS_LIST
)
4744 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
4745 else /* LVS_ICON, or LVS_SMALLICON */
4748 nItemCol
= nItem
/ nPerCol
;
4749 nItemRow
= nItem
% nPerCol
;
4750 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4752 /* move the items below up a slot */
4753 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
4754 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
4755 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
4756 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4757 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4758 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
4759 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4761 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll
), wine_dbgstr_rect(&infoPtr
->rcList
));
4762 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4763 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4766 /* report has only that column, so we're done */
4767 if (uView
== LVS_REPORT
) return;
4769 /* now for LISTs, we have to deal with the columns to the right */
4770 rcScroll
.left
= (nItemCol
+ 1) * infoPtr
->nItemWidth
;
4772 rcScroll
.right
= (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
4773 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4774 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4775 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4776 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4777 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4782 * Removes an item from the listview control.
4785 * [I] infoPtr : valid pointer to the listview structure
4786 * [I] nItem : item index
4792 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
4795 const UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4796 const BOOL is_icon
= (uView
== LVS_SMALLICON
|| uView
== LVS_ICON
);
4798 TRACE("(nItem=%d)\n", nItem
);
4800 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
4802 /* remove selection, and focus */
4804 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4805 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
4807 /* send LVN_DELETEITEM notification. */
4808 if (!notify_deleteitem(infoPtr
, nItem
)) return FALSE
;
4810 /* we need to do this here, because we'll be deleting stuff */
4812 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4814 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4820 hdpaSubItems
= DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
4821 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4823 hdrItem
= DPA_GetPtr(hdpaSubItems
, i
);
4824 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4827 DPA_Destroy(hdpaSubItems
);
4832 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
4833 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
4836 infoPtr
->nItemCount
--;
4837 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
4839 /* now is the invalidation fun */
4841 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
4848 * Callback implementation for editlabel control
4851 * [I] infoPtr : valid pointer to the listview structure
4852 * [I] pszText : modified text
4853 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4859 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, LPWSTR pszText
, BOOL isW
)
4861 HWND hwndSelf
= infoPtr
->hwndSelf
;
4862 NMLVDISPINFOW dispInfo
;
4863 INT editedItem
= infoPtr
->nEditLabelItem
;
4866 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
4868 infoPtr
->nEditLabelItem
= -1;
4870 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4871 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4872 dispInfo
.item
.iItem
= editedItem
;
4873 dispInfo
.item
.iSubItem
= 0;
4874 dispInfo
.item
.stateMask
= ~0;
4875 if (!LISTVIEW_GetItemW(infoPtr
, &dispInfo
.item
)) return FALSE
;
4878 bSame
= (lstrcmpW(dispInfo
.item
.pszText
, pszText
) == 0);
4881 LPWSTR tmp
= textdupTtoW(pszText
, FALSE
);
4882 bSame
= (lstrcmpW(dispInfo
.item
.pszText
, tmp
) == 0);
4883 textfreeT(tmp
, FALSE
);
4885 if (bSame
) return TRUE
;
4887 /* add the text from the edit in */
4888 dispInfo
.item
.mask
|= LVIF_TEXT
;
4889 dispInfo
.item
.pszText
= pszText
;
4890 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4892 /* Do we need to update the Item Text */
4893 if (!notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
)) return FALSE
;
4894 if (!IsWindow(hwndSelf
))
4896 if (!pszText
) return TRUE
;
4898 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4900 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, editedItem
);
4901 ITEM_INFO
* lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
4902 if (lpItem
&& lpItem
->hdr
.pszText
== LPSTR_TEXTCALLBACKW
)
4904 LISTVIEW_InvalidateItem(infoPtr
, editedItem
);
4909 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4910 dispInfo
.item
.mask
= LVIF_TEXT
;
4911 dispInfo
.item
.iItem
= editedItem
;
4912 dispInfo
.item
.iSubItem
= 0;
4913 dispInfo
.item
.pszText
= pszText
;
4914 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4915 return LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
4920 * Begin in place editing of specified list view item
4923 * [I] infoPtr : valid pointer to the listview structure
4924 * [I] nItem : item index
4925 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4931 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
4933 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
4934 NMLVDISPINFOW dispInfo
;
4936 HWND hwndSelf
= infoPtr
->hwndSelf
;
4938 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
4940 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
4941 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
4943 infoPtr
->nEditLabelItem
= nItem
;
4945 /* Is the EditBox still there, if so remove it */
4946 if(infoPtr
->hwndEdit
!= 0)
4948 SetFocus(infoPtr
->hwndSelf
);
4949 infoPtr
->hwndEdit
= 0;
4952 LISTVIEW_SetSelection(infoPtr
, nItem
);
4953 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
4954 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4956 rect
.left
= LVIR_LABEL
;
4957 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
4959 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4960 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4961 dispInfo
.item
.iItem
= nItem
;
4962 dispInfo
.item
.iSubItem
= 0;
4963 dispInfo
.item
.stateMask
= ~0;
4964 dispInfo
.item
.pszText
= szDispText
;
4965 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
4966 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
4968 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, WS_VISIBLE
,
4969 rect
.left
-2, rect
.top
-1, 0, rect
.bottom
- rect
.top
+2, isW
);
4970 if (!infoPtr
->hwndEdit
) return 0;
4972 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
4974 if (!IsWindow(hwndSelf
))
4976 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
4977 infoPtr
->hwndEdit
= 0;
4981 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
4982 SetFocus(infoPtr
->hwndEdit
);
4983 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
4984 return infoPtr
->hwndEdit
;
4990 * Ensures the specified item is visible, scrolling into view if necessary.
4993 * [I] infoPtr : valid pointer to the listview structure
4994 * [I] nItem : item index
4995 * [I] bPartial : partially or entirely visible
5001 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
5003 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5004 INT nScrollPosHeight
= 0;
5005 INT nScrollPosWidth
= 0;
5006 INT nHorzAdjust
= 0;
5007 INT nVertAdjust
= 0;
5010 RECT rcItem
, rcTemp
;
5012 rcItem
.left
= LVIR_BOUNDS
;
5013 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
5015 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
5017 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
5019 /* scroll left/right, but in LVS_REPORT mode */
5020 if (uView
== LVS_LIST
)
5021 nScrollPosWidth
= infoPtr
->nItemWidth
;
5022 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5023 nScrollPosWidth
= 1;
5025 if (rcItem
.left
< infoPtr
->rcList
.left
)
5028 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
5033 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
5037 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
5039 /* scroll up/down, but not in LVS_LIST mode */
5040 if (uView
== LVS_REPORT
)
5041 nScrollPosHeight
= infoPtr
->nItemHeight
;
5042 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
5043 nScrollPosHeight
= 1;
5045 if (rcItem
.top
< infoPtr
->rcList
.top
)
5048 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
5053 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
5057 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
5059 if (nScrollPosWidth
)
5061 INT diff
= nHorzDiff
/ nScrollPosWidth
;
5062 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
5063 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
5066 if (nScrollPosHeight
)
5068 INT diff
= nVertDiff
/ nScrollPosHeight
;
5069 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
5070 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
5078 * Searches for an item with specific characteristics.
5081 * [I] hwnd : window handle
5082 * [I] nStart : base item index
5083 * [I] lpFindInfo : item information to look for
5086 * SUCCESS : index of item
5089 static INT
LISTVIEW_FindItemW(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
5090 const LVFINDINFOW
*lpFindInfo
)
5092 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5093 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5094 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
5095 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
5096 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
5097 POINT Position
, Destination
;
5100 /* Search in virtual listviews should be done by application, not by
5101 listview control, so we just send LVN_ODFINDITEMW and return the result */
5102 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5106 nmlv
.iStart
= nStart
;
5107 nmlv
.lvfi
= *lpFindInfo
;
5108 return notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
5111 if (!lpFindInfo
|| nItem
< 0) return -1;
5114 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
))
5116 lvItem
.mask
|= LVIF_TEXT
;
5117 lvItem
.pszText
= szDispText
;
5118 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5121 if (lpFindInfo
->flags
& LVFI_WRAP
)
5124 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
5125 (uView
== LVS_ICON
|| uView
==LVS_SMALLICON
))
5130 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5131 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
5132 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
5133 switch(lpFindInfo
->vkDirection
)
5135 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
5136 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
5137 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
5138 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
5139 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
5140 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
5141 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
5143 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
5144 Destination
.x
= rcArea
.right
;
5145 Destination
.y
= rcArea
.bottom
;
5147 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
5151 else Destination
.x
= Destination
.y
= 0;
5153 /* if LVFI_PARAM is specified, all other flags are ignored */
5154 if (lpFindInfo
->flags
& LVFI_PARAM
)
5156 lvItem
.mask
|= LVIF_PARAM
;
5158 lvItem
.mask
&= ~LVIF_TEXT
;
5162 for (; nItem
< nLast
; nItem
++)
5164 lvItem
.iItem
= nItem
;
5165 lvItem
.iSubItem
= 0;
5166 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
5168 if (lvItem
.mask
& LVIF_PARAM
)
5170 if (lpFindInfo
->lParam
== lvItem
.lParam
)
5176 if (lvItem
.mask
& LVIF_TEXT
)
5178 if (lpFindInfo
->flags
& LVFI_PARTIAL
)
5180 if (strstrW(lvItem
.pszText
, lpFindInfo
->psz
) == NULL
) continue;
5184 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
5188 if (!bNearest
) return nItem
;
5190 /* This is very inefficient. To do a good job here,
5191 * we need a sorted array of (x,y) item positions */
5192 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5194 /* compute the distance^2 to the destination */
5195 xdist
= Destination
.x
- Position
.x
;
5196 ydist
= Destination
.y
- Position
.y
;
5197 dist
= xdist
* xdist
+ ydist
* ydist
;
5199 /* remember the distance, and item if it's closer */
5203 nNearestItem
= nItem
;
5210 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
5215 return nNearestItem
;
5220 * Searches for an item with specific characteristics.
5223 * [I] hwnd : window handle
5224 * [I] nStart : base item index
5225 * [I] lpFindInfo : item information to look for
5228 * SUCCESS : index of item
5231 static INT
LISTVIEW_FindItemA(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
5232 const LVFINDINFOA
*lpFindInfo
)
5234 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
);
5239 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
5240 if (hasText
) fiw
.psz
= strW
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
5241 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
5242 textfreeT(strW
, FALSE
);
5248 * Retrieves the background image of the listview control.
5251 * [I] infoPtr : valid pointer to the listview structure
5252 * [O] lpBkImage : background image attributes
5258 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5260 /* FIXME (listview, "empty stub!\n"); */
5266 * Retrieves column attributes.
5269 * [I] infoPtr : valid pointer to the listview structure
5270 * [I] nColumn : column index
5271 * [IO] lpColumn : column information
5272 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5273 * otherwise it is in fact a LPLVCOLUMNA
5279 static BOOL
LISTVIEW_GetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
5281 COLUMN_INFO
*lpColumnInfo
;
5284 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5285 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
5287 /* initialize memory */
5288 ZeroMemory(&hdi
, sizeof(hdi
));
5290 if (lpColumn
->mask
& LVCF_TEXT
)
5292 hdi
.mask
|= HDI_TEXT
;
5293 hdi
.pszText
= lpColumn
->pszText
;
5294 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
5297 if (lpColumn
->mask
& LVCF_IMAGE
)
5298 hdi
.mask
|= HDI_IMAGE
;
5300 if (lpColumn
->mask
& LVCF_ORDER
)
5301 hdi
.mask
|= HDI_ORDER
;
5303 if (lpColumn
->mask
& LVCF_SUBITEM
)
5304 hdi
.mask
|= HDI_LPARAM
;
5306 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
5308 if (lpColumn
->mask
& LVCF_FMT
)
5309 lpColumn
->fmt
= lpColumnInfo
->fmt
;
5311 if (lpColumn
->mask
& LVCF_WIDTH
)
5312 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
5314 if (lpColumn
->mask
& LVCF_IMAGE
)
5315 lpColumn
->iImage
= hdi
.iImage
;
5317 if (lpColumn
->mask
& LVCF_ORDER
)
5318 lpColumn
->iOrder
= hdi
.iOrder
;
5320 if (lpColumn
->mask
& LVCF_SUBITEM
)
5321 lpColumn
->iSubItem
= hdi
.lParam
;
5327 static BOOL
LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
5334 /* FIXME: little hack */
5335 for (i
= 0; i
< iCount
; i
++)
5343 * Retrieves the column width.
5346 * [I] infoPtr : valid pointer to the listview structure
5347 * [I] int : column index
5350 * SUCCESS : column width
5353 static INT
LISTVIEW_GetColumnWidth(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
5355 INT nColumnWidth
= 0;
5358 TRACE("nColumn=%d\n", nColumn
);
5360 /* we have a 'column' in LIST and REPORT mode only */
5361 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
)
5364 nColumnWidth
= infoPtr
->nItemWidth
;
5367 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5368 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5369 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5371 * TODO: should we do the same in LVM_GETCOLUMN?
5373 hdItem
.mask
= HDI_WIDTH
;
5374 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdItem
))
5376 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr
->hwndSelf
, nColumn
);
5379 nColumnWidth
= hdItem
.cxy
;
5383 TRACE("nColumnWidth=%d\n", nColumnWidth
);
5384 return nColumnWidth
;
5389 * In list or report display mode, retrieves the number of items that can fit
5390 * vertically in the visible area. In icon or small icon display mode,
5391 * retrieves the total number of visible items.
5394 * [I] infoPtr : valid pointer to the listview structure
5397 * Number of fully visible items.
5399 static INT
LISTVIEW_GetCountPerPage(const LISTVIEW_INFO
*infoPtr
)
5401 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
5405 return infoPtr
->nItemCount
;
5407 return LISTVIEW_GetCountPerColumn(infoPtr
);
5409 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
5417 * Retrieves an image list handle.
5420 * [I] infoPtr : valid pointer to the listview structure
5421 * [I] nImageList : image list identifier
5424 * SUCCESS : image list handle
5427 static HIMAGELIST
LISTVIEW_GetImageList(const LISTVIEW_INFO
*infoPtr
, INT nImageList
)
5431 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
5432 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
5433 case LVSIL_STATE
: return infoPtr
->himlState
;
5438 /* LISTVIEW_GetISearchString */
5442 * Retrieves item attributes.
5445 * [I] hwnd : window handle
5446 * [IO] lpLVItem : item info
5447 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5448 * if FALSE, then lpLVItem is a LPLVITEMA.
5451 * This is the internal 'GetItem' interface -- it tries to
5452 * be smart and avoid text copies, if possible, by modifying
5453 * lpLVItem->pszText to point to the text string. Please note
5454 * that this is not always possible (e.g. OWNERDATA), so on
5455 * entry you *must* supply valid values for pszText, and cchTextMax.
5456 * The only difference to the documented interface is that upon
5457 * return, you should use *only* the lpLVItem->pszText, rather than
5458 * the buffer pointer you provided on input. Most code already does
5459 * that, so it's not a problem.
5460 * For the two cases when the text must be copied (that is,
5461 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5467 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5469 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
5470 NMLVDISPINFOW dispInfo
;
5476 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
5478 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5481 if (lpLVItem
->mask
== 0) return TRUE
;
5483 /* make a local copy */
5484 isubitem
= lpLVItem
->iSubItem
;
5486 /* a quick optimization if all we're asked is the focus state
5487 * these queries are worth optimising since they are common,
5488 * and can be answered in constant time, without the heavy accesses */
5489 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
5490 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
5492 lpLVItem
->state
= 0;
5493 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5494 lpLVItem
->state
|= LVIS_FOCUSED
;
5498 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5500 /* if the app stores all the data, handle it separately */
5501 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5503 dispInfo
.item
.state
= 0;
5505 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5506 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) || infoPtr
->uCallbackMask
)
5508 /* NOTE: copy only fields which we _know_ are initialized, some apps
5509 * depend on the uninitialized fields being 0 */
5510 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
5511 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5512 dispInfo
.item
.iSubItem
= isubitem
;
5513 if (lpLVItem
->mask
& LVIF_TEXT
)
5515 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5516 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5518 if (lpLVItem
->mask
& LVIF_STATE
)
5519 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
5520 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5521 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
5522 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
5524 /* full size structure expected - _WIN32IE >= 0x560 */
5525 *lpLVItem
= dispInfo
.item
;
5527 else if (lpLVItem
->mask
& LVIF_INDENT
)
5529 /* indent member expected - _WIN32IE >= 0x300 */
5530 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
5534 /* minimal structure expected */
5535 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
5537 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
5540 /* make sure lParam is zeroed out */
5541 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
5543 /* we store only a little state, so if we're not asked, we're done */
5544 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
5546 /* if focus is handled by us, report it */
5547 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5549 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5550 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5551 lpLVItem
->state
|= LVIS_FOCUSED
;
5554 /* and do the same for selection, if we handle it */
5555 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5557 lpLVItem
->state
&= ~LVIS_SELECTED
;
5558 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5559 lpLVItem
->state
|= LVIS_SELECTED
;
5565 /* find the item and subitem structures before we proceed */
5566 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
5567 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5572 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
5573 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
5576 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
5581 pItemHdr
= &lpItem
->hdr
;
5583 /* Do we need to query the state from the app? */
5584 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
5586 dispInfo
.item
.mask
|= LVIF_STATE
;
5587 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
5590 /* Do we need to enquire about the image? */
5591 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
5592 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
5594 dispInfo
.item
.mask
|= LVIF_IMAGE
;
5595 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
5598 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5599 if ((lpLVItem
->mask
& LVIF_TEXT
) && !is_textW(pItemHdr
->pszText
))
5601 dispInfo
.item
.mask
|= LVIF_TEXT
;
5602 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5603 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5604 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
5605 *dispInfo
.item
.pszText
= '\0';
5608 /* If we don't have all the requested info, query the application */
5609 if (dispInfo
.item
.mask
!= 0)
5611 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5612 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
5613 dispInfo
.item
.lParam
= lpItem
->lParam
;
5614 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5615 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
5618 /* we should not store values for subitems */
5619 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
5621 /* Now, handle the iImage field */
5622 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
5624 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
5625 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
5626 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
5628 else if (lpLVItem
->mask
& LVIF_IMAGE
)
5630 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
5631 lpLVItem
->iImage
= pItemHdr
->iImage
;
5633 lpLVItem
->iImage
= 0;
5636 /* The pszText field */
5637 if (dispInfo
.item
.mask
& LVIF_TEXT
)
5639 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
5640 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
5642 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
5644 else if (lpLVItem
->mask
& LVIF_TEXT
)
5646 if (isW
) lpLVItem
->pszText
= pItemHdr
->pszText
;
5647 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
5650 /* Next is the lParam field */
5651 if (dispInfo
.item
.mask
& LVIF_PARAM
)
5653 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
5654 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
5655 lpItem
->lParam
= dispInfo
.item
.lParam
;
5657 else if (lpLVItem
->mask
& LVIF_PARAM
)
5658 lpLVItem
->lParam
= lpItem
->lParam
;
5660 /* if this is a subitem, we're done */
5661 if (isubitem
) return TRUE
;
5663 /* ... the state field (this one is different due to uCallbackmask) */
5664 if (lpLVItem
->mask
& LVIF_STATE
)
5666 lpLVItem
->state
= lpItem
->state
& lpLVItem
->stateMask
;
5667 if (dispInfo
.item
.mask
& LVIF_STATE
)
5669 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
5670 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
5672 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5674 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5675 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5676 lpLVItem
->state
|= LVIS_FOCUSED
;
5678 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5680 lpLVItem
->state
&= ~LVIS_SELECTED
;
5681 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5682 lpLVItem
->state
|= LVIS_SELECTED
;
5686 /* and last, but not least, the indent field */
5687 if (lpLVItem
->mask
& LVIF_INDENT
)
5688 lpLVItem
->iIndent
= lpItem
->iIndent
;
5695 * Retrieves item attributes.
5698 * [I] hwnd : window handle
5699 * [IO] lpLVItem : item info
5700 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5701 * if FALSE, then lpLVItem is a LPLVITEMA.
5704 * This is the external 'GetItem' interface -- it properly copies
5705 * the text in the provided buffer.
5711 static BOOL
LISTVIEW_GetItemExtT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5716 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5719 pszText
= lpLVItem
->pszText
;
5720 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
5721 if (bResult
&& lpLVItem
->pszText
!= pszText
)
5722 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
5723 lpLVItem
->pszText
= pszText
;
5731 * Retrieves the position (upper-left) of the listview control item.
5732 * Note that for LVS_ICON style, the upper-left is that of the icon
5733 * and not the bounding box.
5736 * [I] infoPtr : valid pointer to the listview structure
5737 * [I] nItem : item index
5738 * [O] lpptPosition : coordinate information
5744 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
5746 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5749 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
5751 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5753 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5754 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
5756 if (uView
== LVS_ICON
)
5758 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
5759 lpptPosition
->y
+= ICON_TOP_PADDING
;
5761 lpptPosition
->x
+= Origin
.x
;
5762 lpptPosition
->y
+= Origin
.y
;
5764 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition
));
5771 * Retrieves the bounding rectangle for a listview control item.
5774 * [I] infoPtr : valid pointer to the listview structure
5775 * [I] nItem : item index
5776 * [IO] lprc : bounding rectangle coordinates
5777 * lprc->left specifies the portion of the item for which the bounding
5778 * rectangle will be retrieved.
5780 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5781 * including the icon and label.
5784 * * Experiment shows that native control returns:
5785 * * width = min (48, length of text line)
5786 * * .left = position.x - (width - iconsize.cx)/2
5787 * * .right = .left + width
5788 * * height = #lines of text * ntmHeight + icon height + 8
5789 * * .top = position.y - 2
5790 * * .bottom = .top + height
5791 * * separation between items .y = itemSpacing.cy - height
5792 * * .x = itemSpacing.cx - width
5793 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5796 * * Experiment shows that native control returns:
5797 * * width = iconSize.cx + 16
5798 * * .left = position.x - (width - iconsize.cx)/2
5799 * * .right = .left + width
5800 * * height = iconSize.cy + 4
5801 * * .top = position.y - 2
5802 * * .bottom = .top + height
5803 * * separation between items .y = itemSpacing.cy - height
5804 * * .x = itemSpacing.cx - width
5805 * LVIR_LABEL Returns the bounding rectangle of the item text.
5808 * * Experiment shows that native control returns:
5809 * * width = text length
5810 * * .left = position.x - width/2
5811 * * .right = .left + width
5812 * * height = ntmH * linecount + 2
5813 * * .top = position.y + iconSize.cy + 6
5814 * * .bottom = .top + height
5815 * * separation between items .y = itemSpacing.cy - height
5816 * * .x = itemSpacing.cx - width
5817 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5818 * rectangles, but excludes columns in report view.
5825 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5826 * upon whether the window has the focus currently and on whether the item
5827 * is the one with the focus. Ensure that the control's record of which
5828 * item has the focus agrees with the items' records.
5830 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5832 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5833 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5834 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
5835 POINT Position
, Origin
;
5838 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
5840 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5842 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5843 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5845 /* Be smart and try to figure out the minimum we have to do */
5846 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
5847 if (uView
== LVS_REPORT
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
5848 if (uView
== LVS_ICON
&& lprc
->left
!= LVIR_ICON
&&
5849 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
5850 oversizedBox
= TRUE
;
5852 /* get what we need from the item before hand, so we make
5853 * only one request. This can speed up things, if data
5854 * is stored on the app side */
5856 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
5857 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
5858 lvItem
.iItem
= nItem
;
5859 lvItem
.iSubItem
= 0;
5860 lvItem
.pszText
= szDispText
;
5861 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5862 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5863 /* we got the state already up, simulate it here, to avoid a reget */
5864 if (uView
== LVS_ICON
&& (lprc
->left
!= LVIR_ICON
))
5866 lvItem
.mask
|= LVIF_STATE
;
5867 lvItem
.stateMask
= LVIS_FOCUSED
;
5868 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
5871 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
5872 lprc
->left
= LVIR_BOUNDS
;
5876 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
5880 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, NULL
, lprc
);
5884 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
5887 case LVIR_SELECTBOUNDS
:
5888 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, lprc
, NULL
, NULL
, NULL
);
5892 WARN("Unknown value: %d\n", lprc
->left
);
5896 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
5898 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc
));
5905 * Retrieves the spacing between listview control items.
5908 * [I] infoPtr : valid pointer to the listview structure
5909 * [IO] lprc : rectangle to receive the output
5910 * on input, lprc->top = nSubItem
5911 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5913 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5914 * not only those of the first column.
5915 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5921 static BOOL
LISTVIEW_GetSubItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5927 if (!lprc
) return FALSE
;
5929 nColumn
= lprc
->top
;
5931 TRACE("(nItem=%d, nSubItem=%d)\n", nItem
, lprc
->top
);
5932 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5934 return LISTVIEW_GetItemRect(infoPtr
, nItem
, lprc
);
5936 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return FALSE
;
5938 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &Position
)) return FALSE
;
5940 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5943 lvItem
.iItem
= nItem
;
5944 lvItem
.iSubItem
= nColumn
;
5946 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5950 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
5955 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
5959 ERR("Unknown bounds=%d\n", lprc
->left
);
5963 OffsetRect(lprc
, Position
.x
, Position
.y
);
5970 * Retrieves the width of a label.
5973 * [I] infoPtr : valid pointer to the listview structure
5976 * SUCCESS : string width (in pixels)
5979 static INT
LISTVIEW_GetLabelWidth(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
5981 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5984 TRACE("(nItem=%d)\n", nItem
);
5986 lvItem
.mask
= LVIF_TEXT
;
5987 lvItem
.iItem
= nItem
;
5988 lvItem
.iSubItem
= 0;
5989 lvItem
.pszText
= szDispText
;
5990 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5991 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5993 return LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
5998 * Retrieves the spacing between listview control items.
6001 * [I] infoPtr : valid pointer to the listview structure
6002 * [I] bSmall : flag for small or large icon
6005 * Horizontal + vertical spacing
6007 static LONG
LISTVIEW_GetItemSpacing(const LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
6013 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
6017 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
6018 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
6020 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
6027 * Retrieves the state of a listview control item.
6030 * [I] infoPtr : valid pointer to the listview structure
6031 * [I] nItem : item index
6032 * [I] uMask : state mask
6035 * State specified by the mask.
6037 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
6041 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6043 lvItem
.iItem
= nItem
;
6044 lvItem
.iSubItem
= 0;
6045 lvItem
.mask
= LVIF_STATE
;
6046 lvItem
.stateMask
= uMask
;
6047 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
6049 return lvItem
.state
& uMask
;
6054 * Retrieves the text of a listview control item or subitem.
6057 * [I] hwnd : window handle
6058 * [I] nItem : item index
6059 * [IO] lpLVItem : item information
6060 * [I] isW : TRUE if lpLVItem is Unicode
6063 * SUCCESS : string length
6066 static INT
LISTVIEW_GetItemTextT(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
6068 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6070 lpLVItem
->mask
= LVIF_TEXT
;
6071 lpLVItem
->iItem
= nItem
;
6072 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
6074 return textlenT(lpLVItem
->pszText
, isW
);
6079 * Searches for an item based on properties + relationships.
6082 * [I] infoPtr : valid pointer to the listview structure
6083 * [I] nItem : item index
6084 * [I] uFlags : relationship flag
6087 * SUCCESS : item index
6090 static INT
LISTVIEW_GetNextItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
6092 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6094 LVFINDINFOW lvFindInfo
;
6095 INT nCountPerColumn
;
6099 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
6100 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
6102 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
6104 if (uFlags
& LVNI_CUT
)
6107 if (uFlags
& LVNI_DROPHILITED
)
6108 uMask
|= LVIS_DROPHILITED
;
6110 if (uFlags
& LVNI_FOCUSED
)
6111 uMask
|= LVIS_FOCUSED
;
6113 if (uFlags
& LVNI_SELECTED
)
6114 uMask
|= LVIS_SELECTED
;
6116 /* if we're asked for the focused item, that's only one,
6117 * so it's worth optimizing */
6118 if (uFlags
& LVNI_FOCUSED
)
6120 if ((LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) != uMask
) return -1;
6121 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
6124 if (uFlags
& LVNI_ABOVE
)
6126 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
6131 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6137 /* Special case for autoarrange - move 'til the top of a list */
6138 if (is_autoarrange(infoPtr
))
6140 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6141 while (nItem
- nCountPerRow
>= 0)
6143 nItem
-= nCountPerRow
;
6144 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6149 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6150 lvFindInfo
.vkDirection
= VK_UP
;
6151 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6152 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6154 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6159 else if (uFlags
& LVNI_BELOW
)
6161 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
6163 while (nItem
< infoPtr
->nItemCount
)
6166 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6172 /* Special case for autoarrange - move 'til the bottom of a list */
6173 if (is_autoarrange(infoPtr
))
6175 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6176 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
6178 nItem
+= nCountPerRow
;
6179 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6184 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6185 lvFindInfo
.vkDirection
= VK_DOWN
;
6186 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6187 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6189 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6194 else if (uFlags
& LVNI_TOLEFT
)
6196 if (uView
== LVS_LIST
)
6198 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
6199 while (nItem
- nCountPerColumn
>= 0)
6201 nItem
-= nCountPerColumn
;
6202 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6206 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6208 /* Special case for autoarrange - move 'ti the beginning of a row */
6209 if (is_autoarrange(infoPtr
))
6211 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6212 while (nItem
% nCountPerRow
> 0)
6215 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6220 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6221 lvFindInfo
.vkDirection
= VK_LEFT
;
6222 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6223 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6225 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6230 else if (uFlags
& LVNI_TORIGHT
)
6232 if (uView
== LVS_LIST
)
6234 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
6235 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
6237 nItem
+= nCountPerColumn
;
6238 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6242 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6244 /* Special case for autoarrange - move 'til the end of a row */
6245 if (is_autoarrange(infoPtr
))
6247 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6248 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
6251 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6256 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6257 lvFindInfo
.vkDirection
= VK_RIGHT
;
6258 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6259 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6261 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6270 /* search by index */
6271 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
6273 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
6281 /* LISTVIEW_GetNumberOfWorkAreas */
6285 * Retrieves the origin coordinates when in icon or small icon display mode.
6288 * [I] infoPtr : valid pointer to the listview structure
6289 * [O] lpptOrigin : coordinate information
6294 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
6296 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6297 INT nHorzPos
= 0, nVertPos
= 0;
6298 SCROLLINFO scrollInfo
;
6300 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
6301 scrollInfo
.fMask
= SIF_POS
;
6303 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
6304 nHorzPos
= scrollInfo
.nPos
;
6305 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
6306 nVertPos
= scrollInfo
.nPos
;
6308 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
6310 lpptOrigin
->x
= infoPtr
->rcList
.left
;
6311 lpptOrigin
->y
= infoPtr
->rcList
.top
;
6312 if (uView
== LVS_LIST
)
6313 nHorzPos
*= infoPtr
->nItemWidth
;
6314 else if (uView
== LVS_REPORT
)
6315 nVertPos
*= infoPtr
->nItemHeight
;
6317 lpptOrigin
->x
-= nHorzPos
;
6318 lpptOrigin
->y
-= nVertPos
;
6320 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin
));
6325 * Retrieves the width of a string.
6328 * [I] hwnd : window handle
6329 * [I] lpszText : text string to process
6330 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6333 * SUCCESS : string width (in pixels)
6336 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
6341 if (is_textT(lpszText
, isW
))
6343 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
6344 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
6345 HFONT hOldFont
= SelectObject(hdc
, hFont
);
6348 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
6350 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
6351 SelectObject(hdc
, hOldFont
);
6352 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6354 return stringSize
.cx
;
6359 * Determines which listview item is located at the specified position.
6362 * [I] infoPtr : valid pointer to the listview structure
6363 * [IO] lpht : hit test information
6364 * [I] subitem : fill out iSubItem.
6365 * [I] select : return the index only if the hit selects the item
6368 * (mm 20001022): We must not allow iSubItem to be touched, for
6369 * an app might pass only a structure with space up to iItem!
6370 * (MS Office 97 does that for instance in the file open dialog)
6373 * SUCCESS : item index
6376 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
6378 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6379 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6380 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
6381 POINT Origin
, Position
, opt
;
6386 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht
->pt
), subitem
, select
);
6390 if (subitem
) lpht
->iSubItem
= 0;
6392 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
6393 lpht
->flags
|= LVHT_TOLEFT
;
6394 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
6395 lpht
->flags
|= LVHT_TORIGHT
;
6397 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
6398 lpht
->flags
|= LVHT_ABOVE
;
6399 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
6400 lpht
->flags
|= LVHT_BELOW
;
6402 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6403 if (lpht
->flags
) return -1;
6405 lpht
->flags
|= LVHT_NOWHERE
;
6407 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6409 /* first deal with the large items */
6410 rcSearch
.left
= lpht
->pt
.x
;
6411 rcSearch
.top
= lpht
->pt
.y
;
6412 rcSearch
.right
= rcSearch
.left
+ 1;
6413 rcSearch
.bottom
= rcSearch
.top
+ 1;
6415 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
6416 iterator_next(&i
); /* go to first item in the sequence */
6418 iterator_destroy(&i
);
6420 TRACE("lpht->iItem=%d\n", iItem
);
6421 if (iItem
== -1) return -1;
6423 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
6424 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
6425 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
6426 if (uView
== LVS_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
6427 lvItem
.iItem
= iItem
;
6428 lvItem
.iSubItem
= 0;
6429 lvItem
.pszText
= szDispText
;
6430 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6431 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
6432 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
6434 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
6435 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
6436 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
6437 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
6439 if (uView
== LVS_REPORT
)
6443 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6444 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6446 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds
));
6447 if (!PtInRect(&rcBounds
, opt
)) return -1;
6449 if (PtInRect(&rcIcon
, opt
))
6450 lpht
->flags
|= LVHT_ONITEMICON
;
6451 else if (PtInRect(&rcLabel
, opt
))
6452 lpht
->flags
|= LVHT_ONITEMLABEL
;
6453 else if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && PtInRect(&rcState
, opt
))
6454 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
6455 if (lpht
->flags
& LVHT_ONITEM
)
6456 lpht
->flags
&= ~LVHT_NOWHERE
;
6458 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6459 if (uView
== LVS_REPORT
&& subitem
)
6463 rcBounds
.right
= rcBounds
.left
;
6464 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
6466 rcBounds
.left
= rcBounds
.right
;
6467 rcBounds
.right
+= LISTVIEW_GetColumnWidth(infoPtr
, j
);
6468 if (PtInRect(&rcBounds
, opt
))
6476 if (select
&& !(uView
== LVS_REPORT
&&
6477 ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) ||
6478 (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))))
6480 if (uView
== LVS_REPORT
)
6482 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6483 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6485 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
6487 return lpht
->iItem
= iItem
;
6491 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6492 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6493 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6494 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6495 their own sort proc. when sending LVM_SORTITEMS.
6498 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6500 LVS_SORTXXX must be specified,
6501 LVS_OWNERDRAW is not set,
6502 <item>.pszText is not LPSTR_TEXTCALLBACK.
6504 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6505 are sorted based on item text..."
6507 static INT WINAPI
LISTVIEW_InsertCompare( LPVOID first
, LPVOID second
, LPARAM lParam
)
6509 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
6510 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
6511 INT cmpv
= textcmpWT(lv_first
->hdr
.pszText
, lv_second
->hdr
.pszText
, TRUE
);
6513 /* if we're sorting descending, negate the return value */
6514 return (((const LISTVIEW_INFO
*)lParam
)->dwStyle
& LVS_SORTDESCENDING
) ? -cmpv
: cmpv
;
6519 * Inserts a new item in the listview control.
6522 * [I] infoPtr : valid pointer to the listview structure
6523 * [I] lpLVItem : item information
6524 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6527 * SUCCESS : new item index
6530 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
6532 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6537 BOOL is_sorted
, has_changed
;
6539 HWND hwndSelf
= infoPtr
->hwndSelf
;
6541 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6543 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
6545 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6546 if (!lpLVItem
|| lpLVItem
->iSubItem
) return -1;
6548 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
6550 if (!(lpItem
= Alloc(sizeof(ITEM_INFO
)))) return -1;
6552 /* insert item in listview control data structure */
6553 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
6554 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
6556 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
6557 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
6559 if (lpLVItem
->iItem
< 0 && !is_sorted
) return -1;
6561 nItem
= is_sorted
? infoPtr
->nItemCount
: min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
6562 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
6563 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
6564 if (nItem
== -1) goto fail
;
6565 infoPtr
->nItemCount
++;
6567 /* shift indices first so they don't get tangled */
6568 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
6570 /* set the item attributes */
6571 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6573 /* full size structure expected - _WIN32IE >= 0x560 */
6576 else if (lpLVItem
->mask
& LVIF_INDENT
)
6578 /* indent member expected - _WIN32IE >= 0x300 */
6579 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
6583 /* minimal structure expected */
6584 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
6587 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
6589 item
.mask
|= LVIF_STATE
;
6590 item
.stateMask
|= LVIS_STATEIMAGEMASK
;
6591 item
.state
&= ~LVIS_STATEIMAGEMASK
;
6592 item
.state
|= INDEXTOSTATEIMAGEMASK(1);
6594 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
6596 /* if we're sorted, sort the list, and update the index */
6599 DPA_Sort( infoPtr
->hdpaItems
, LISTVIEW_InsertCompare
, (LPARAM
)infoPtr
);
6600 nItem
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, hdpaSubItems
);
6601 assert(nItem
!= -1);
6604 /* make room for the position, if we are in the right mode */
6605 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6607 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
6609 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
6611 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
6616 /* send LVN_INSERTITEM notification */
6617 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
6619 nmlv
.lParam
= lpItem
->lParam
;
6620 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
6621 if (!IsWindow(hwndSelf
))
6624 /* align items (set position of each item) */
6625 if ((uView
== LVS_SMALLICON
|| uView
== LVS_ICON
))
6629 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
6630 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
6632 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
6634 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
6637 /* now is the invalidation fun */
6638 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
6642 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
6643 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
6644 infoPtr
->nItemCount
--;
6646 DPA_DeletePtr(hdpaSubItems
, 0);
6647 DPA_Destroy (hdpaSubItems
);
6654 * Redraws a range of items.
6657 * [I] infoPtr : valid pointer to the listview structure
6658 * [I] nFirst : first item
6659 * [I] nLast : last item
6665 static BOOL
LISTVIEW_RedrawItems(const LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
6669 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
6670 max(nFirst
, nLast
) >= infoPtr
->nItemCount
)
6673 for (i
= nFirst
; i
<= nLast
; i
++)
6674 LISTVIEW_InvalidateItem(infoPtr
, i
);
6681 * Scroll the content of a listview.
6684 * [I] infoPtr : valid pointer to the listview structure
6685 * [I] dx : horizontal scroll amount in pixels
6686 * [I] dy : vertical scroll amount in pixels
6693 * If the control is in report mode (LVS_REPORT) the control can
6694 * be scrolled only in line increments. "dy" will be rounded to the
6695 * nearest number of pixels that are a whole line. Ex: if line height
6696 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6697 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6699 * For: (per experimentation with native control and CSpy ListView)
6700 * LVS_ICON dy=1 = 1 pixel (vertical only)
6702 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6704 * LVS_LIST dx=1 = 1 column (horizontal only)
6705 * but will only scroll 1 column per message
6706 * no matter what the value.
6707 * dy must be 0 or FALSE returned.
6708 * LVS_REPORT dx=1 = 1 pixel
6712 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
6714 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
) {
6716 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
6717 dy
/= infoPtr
->nItemHeight
;
6720 if (dy
!= 0) return FALSE
;
6727 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
, 0);
6728 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
, 0);
6735 * Sets the background color.
6738 * [I] infoPtr : valid pointer to the listview structure
6739 * [I] clrBk : background color
6745 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrBk
)
6747 TRACE("(clrBk=%x)\n", clrBk
);
6749 if(infoPtr
->clrBk
!= clrBk
) {
6750 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
6751 infoPtr
->clrBk
= clrBk
;
6752 if (clrBk
== CLR_NONE
)
6753 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
6755 infoPtr
->hBkBrush
= CreateSolidBrush(clrBk
);
6756 LISTVIEW_InvalidateList(infoPtr
);
6762 /* LISTVIEW_SetBkImage */
6764 /*** Helper for {Insert,Set}ColumnT *only* */
6765 static void column_fill_hditem(const LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
,
6766 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6768 if (lpColumn
->mask
& LVCF_FMT
)
6770 /* format member is valid */
6771 lphdi
->mask
|= HDI_FORMAT
;
6773 /* set text alignment (leftmost column must be left-aligned) */
6774 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
6775 lphdi
->fmt
|= HDF_LEFT
;
6776 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
6777 lphdi
->fmt
|= HDF_RIGHT
;
6778 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
6779 lphdi
->fmt
|= HDF_CENTER
;
6781 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6782 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
6784 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6786 lphdi
->fmt
|= HDF_IMAGE
;
6787 lphdi
->iImage
= I_IMAGECALLBACK
;
6791 if (lpColumn
->mask
& LVCF_WIDTH
)
6793 lphdi
->mask
|= HDI_WIDTH
;
6794 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
6796 /* make it fill the remainder of the controls width */
6800 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
6802 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
6803 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
6806 /* retrieve the layout of the header */
6807 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
6808 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, wine_dbgstr_rect(&rcHeader
));
6810 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
6813 lphdi
->cxy
= lpColumn
->cx
;
6816 if (lpColumn
->mask
& LVCF_TEXT
)
6818 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
6819 lphdi
->fmt
|= HDF_STRING
;
6820 lphdi
->pszText
= lpColumn
->pszText
;
6821 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
6824 if (lpColumn
->mask
& LVCF_IMAGE
)
6826 lphdi
->mask
|= HDI_IMAGE
;
6827 lphdi
->iImage
= lpColumn
->iImage
;
6830 if (lpColumn
->mask
& LVCF_ORDER
)
6832 lphdi
->mask
|= HDI_ORDER
;
6833 lphdi
->iOrder
= lpColumn
->iOrder
;
6840 * Inserts a new column.
6843 * [I] infoPtr : valid pointer to the listview structure
6844 * [I] nColumn : column index
6845 * [I] lpColumn : column information
6846 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6849 * SUCCESS : new column index
6852 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6853 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6855 COLUMN_INFO
*lpColumnInfo
;
6859 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6861 if (!lpColumn
|| nColumn
< 0) return -1;
6862 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
6864 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6865 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6868 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6869 * (can be seen in SPY) otherwise column never gets added.
6871 if (!(lpColumn
->mask
& LVCF_WIDTH
)) {
6872 hdi
.mask
|= HDI_WIDTH
;
6877 * when the iSubItem is available Windows copies it to the header lParam. It seems
6878 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6880 if (lpColumn
->mask
& LVCF_SUBITEM
)
6882 hdi
.mask
|= HDI_LPARAM
;
6883 hdi
.lParam
= lpColumn
->iSubItem
;
6886 /* insert item in header control */
6887 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
6888 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
6889 (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6890 if (nNewColumn
== -1) return -1;
6891 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
6893 /* create our own column info */
6894 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
6895 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
6897 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
6898 if (!Header_GetItemRect(infoPtr
->hwndHeader
, nNewColumn
, &lpColumnInfo
->rcHeader
)) goto fail
;
6900 /* now we have to actually adjust the data */
6901 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
6903 SUBITEM_INFO
*lpSubItem
;
6907 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
6909 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
6910 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
6912 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
6913 if (lpSubItem
->iSubItem
>= nNewColumn
)
6914 lpSubItem
->iSubItem
++;
6919 /* make space for the new column */
6920 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
6921 LISTVIEW_UpdateItemSize(infoPtr
);
6926 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
6929 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
6937 * Sets the attributes of a header item.
6940 * [I] infoPtr : valid pointer to the listview structure
6941 * [I] nColumn : column index
6942 * [I] lpColumn : column attributes
6943 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6949 static BOOL
LISTVIEW_SetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6950 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6952 HDITEMW hdi
, hdiget
;
6955 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6957 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6959 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6960 if (lpColumn
->mask
& LVCF_FMT
)
6962 hdi
.mask
|= HDI_FORMAT
;
6963 hdiget
.mask
= HDI_FORMAT
;
6964 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdiget
))
6965 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
6967 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6969 /* set header item attributes */
6970 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6971 if (!bResult
) return FALSE
;
6973 if (lpColumn
->mask
& LVCF_FMT
)
6975 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
6976 int oldFmt
= lpColumnInfo
->fmt
;
6978 lpColumnInfo
->fmt
= lpColumn
->fmt
;
6979 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
6981 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6982 if (uView
== LVS_REPORT
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
6991 * Sets the column order array
6994 * [I] infoPtr : valid pointer to the listview structure
6995 * [I] iCount : number of elements in column order array
6996 * [I] lpiArray : pointer to column order array
7002 static BOOL
LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
7004 FIXME("iCount %d lpiArray %p\n", iCount
, lpiArray
);
7015 * Sets the width of a column
7018 * [I] infoPtr : valid pointer to the listview structure
7019 * [I] nColumn : column index
7020 * [I] cx : column width
7026 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
7028 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7029 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
7033 TRACE("(nColumn=%d, cx=%d\n", nColumn
, cx
);
7035 /* set column width only if in report or list mode */
7036 if (uView
!= LVS_REPORT
&& uView
!= LVS_LIST
) return FALSE
;
7038 /* take care of invalid cx values */
7039 if(uView
== LVS_REPORT
&& cx
< -2) cx
= LVSCW_AUTOSIZE
;
7040 else if (uView
== LVS_LIST
&& cx
< 1) return FALSE
;
7042 /* resize all columns if in LVS_LIST mode */
7043 if(uView
== LVS_LIST
)
7045 infoPtr
->nItemWidth
= cx
;
7046 LISTVIEW_InvalidateList(infoPtr
);
7050 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
7052 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
7057 lvItem
.mask
= LVIF_TEXT
;
7059 lvItem
.iSubItem
= nColumn
;
7060 lvItem
.pszText
= szDispText
;
7061 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7062 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7064 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
7065 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
7066 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
7068 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
7069 max_cx
+= infoPtr
->iconSize
.cx
;
7070 max_cx
+= TRAILING_LABEL_PADDING
;
7073 /* autosize based on listview items width */
7074 if(cx
== LVSCW_AUTOSIZE
)
7076 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
7078 /* if iCol is the last column make it fill the remainder of the controls width */
7079 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
7084 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7085 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
7087 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
7091 /* Despite what the MS docs say, if this is not the last
7092 column, then MS resizes the column to the width of the
7093 largest text string in the column, including headers
7094 and items. This is different from LVSCW_AUTOSIZE in that
7095 LVSCW_AUTOSIZE ignores the header string length. */
7098 /* retrieve header text */
7099 hdi
.mask
= HDI_TEXT
;
7100 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
7101 hdi
.pszText
= szDispText
;
7102 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
))
7104 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
7105 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
7108 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
7109 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
7110 /* FIXME: Take into account the header image, if one is present */
7111 SelectObject(hdc
, old_font
);
7112 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7114 cx
= max (cx
, max_cx
);
7118 if (cx
< 0) return FALSE
;
7120 /* call header to update the column change */
7121 hdi
.mask
= HDI_WIDTH
;
7123 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
7124 return Header_SetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
);
7128 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7131 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO
*infoPtr
)
7134 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
7136 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
7137 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
7140 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
7141 ILC_COLOR
| ILC_MASK
, 2, 2);
7142 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
7143 hdc
= CreateCompatibleDC(hdc_wnd
);
7144 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
7145 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
7146 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
7148 rc
.left
= rc
.top
= 0;
7149 rc
.right
= GetSystemMetrics(SM_CXSMICON
);
7150 rc
.bottom
= GetSystemMetrics(SM_CYSMICON
);
7152 hbm_orig
= SelectObject(hdc
, hbm_mask
);
7153 FillRect(hdc
, &rc
, hbr_white
);
7154 InflateRect(&rc
, -3, -3);
7155 FillRect(hdc
, &rc
, hbr_black
);
7157 SelectObject(hdc
, hbm_im
);
7158 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
7159 SelectObject(hdc
, hbm_orig
);
7160 ImageList_Add(himl
, hbm_im
, hbm_mask
);
7162 SelectObject(hdc
, hbm_im
);
7163 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
7164 SelectObject(hdc
, hbm_orig
);
7165 ImageList_Add(himl
, hbm_im
, hbm_mask
);
7167 DeleteObject(hbm_mask
);
7168 DeleteObject(hbm_im
);
7176 * Sets the extended listview style.
7179 * [I] infoPtr : valid pointer to the listview structure
7181 * [I] dwStyle : style
7184 * SUCCESS : previous style
7187 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD dwMask
, DWORD dwExStyle
)
7189 DWORD dwOldExStyle
= infoPtr
->dwLvExStyle
;
7193 infoPtr
->dwLvExStyle
= (dwOldExStyle
& ~dwMask
) | (dwExStyle
& dwMask
);
7195 infoPtr
->dwLvExStyle
= dwExStyle
;
7197 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_CHECKBOXES
)
7199 HIMAGELIST himl
= 0;
7200 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
7203 item
.mask
= LVIF_STATE
;
7204 item
.stateMask
= LVIS_STATEIMAGEMASK
;
7205 item
.state
= INDEXTOSTATEIMAGEMASK(1);
7206 LISTVIEW_SetItemState(infoPtr
, -1, &item
);
7208 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
7210 LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
7213 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_HEADERDRAGDROP
)
7215 DWORD dwStyle
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
7216 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
)
7217 dwStyle
|= HDS_DRAGDROP
;
7219 dwStyle
&= ~HDS_DRAGDROP
;
7220 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, dwStyle
);
7223 /* GRIDLINES adds decoration at top so changes sizes */
7224 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_GRIDLINES
)
7226 LISTVIEW_UpdateSize(infoPtr
);
7230 LISTVIEW_InvalidateList(infoPtr
);
7231 return dwOldExStyle
;
7236 * Sets the new hot cursor used during hot tracking and hover selection.
7239 * [I] infoPtr : valid pointer to the listview structure
7240 * [I] hCursor : the new hot cursor handle
7243 * Returns the previous hot cursor
7245 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
7247 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
7249 infoPtr
->hHotCursor
= hCursor
;
7257 * Sets the hot item index.
7260 * [I] infoPtr : valid pointer to the listview structure
7261 * [I] iIndex : index
7264 * SUCCESS : previous hot item index
7265 * FAILURE : -1 (no hot item)
7267 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
7269 INT iOldIndex
= infoPtr
->nHotItem
;
7271 infoPtr
->nHotItem
= iIndex
;
7279 * Sets the amount of time the cursor must hover over an item before it is selected.
7282 * [I] infoPtr : valid pointer to the listview structure
7283 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7286 * Returns the previous hover time
7288 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
7290 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
7292 infoPtr
->dwHoverTime
= dwHoverTime
;
7294 return oldHoverTime
;
7299 * Sets spacing for icons of LVS_ICON style.
7302 * [I] infoPtr : valid pointer to the listview structure
7303 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7304 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7307 * MAKELONG(oldcx, oldcy)
7309 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
7311 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
7312 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7314 TRACE("requested=(%d,%d)\n", cx
, cy
);
7316 /* this is supported only for LVS_ICON style */
7317 if (uView
!= LVS_ICON
) return oldspacing
;
7319 /* set to defaults, if instructed to */
7320 if (cx
== -1) cx
= GetSystemMetrics(SM_CXICONSPACING
);
7321 if (cy
== -1) cy
= GetSystemMetrics(SM_CYICONSPACING
);
7323 /* if 0 then compute width
7324 * FIXME: Should scan each item and determine max width of
7325 * icon or label, then make that the width */
7327 cx
= infoPtr
->iconSpacing
.cx
;
7329 /* if 0 then compute height */
7331 cy
= infoPtr
->iconSize
.cy
+ 2 * infoPtr
->ntmHeight
+
7332 ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_PADDING
;
7335 infoPtr
->iconSpacing
.cx
= cx
;
7336 infoPtr
->iconSpacing
.cy
= cy
;
7338 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7339 LOWORD(oldspacing
), HIWORD(oldspacing
), cx
, cy
,
7340 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
7341 infoPtr
->ntmHeight
);
7343 /* these depend on the iconSpacing */
7344 LISTVIEW_UpdateItemSize(infoPtr
);
7349 static inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL small
)
7353 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
7360 size
->cx
= GetSystemMetrics(small
? SM_CXSMICON
: SM_CXICON
);
7361 size
->cy
= GetSystemMetrics(small
? SM_CYSMICON
: SM_CYICON
);
7370 * [I] infoPtr : valid pointer to the listview structure
7371 * [I] nType : image list type
7372 * [I] himl : image list handle
7375 * SUCCESS : old image list
7378 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
7380 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7381 INT oldHeight
= infoPtr
->nItemHeight
;
7382 HIMAGELIST himlOld
= 0;
7384 TRACE("(nType=%d, himl=%p\n", nType
, himl
);
7389 himlOld
= infoPtr
->himlNormal
;
7390 infoPtr
->himlNormal
= himl
;
7391 if (uView
== LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
7392 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
7396 himlOld
= infoPtr
->himlSmall
;
7397 infoPtr
->himlSmall
= himl
;
7398 if (uView
!= LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
7402 himlOld
= infoPtr
->himlState
;
7403 infoPtr
->himlState
= himl
;
7404 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
7405 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
7409 ERR("Unknown icon type=%d\n", nType
);
7413 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
7414 if (infoPtr
->nItemHeight
!= oldHeight
)
7415 LISTVIEW_UpdateScroll(infoPtr
);
7422 * Preallocates memory (does *not* set the actual count of items !)
7425 * [I] infoPtr : valid pointer to the listview structure
7426 * [I] nItems : item count (projected number of items to allocate)
7427 * [I] dwFlags : update flags
7433 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
7435 TRACE("(nItems=%d, dwFlags=%x)\n", nItems
, dwFlags
);
7437 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
7439 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7440 INT nOldCount
= infoPtr
->nItemCount
;
7442 if (nItems
< nOldCount
)
7444 RANGE range
= { nItems
, nOldCount
};
7445 ranges_del(infoPtr
->selectionRanges
, range
);
7446 if (infoPtr
->nFocusedItem
>= nItems
)
7448 infoPtr
->nFocusedItem
= -1;
7449 SetRectEmpty(&infoPtr
->rcFocus
);
7453 infoPtr
->nItemCount
= nItems
;
7454 LISTVIEW_UpdateScroll(infoPtr
);
7456 /* the flags are valid only in ownerdata report and list modes */
7457 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
) dwFlags
= 0;
7459 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
7460 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
7462 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
7463 LISTVIEW_InvalidateList(infoPtr
);
7470 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7471 nFrom
= min(nOldCount
, nItems
);
7472 nTo
= max(nOldCount
, nItems
);
7474 if (uView
== LVS_REPORT
)
7477 rcErase
.top
= nFrom
* infoPtr
->nItemHeight
;
7478 rcErase
.right
= infoPtr
->nItemWidth
;
7479 rcErase
.bottom
= nTo
* infoPtr
->nItemHeight
;
7480 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7481 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7482 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7486 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
7488 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
7489 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
7490 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
7491 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7492 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7493 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7494 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7496 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7498 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7499 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7500 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7501 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7502 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7508 /* According to MSDN for non-LVS_OWNERDATA this is just
7509 * a performance issue. The control allocates its internal
7510 * data structures for the number of items specified. It
7511 * cuts down on the number of memory allocations. Therefore
7512 * we will just issue a WARN here
7514 WARN("for non-ownerdata performance option not implemented.\n");
7522 * Sets the position of an item.
7525 * [I] infoPtr : valid pointer to the listview structure
7526 * [I] nItem : item index
7527 * [I] pt : coordinate
7533 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, POINT pt
)
7535 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7538 TRACE("(nItem=%d, &pt=%s\n", nItem
, wine_dbgstr_point(&pt
));
7540 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
7541 !(uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)) return FALSE
;
7543 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7545 /* This point value seems to be an undocumented feature.
7546 * The best guess is that it means either at the origin,
7547 * or at true beginning of the list. I will assume the origin. */
7548 if ((pt
.x
== -1) && (pt
.y
== -1))
7551 if (uView
== LVS_ICON
)
7553 pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
7554 pt
.y
-= ICON_TOP_PADDING
;
7559 infoPtr
->bAutoarrange
= FALSE
;
7561 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, FALSE
);
7566 * Sets the state of one or many items.
7569 * [I] infoPtr : valid pointer to the listview structure
7570 * [I] nItem : item index
7571 * [I] lpLVItem : item or subitem info
7577 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
)
7579 BOOL bResult
= TRUE
;
7582 lvItem
.iItem
= nItem
;
7583 lvItem
.iSubItem
= 0;
7584 lvItem
.mask
= LVIF_STATE
;
7585 lvItem
.state
= lpLVItem
->state
;
7586 lvItem
.stateMask
= lpLVItem
->stateMask
;
7587 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
7591 /* apply to all items */
7592 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7593 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) bResult
= FALSE
;
7596 bResult
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
7599 * Update selection mark
7601 * Investigation on windows 2k showed that selection mark was updated
7602 * whenever a new selection was made, but if the selected item was
7603 * unselected it was not updated.
7605 * we are probably still not 100% accurate, but this at least sets the
7606 * proper selection mark when it is needed
7609 if (bResult
&& (lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) &&
7610 (infoPtr
->nSelectionMark
== -1))
7613 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
7615 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
7617 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
7619 infoPtr
->nSelectionMark
= i
;
7623 else if (ranges_contain(infoPtr
->selectionRanges
, i
))
7625 infoPtr
->nSelectionMark
= i
;
7636 * Sets the text of an item or subitem.
7639 * [I] hwnd : window handle
7640 * [I] nItem : item index
7641 * [I] lpLVItem : item or subitem info
7642 * [I] isW : TRUE if input is Unicode
7648 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
7652 if (nItem
< 0 && nItem
>= infoPtr
->nItemCount
) return FALSE
;
7654 lvItem
.iItem
= nItem
;
7655 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
7656 lvItem
.mask
= LVIF_TEXT
;
7657 lvItem
.pszText
= lpLVItem
->pszText
;
7658 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
7660 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
7662 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
7667 * Set item index that marks the start of a multiple selection.
7670 * [I] infoPtr : valid pointer to the listview structure
7671 * [I] nIndex : index
7674 * Index number or -1 if there is no selection mark.
7676 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
7678 INT nOldIndex
= infoPtr
->nSelectionMark
;
7680 TRACE("(nIndex=%d)\n", nIndex
);
7682 infoPtr
->nSelectionMark
= nIndex
;
7689 * Sets the text background color.
7692 * [I] infoPtr : valid pointer to the listview structure
7693 * [I] clrTextBk : text background color
7699 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrTextBk
)
7701 TRACE("(clrTextBk=%x)\n", clrTextBk
);
7703 if (infoPtr
->clrTextBk
!= clrTextBk
)
7705 infoPtr
->clrTextBk
= clrTextBk
;
7706 LISTVIEW_InvalidateList(infoPtr
);
7714 * Sets the text foreground color.
7717 * [I] infoPtr : valid pointer to the listview structure
7718 * [I] clrText : text color
7724 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF clrText
)
7726 TRACE("(clrText=%x)\n", clrText
);
7728 if (infoPtr
->clrText
!= clrText
)
7730 infoPtr
->clrText
= clrText
;
7731 LISTVIEW_InvalidateList(infoPtr
);
7739 * Determines which listview item is located at the specified position.
7742 * [I] infoPtr : valid pointer to the listview structure
7743 * [I] hwndNewToolTip : handle to new ToolTip
7748 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
7750 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
7751 infoPtr
->hwndToolTip
= hwndNewToolTip
;
7752 return hwndOldToolTip
;
7757 * sets the Unicode character format flag for the control
7759 * [I] infoPtr :valid pointer to the listview structure
7760 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7763 * Old Unicode Format
7765 static BOOL
LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO
*infoPtr
, BOOL fUnicode
)
7767 BOOL rc
= infoPtr
->notifyFormat
;
7768 infoPtr
->notifyFormat
= (fUnicode
)?NFR_UNICODE
:NFR_ANSI
;
7772 /* LISTVIEW_SetWorkAreas */
7776 * Callback internally used by LISTVIEW_SortItems()
7779 * [I] first : pointer to first ITEM_INFO to compare
7780 * [I] second : pointer to second ITEM_INFO to compare
7781 * [I] lParam : HWND of control
7784 * if first comes before second : negative
7785 * if first comes after second : positive
7786 * if first and second are equivalent : zero
7788 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
7790 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
7791 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
7792 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
7794 /* Forward the call to the client defined callback */
7795 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
7800 * Sorts the listview items.
7803 * [I] infoPtr : valid pointer to the listview structure
7804 * [I] pfnCompare : application-defined value
7805 * [I] lParamSort : pointer to comparison callback
7811 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
, LPARAM lParamSort
)
7813 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7816 LPVOID selectionMarkItem
;
7820 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
7822 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
7824 if (!pfnCompare
) return FALSE
;
7825 if (!infoPtr
->hdpaItems
) return FALSE
;
7827 /* if there are 0 or 1 items, there is no need to sort */
7828 if (infoPtr
->nItemCount
< 2) return TRUE
;
7830 if (infoPtr
->nFocusedItem
>= 0)
7832 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
7833 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
7834 if (lpItem
) lpItem
->state
|= LVIS_FOCUSED
;
7836 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7837 /* clear the lpItem->state for non-selected ones */
7838 /* remove the selection ranges */
7840 infoPtr
->pfnCompare
= pfnCompare
;
7841 infoPtr
->lParamSort
= lParamSort
;
7842 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
7844 /* Adjust selections and indices so that they are the way they should
7845 * be after the sort (otherwise, the list items move around, but
7846 * whatever is at the item's previous original position will be
7849 selectionMarkItem
=(infoPtr
->nSelectionMark
>=0)?DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
):NULL
;
7850 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
7852 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
7853 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
7855 if (lpItem
->state
& LVIS_SELECTED
)
7857 item
.state
= LVIS_SELECTED
;
7858 item
.stateMask
= LVIS_SELECTED
;
7859 LISTVIEW_SetItemState(infoPtr
, i
, &item
);
7861 if (lpItem
->state
& LVIS_FOCUSED
)
7863 infoPtr
->nFocusedItem
= i
;
7864 lpItem
->state
&= ~LVIS_FOCUSED
;
7867 if (selectionMarkItem
!= NULL
)
7868 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
7869 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7871 /* refresh the display */
7872 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
)
7873 LISTVIEW_InvalidateList(infoPtr
);
7880 * Update theme handle after a theme change.
7883 * [I] infoPtr : valid pointer to the listview structure
7887 * FAILURE : something else
7889 static LRESULT
LISTVIEW_ThemeChanged(const LISTVIEW_INFO
*infoPtr
)
7891 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
7892 CloseThemeData(theme
);
7893 OpenThemeData(infoPtr
->hwndSelf
, themeClass
);
7899 * Updates an items or rearranges the listview control.
7902 * [I] infoPtr : valid pointer to the listview structure
7903 * [I] nItem : item index
7909 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
7911 TRACE("(nItem=%d)\n", nItem
);
7913 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
7915 /* rearrange with default alignment style */
7916 if (is_autoarrange(infoPtr
))
7917 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
7919 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
7926 * Draw the track line at the place defined in the infoPtr structure.
7927 * The line is drawn with a XOR pen so drawing the line for the second time
7928 * in the same place erases the line.
7931 * [I] infoPtr : valid pointer to the listview structure
7937 static BOOL
LISTVIEW_DrawTrackLine(const LISTVIEW_INFO
*infoPtr
)
7943 if (infoPtr
->xTrackLine
== -1)
7946 if (!(hdc
= GetDC(infoPtr
->hwndSelf
)))
7948 hOldPen
= SelectObject(hdc
, GetStockObject(BLACK_PEN
));
7949 oldROP
= SetROP2(hdc
, R2_XORPEN
);
7950 MoveToEx(hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.top
, NULL
);
7951 LineTo(hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.bottom
);
7952 SetROP2(hdc
, oldROP
);
7953 SelectObject(hdc
, hOldPen
);
7954 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7960 * Called when an edit control should be displayed. This function is called after
7961 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7964 * [I] hwnd : Handle to the listview
7965 * [I] uMsg : WM_TIMER (ignored)
7966 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7967 * [I] dwTimer : The elapsed time (ignored)
7972 static VOID CALLBACK
LISTVIEW_DelayedEditItem(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
7974 DELAYED_ITEM_EDIT
*editItem
= (DELAYED_ITEM_EDIT
*)idEvent
;
7975 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
7977 KillTimer(hwnd
, idEvent
);
7978 editItem
->fEnabled
= FALSE
;
7979 /* check if the item is still selected */
7980 if (infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, editItem
->iItem
, LVIS_SELECTED
))
7981 LISTVIEW_EditLabelT(infoPtr
, editItem
->iItem
, TRUE
);
7986 * Creates the listview control - the WM_NCCREATE phase.
7989 * [I] hwnd : window handle
7990 * [I] lpcs : the create parameters
7996 static LRESULT
LISTVIEW_NCCreate(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
7998 LISTVIEW_INFO
*infoPtr
;
8001 TRACE("(lpcs=%p)\n", lpcs
);
8003 /* initialize info pointer */
8004 infoPtr
= Alloc(sizeof(LISTVIEW_INFO
));
8005 if (!infoPtr
) return FALSE
;
8007 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
8009 infoPtr
->hwndSelf
= hwnd
;
8010 infoPtr
->dwStyle
= lpcs
->style
; /* Note: may be changed in WM_CREATE */
8011 /* determine the type of structures to use */
8012 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
8013 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8015 /* initialize color information */
8016 infoPtr
->clrBk
= CLR_NONE
;
8017 infoPtr
->clrText
= CLR_DEFAULT
;
8018 infoPtr
->clrTextBk
= CLR_DEFAULT
;
8019 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
8021 /* set default values */
8022 infoPtr
->nFocusedItem
= -1;
8023 infoPtr
->nSelectionMark
= -1;
8024 infoPtr
->nHotItem
= -1;
8025 infoPtr
->bRedraw
= TRUE
;
8026 infoPtr
->bNoItemMetrics
= TRUE
;
8027 infoPtr
->bDoChangeNotify
= TRUE
;
8028 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
8029 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
8030 infoPtr
->nEditLabelItem
= -1;
8031 infoPtr
->dwHoverTime
= -1; /* default system hover time */
8032 infoPtr
->nMeasureItemHeight
= 0;
8033 infoPtr
->xTrackLine
= -1; /* no track line */
8034 infoPtr
->itemEdit
.fEnabled
= FALSE
;
8036 /* get default font (icon title) */
8037 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
8038 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
8039 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
8040 LISTVIEW_SaveTextMetrics(infoPtr
);
8042 /* allocate memory for the data structure */
8043 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
8044 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
8045 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
8046 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
8047 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
8051 DestroyWindow(infoPtr
->hwndHeader
);
8052 ranges_destroy(infoPtr
->selectionRanges
);
8053 DPA_Destroy(infoPtr
->hdpaItems
);
8054 DPA_Destroy(infoPtr
->hdpaPosX
);
8055 DPA_Destroy(infoPtr
->hdpaPosY
);
8056 DPA_Destroy(infoPtr
->hdpaColumns
);
8063 * Creates the listview control - the WM_CREATE phase. Most of the data is
8064 * already set up in LISTVIEW_NCCreate
8067 * [I] hwnd : window handle
8068 * [I] lpcs : the create parameters
8074 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
8076 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
8077 UINT uView
= lpcs
->style
& LVS_TYPEMASK
;
8078 DWORD dFlags
= WS_CHILD
| HDS_HORZ
| HDS_FULLDRAG
| HDS_DRAGDROP
;
8080 TRACE("(lpcs=%p)\n", lpcs
);
8082 infoPtr
->dwStyle
= lpcs
->style
;
8083 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
8084 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
8086 /* setup creation flags */
8087 dFlags
|= (LVS_NOSORTHEADER
& lpcs
->style
) ? 0 : HDS_BUTTONS
;
8088 dFlags
|= (LVS_NOCOLUMNHEADER
& lpcs
->style
) ? HDS_HIDDEN
: 0;
8091 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
, dFlags
,
8092 0, 0, 0, 0, hwnd
, NULL
,
8093 lpcs
->hInstance
, NULL
);
8094 if (!infoPtr
->hwndHeader
) return -1;
8096 /* set header unicode format */
8097 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, TRUE
, 0);
8099 /* set header font */
8100 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, (LPARAM
)TRUE
);
8102 /* init item size to avoid division by 0 */
8103 LISTVIEW_UpdateItemSize (infoPtr
);
8105 if (uView
== LVS_REPORT
)
8107 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
))
8109 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
8111 LISTVIEW_UpdateSize(infoPtr
);
8112 LISTVIEW_UpdateScroll(infoPtr
);
8115 OpenThemeData(hwnd
, themeClass
);
8117 /* initialize the icon sizes */
8118 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, uView
!= LVS_ICON
);
8119 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
8125 * Destroys the listview control.
8128 * [I] infoPtr : valid pointer to the listview structure
8134 static LRESULT
LISTVIEW_Destroy(const LISTVIEW_INFO
*infoPtr
)
8136 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
8137 CloseThemeData(theme
);
8143 * Enables the listview control.
8146 * [I] infoPtr : valid pointer to the listview structure
8147 * [I] bEnable : specifies whether to enable or disable the window
8153 static BOOL
LISTVIEW_Enable(const LISTVIEW_INFO
*infoPtr
, BOOL bEnable
)
8155 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
8156 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
8162 * Erases the background of the listview control.
8165 * [I] infoPtr : valid pointer to the listview structure
8166 * [I] hdc : device context handle
8172 static inline BOOL
LISTVIEW_EraseBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
8176 TRACE("(hdc=%p)\n", hdc
);
8178 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
8180 /* for double buffered controls we need to do this during refresh */
8181 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) return FALSE
;
8183 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
8189 * Helper function for LISTVIEW_[HV]Scroll *only*.
8190 * Performs vertical/horizontal scrolling by a give amount.
8193 * [I] infoPtr : valid pointer to the listview structure
8194 * [I] dx : amount of horizontal scroll
8195 * [I] dy : amount of vertical scroll
8197 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
8199 /* now we can scroll the list */
8200 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
8201 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
8202 /* if we have focus, adjust rect */
8203 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
8204 UpdateWindow(infoPtr
->hwndSelf
);
8209 * Performs vertical scrolling.
8212 * [I] infoPtr : valid pointer to the listview structure
8213 * [I] nScrollCode : scroll code
8214 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8215 * [I] hScrollWnd : scrollbar control window handle
8221 * SB_LINEUP/SB_LINEDOWN:
8222 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8223 * for LVS_REPORT is 1 line
8224 * for LVS_LIST cannot occur
8227 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
8228 INT nScrollDiff
, HWND hScrollWnd
)
8230 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8231 INT nOldScrollPos
, nNewScrollPos
;
8232 SCROLLINFO scrollInfo
;
8235 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
8236 debugscrollcode(nScrollCode
), nScrollDiff
);
8238 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8240 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8241 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
8243 is_an_icon
= ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
));
8245 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
8247 nOldScrollPos
= scrollInfo
.nPos
;
8248 switch (nScrollCode
)
8254 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
8258 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
8262 nScrollDiff
= -scrollInfo
.nPage
;
8266 nScrollDiff
= scrollInfo
.nPage
;
8269 case SB_THUMBPOSITION
:
8271 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
8278 /* quit right away if pos isn't changing */
8279 if (nScrollDiff
== 0) return 0;
8281 /* calculate new position, and handle overflows */
8282 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
8283 if (nScrollDiff
> 0) {
8284 if (nNewScrollPos
< nOldScrollPos
||
8285 nNewScrollPos
> scrollInfo
.nMax
)
8286 nNewScrollPos
= scrollInfo
.nMax
;
8288 if (nNewScrollPos
> nOldScrollPos
||
8289 nNewScrollPos
< scrollInfo
.nMin
)
8290 nNewScrollPos
= scrollInfo
.nMin
;
8293 /* set the new position, and reread in case it changed */
8294 scrollInfo
.fMask
= SIF_POS
;
8295 scrollInfo
.nPos
= nNewScrollPos
;
8296 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
8298 /* carry on only if it really changed */
8299 if (nNewScrollPos
== nOldScrollPos
) return 0;
8301 /* now adjust to client coordinates */
8302 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
8303 if (uView
== LVS_REPORT
) nScrollDiff
*= infoPtr
->nItemHeight
;
8305 /* and scroll the window */
8306 scroll_list(infoPtr
, 0, nScrollDiff
);
8313 * Performs horizontal scrolling.
8316 * [I] infoPtr : valid pointer to the listview structure
8317 * [I] nScrollCode : scroll code
8318 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8319 * [I] hScrollWnd : scrollbar control window handle
8325 * SB_LINELEFT/SB_LINERIGHT:
8326 * for LVS_ICON, LVS_SMALLICON 1 pixel
8327 * for LVS_REPORT is 1 pixel
8328 * for LVS_LIST is 1 column --> which is a 1 because the
8329 * scroll is based on columns not pixels
8332 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
8333 INT nScrollDiff
, HWND hScrollWnd
)
8335 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8336 INT nOldScrollPos
, nNewScrollPos
;
8337 SCROLLINFO scrollInfo
;
8339 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
8340 debugscrollcode(nScrollCode
), nScrollDiff
);
8342 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8344 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8345 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
8347 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
8349 nOldScrollPos
= scrollInfo
.nPos
;
8351 switch (nScrollCode
)
8365 nScrollDiff
= -scrollInfo
.nPage
;
8369 nScrollDiff
= scrollInfo
.nPage
;
8372 case SB_THUMBPOSITION
:
8374 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
8381 /* quit right away if pos isn't changing */
8382 if (nScrollDiff
== 0) return 0;
8384 /* calculate new position, and handle overflows */
8385 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
8386 if (nScrollDiff
> 0) {
8387 if (nNewScrollPos
< nOldScrollPos
||
8388 nNewScrollPos
> scrollInfo
.nMax
)
8389 nNewScrollPos
= scrollInfo
.nMax
;
8391 if (nNewScrollPos
> nOldScrollPos
||
8392 nNewScrollPos
< scrollInfo
.nMin
)
8393 nNewScrollPos
= scrollInfo
.nMin
;
8396 /* set the new position, and reread in case it changed */
8397 scrollInfo
.fMask
= SIF_POS
;
8398 scrollInfo
.nPos
= nNewScrollPos
;
8399 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
8401 /* carry on only if it really changed */
8402 if (nNewScrollPos
== nOldScrollPos
) return 0;
8404 if(uView
== LVS_REPORT
)
8405 LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
8407 /* now adjust to client coordinates */
8408 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
8409 if (uView
== LVS_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
8411 /* and scroll the window */
8412 scroll_list(infoPtr
, nScrollDiff
, 0);
8417 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
8419 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8420 INT gcWheelDelta
= 0;
8421 INT pulScrollLines
= 3;
8422 SCROLLINFO scrollInfo
;
8424 TRACE("(wheelDelta=%d)\n", wheelDelta
);
8426 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
8427 gcWheelDelta
-= wheelDelta
;
8429 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8430 scrollInfo
.fMask
= SIF_POS
;
8437 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8438 * should be fixed in the future.
8440 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (gcWheelDelta
< 0) ?
8441 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
, 0);
8445 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
8447 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
8448 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
8449 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, cLineScroll
, 0);
8454 LISTVIEW_HScroll(infoPtr
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0, 0);
8465 * [I] infoPtr : valid pointer to the listview structure
8466 * [I] nVirtualKey : virtual key
8467 * [I] lKeyData : key data
8472 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
8474 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8475 HWND hwndSelf
= infoPtr
->hwndSelf
;
8477 NMLVKEYDOWN nmKeyDown
;
8479 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey
, lKeyData
);
8481 /* send LVN_KEYDOWN notification */
8482 nmKeyDown
.wVKey
= nVirtualKey
;
8483 nmKeyDown
.flags
= 0;
8484 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
8485 if (!IsWindow(hwndSelf
))
8488 switch (nVirtualKey
)
8491 nItem
= infoPtr
->nFocusedItem
;
8492 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
8493 toggle_checkbox_state(infoPtr
, infoPtr
->nFocusedItem
);
8497 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
8499 if (!notify(infoPtr
, NM_RETURN
)) return 0;
8500 if (!notify(infoPtr
, LVN_ITEMACTIVATE
)) return 0;
8505 if (infoPtr
->nItemCount
> 0)
8510 if (infoPtr
->nItemCount
> 0)
8511 nItem
= infoPtr
->nItemCount
- 1;
8515 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
8519 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
8523 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
8527 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
8531 if (uView
== LVS_REPORT
)
8533 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8534 if (infoPtr
->nFocusedItem
== topidx
)
8535 nItem
= topidx
- LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
8540 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
8541 * LISTVIEW_GetCountPerRow(infoPtr
);
8542 if(nItem
< 0) nItem
= 0;
8546 if (uView
== LVS_REPORT
)
8548 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8549 INT cnt
= LISTVIEW_GetCountPerColumn(infoPtr
);
8550 if (infoPtr
->nFocusedItem
== topidx
+ cnt
- 1)
8551 nItem
= infoPtr
->nFocusedItem
+ cnt
- 1;
8553 nItem
= topidx
+ cnt
- 1;
8556 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
8557 * LISTVIEW_GetCountPerRow(infoPtr
);
8558 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
8562 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
|| nVirtualKey
== VK_SPACE
))
8563 LISTVIEW_KeySelection(infoPtr
, nItem
);
8573 * [I] infoPtr : valid pointer to the listview structure
8578 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
8582 /* if we did not have the focus, there's nothing to do */
8583 if (!infoPtr
->bFocus
) return 0;
8585 /* send NM_KILLFOCUS notification */
8586 if (!notify(infoPtr
, NM_KILLFOCUS
)) return 0;
8588 /* if we have a focus rectagle, get rid of it */
8589 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
8591 /* set window focus flag */
8592 infoPtr
->bFocus
= FALSE
;
8594 /* invalidate the selected items before resetting focus flag */
8595 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8602 * Processes double click messages (left mouse button).
8605 * [I] infoPtr : valid pointer to the listview structure
8606 * [I] wKey : key flag
8607 * [I] x,y : mouse coordinate
8612 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8614 LVHITTESTINFO htInfo
;
8616 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8618 /* Cancel the item edition if any */
8619 if (infoPtr
->itemEdit
.fEnabled
)
8621 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
)&infoPtr
->itemEdit
);
8622 infoPtr
->itemEdit
.fEnabled
= FALSE
;
8625 /* send NM_RELEASEDCAPTURE notification */
8626 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8631 /* send NM_DBLCLK notification */
8632 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
8633 if (!notify_click(infoPtr
, NM_DBLCLK
, &htInfo
)) return 0;
8635 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8636 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
8643 * Processes mouse down messages (left mouse button).
8646 * infoPtr [I ] valid pointer to the listview structure
8647 * wKey [I ] key flag
8648 * x,y [I ] mouse coordinate
8653 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8655 LVHITTESTINFO lvHitTestInfo
;
8656 static BOOL bGroupSelect
= TRUE
;
8657 POINT pt
= { x
, y
};
8660 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8662 /* send NM_RELEASEDCAPTURE notification */
8663 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8665 /* set left button down flag and record the click position */
8666 infoPtr
->bLButtonDown
= TRUE
;
8667 infoPtr
->ptClickPos
= pt
;
8668 infoPtr
->bDragging
= FALSE
;
8670 lvHitTestInfo
.pt
.x
= x
;
8671 lvHitTestInfo
.pt
.y
= y
;
8673 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8674 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt
), nItem
);
8675 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8677 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
8679 toggle_checkbox_state(infoPtr
, nItem
);
8683 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
8685 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8686 infoPtr
->nEditLabelItem
= nItem
;
8688 LISTVIEW_SetSelection(infoPtr
, nItem
);
8692 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
8696 if (!LISTVIEW_AddGroupSelection(infoPtr
, nItem
)) return 0;
8697 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8698 infoPtr
->nSelectionMark
= nItem
;
8704 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
8705 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8707 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
8708 infoPtr
->nSelectionMark
= nItem
;
8711 else if (wKey
& MK_CONTROL
)
8715 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
8717 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
8718 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8719 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
8720 infoPtr
->nSelectionMark
= nItem
;
8722 else if (wKey
& MK_SHIFT
)
8724 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
8728 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8729 infoPtr
->nEditLabelItem
= nItem
;
8731 /* set selection (clears other pre-existing selections) */
8732 LISTVIEW_SetSelection(infoPtr
, nItem
);
8736 if (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
)
8737 if(lvHitTestInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&lvHitTestInfo
);
8741 /* remove all selections */
8742 LISTVIEW_DeselectAll(infoPtr
);
8751 * Processes mouse up messages (left mouse button).
8754 * infoPtr [I ] valid pointer to the listview structure
8755 * wKey [I ] key flag
8756 * x,y [I ] mouse coordinate
8761 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8763 LVHITTESTINFO lvHitTestInfo
;
8765 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8767 if (!infoPtr
->bLButtonDown
) return 0;
8769 lvHitTestInfo
.pt
.x
= x
;
8770 lvHitTestInfo
.pt
.y
= y
;
8772 /* send NM_CLICK notification */
8773 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8774 if (!notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
)) return 0;
8776 /* set left button flag */
8777 infoPtr
->bLButtonDown
= FALSE
;
8779 if (infoPtr
->bDragging
)
8781 infoPtr
->bDragging
= FALSE
;
8785 /* if we clicked on a selected item, edit the label */
8786 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
8788 /* we want to make sure the user doesn't want to do a double click. So we will
8789 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8791 infoPtr
->itemEdit
.fEnabled
= TRUE
;
8792 infoPtr
->itemEdit
.iItem
= lvHitTestInfo
.iItem
;
8793 SetTimer(infoPtr
->hwndSelf
,
8794 (UINT_PTR
)&infoPtr
->itemEdit
,
8795 GetDoubleClickTime(),
8796 LISTVIEW_DelayedEditItem
);
8799 if (!infoPtr
->bFocus
)
8800 SetFocus(infoPtr
->hwndSelf
);
8807 * Destroys the listview control (called after WM_DESTROY).
8810 * [I] infoPtr : valid pointer to the listview structure
8815 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
8819 /* delete all items */
8820 LISTVIEW_DeleteAllItems(infoPtr
, TRUE
);
8822 /* destroy data structure */
8823 DPA_Destroy(infoPtr
->hdpaItems
);
8824 DPA_Destroy(infoPtr
->hdpaPosX
);
8825 DPA_Destroy(infoPtr
->hdpaPosY
);
8826 DPA_Destroy(infoPtr
->hdpaColumns
);
8827 ranges_destroy(infoPtr
->selectionRanges
);
8829 /* destroy image lists */
8830 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
8832 if (infoPtr
->himlNormal
)
8833 ImageList_Destroy(infoPtr
->himlNormal
);
8834 if (infoPtr
->himlSmall
)
8835 ImageList_Destroy(infoPtr
->himlSmall
);
8836 if (infoPtr
->himlState
)
8837 ImageList_Destroy(infoPtr
->himlState
);
8840 /* destroy font, bkgnd brush */
8842 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
8843 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
8845 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
8847 /* free listview info pointer*/
8855 * Handles notifications from header.
8858 * [I] infoPtr : valid pointer to the listview structure
8859 * [I] nCtrlId : control identifier
8860 * [I] lpnmh : notification information
8865 static LRESULT
LISTVIEW_HeaderNotification(LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
8867 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8868 HWND hwndSelf
= infoPtr
->hwndSelf
;
8870 TRACE("(lpnmh=%p)\n", lpnmh
);
8872 if (!lpnmh
|| lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
8874 switch (lpnmh
->hdr
.code
)
8879 COLUMN_INFO
*lpColumnInfo
;
8883 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8886 /* remove the old line (if any) */
8887 LISTVIEW_DrawTrackLine(infoPtr
);
8889 /* compute & draw the new line */
8890 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8891 x
= lpColumnInfo
->rcHeader
.left
+ lpnmh
->pitem
->cxy
;
8892 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
8893 infoPtr
->xTrackLine
= x
+ ptOrigin
.x
;
8894 LISTVIEW_DrawTrackLine(infoPtr
);
8900 /* remove the track line (if any) */
8901 LISTVIEW_DrawTrackLine(infoPtr
);
8902 infoPtr
->xTrackLine
= -1;
8906 FIXME("Changing column order not implemented\n");
8909 case HDN_ITEMCHANGINGW
:
8910 case HDN_ITEMCHANGINGA
:
8911 return notify_forward_header(infoPtr
, lpnmh
);
8913 case HDN_ITEMCHANGEDW
:
8914 case HDN_ITEMCHANGEDA
:
8916 COLUMN_INFO
*lpColumnInfo
;
8919 notify_forward_header(infoPtr
, lpnmh
);
8920 if (!IsWindow(hwndSelf
))
8923 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8927 hdi
.mask
= HDI_WIDTH
;
8928 if (!Header_GetItemW(infoPtr
->hwndHeader
, lpnmh
->iItem
, &hdi
)) return 0;
8932 cxy
= lpnmh
->pitem
->cxy
;
8934 /* determine how much we change since the last know position */
8935 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8936 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
8939 lpColumnInfo
->rcHeader
.right
+= dx
;
8940 if (lpnmh
->iItem
+ 1 < DPA_GetPtrCount(infoPtr
->hdpaColumns
))
8941 LISTVIEW_ScrollColumns(infoPtr
, lpnmh
->iItem
+ 1, dx
);
8944 /* only needs to update the scrolls */
8945 infoPtr
->nItemWidth
+= dx
;
8946 LISTVIEW_UpdateScroll(infoPtr
);
8948 LISTVIEW_UpdateItemSize(infoPtr
);
8949 if (uView
== LVS_REPORT
&& is_redrawing(infoPtr
))
8952 RECT rcCol
= lpColumnInfo
->rcHeader
;
8954 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
8955 OffsetRect(&rcCol
, ptOrigin
.x
, 0);
8957 rcCol
.top
= infoPtr
->rcList
.top
;
8958 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
8960 /* resizing left-aligned columns leaves most of the left side untouched */
8961 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
8963 INT nMaxDirty
= infoPtr
->nEllipsisWidth
+ infoPtr
->ntmMaxCharWidth
;
8966 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- nMaxDirty
);
8969 /* when shrinking the last column clear the now unused field */
8970 if (lpnmh
->iItem
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1 && dx
< 0)
8973 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
8979 case HDN_ITEMCLICKW
:
8980 case HDN_ITEMCLICKA
:
8982 /* Handle sorting by Header Column */
8985 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
8987 nmlv
.iSubItem
= lpnmh
->iItem
;
8988 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
8992 case HDN_DIVIDERDBLCLICKW
:
8993 case HDN_DIVIDERDBLCLICKA
:
8994 LISTVIEW_SetColumnWidth(infoPtr
, lpnmh
->iItem
, LVSCW_AUTOSIZE
);
9003 * Paint non-client area of control.
9006 * [I] infoPtr : valid pointer to the listview structureof the sender
9007 * [I] region : update region
9010 * TRUE - frame was painted
9011 * FALSE - call default window proc
9013 static BOOL
LISTVIEW_NCPaint(const LISTVIEW_INFO
*infoPtr
, HRGN region
)
9015 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
9019 int cxEdge
= GetSystemMetrics (SM_CXEDGE
),
9020 cyEdge
= GetSystemMetrics (SM_CYEDGE
);
9022 if (!theme
) return FALSE
;
9024 GetWindowRect(infoPtr
->hwndSelf
, &r
);
9026 cliprgn
= CreateRectRgn (r
.left
+ cxEdge
, r
.top
+ cyEdge
,
9027 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
9028 if (region
!= (HRGN
)1)
9029 CombineRgn (cliprgn
, cliprgn
, region
, RGN_AND
);
9030 OffsetRect(&r
, -r
.left
, -r
.top
);
9032 dc
= GetDCEx(infoPtr
->hwndSelf
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
9033 OffsetRect(&r
, -r
.left
, -r
.top
);
9035 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
9036 DrawThemeParentBackground(infoPtr
->hwndSelf
, dc
, &r
);
9037 DrawThemeBackground (theme
, dc
, 0, 0, &r
, 0);
9038 ReleaseDC(infoPtr
->hwndSelf
, dc
);
9040 /* Call default proc to get the scrollbars etc. painted */
9041 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)cliprgn
, 0);
9048 * Determines the type of structure to use.
9051 * [I] infoPtr : valid pointer to the listview structureof the sender
9052 * [I] hwndFrom : listview window handle
9053 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9058 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
9060 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
9062 if (nCommand
== NF_REQUERY
)
9063 infoPtr
->notifyFormat
= SendMessageW(hwndFrom
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
9065 return infoPtr
->notifyFormat
;
9070 * Paints/Repaints the listview control.
9073 * [I] infoPtr : valid pointer to the listview structure
9074 * [I] hdc : device context handle
9079 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
9081 TRACE("(hdc=%p)\n", hdc
);
9083 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
9085 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9087 infoPtr
->bNoItemMetrics
= FALSE
;
9088 LISTVIEW_UpdateItemSize(infoPtr
);
9089 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
9090 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9091 LISTVIEW_UpdateScroll(infoPtr
);
9094 UpdateWindow(infoPtr
->hwndHeader
);
9097 LISTVIEW_Refresh(infoPtr
, hdc
, NULL
);
9102 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
9104 LISTVIEW_Refresh(infoPtr
, hdc
, ps
.fErase
? &ps
.rcPaint
: NULL
);
9105 EndPaint(infoPtr
->hwndSelf
, &ps
);
9114 * Paints/Repaints the listview control.
9117 * [I] infoPtr : valid pointer to the listview structure
9118 * [I] hdc : device context handle
9119 * [I] options : drawing options
9124 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
9126 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc
, options
);
9128 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
9131 if (options
& PRF_ERASEBKGND
)
9132 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
9134 if (options
& PRF_CLIENT
)
9135 LISTVIEW_Paint(infoPtr
, hdc
);
9143 * Processes double click messages (right mouse button).
9146 * [I] infoPtr : valid pointer to the listview structure
9147 * [I] wKey : key flag
9148 * [I] x,y : mouse coordinate
9153 static LRESULT
LISTVIEW_RButtonDblClk(const LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9155 LVHITTESTINFO lvHitTestInfo
;
9157 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9159 /* send NM_RELEASEDCAPTURE notification */
9160 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9162 /* send NM_RDBLCLK notification */
9163 lvHitTestInfo
.pt
.x
= x
;
9164 lvHitTestInfo
.pt
.y
= y
;
9165 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9166 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
9173 * Processes mouse down messages (right mouse button).
9176 * [I] infoPtr : valid pointer to the listview structure
9177 * [I] wKey : key flag
9178 * [I] x,y : mouse coordinate
9183 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9185 LVHITTESTINFO lvHitTestInfo
;
9188 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9190 /* send NM_RELEASEDCAPTURE notification */
9191 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9193 /* make sure the listview control window has the focus */
9194 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
9196 /* set right button down flag */
9197 infoPtr
->bRButtonDown
= TRUE
;
9199 /* determine the index of the selected item */
9200 lvHitTestInfo
.pt
.x
= x
;
9201 lvHitTestInfo
.pt
.y
= y
;
9202 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
9204 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
9206 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
9207 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
9208 !LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
9209 LISTVIEW_SetSelection(infoPtr
, nItem
);
9213 LISTVIEW_DeselectAll(infoPtr
);
9221 * Processes mouse up messages (right mouse button).
9224 * [I] infoPtr : valid pointer to the listview structure
9225 * [I] wKey : key flag
9226 * [I] x,y : mouse coordinate
9231 static LRESULT
LISTVIEW_RButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9233 LVHITTESTINFO lvHitTestInfo
;
9236 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9238 if (!infoPtr
->bRButtonDown
) return 0;
9240 /* set button flag */
9241 infoPtr
->bRButtonDown
= FALSE
;
9243 /* Send NM_RClICK notification */
9244 lvHitTestInfo
.pt
.x
= x
;
9245 lvHitTestInfo
.pt
.y
= y
;
9246 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9247 if (!notify_click(infoPtr
, NM_RCLICK
, &lvHitTestInfo
)) return 0;
9249 /* Change to screen coordinate for WM_CONTEXTMENU */
9250 pt
= lvHitTestInfo
.pt
;
9251 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
9253 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9254 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
9255 (WPARAM
)infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
9266 * [I] infoPtr : valid pointer to the listview structure
9267 * [I] hwnd : window handle of window containing the cursor
9268 * [I] nHittest : hit-test code
9269 * [I] wMouseMsg : ideintifier of the mouse message
9272 * TRUE if cursor is set
9275 static BOOL
LISTVIEW_SetCursor(const LISTVIEW_INFO
*infoPtr
, HWND hwnd
, UINT nHittest
, UINT wMouseMsg
)
9277 LVHITTESTINFO lvHitTestInfo
;
9279 if(!(LISTVIEW_isHotTracking(infoPtr
))) return FALSE
;
9281 if(!infoPtr
->hHotCursor
) return FALSE
;
9283 GetCursorPos(&lvHitTestInfo
.pt
);
9284 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) return FALSE
;
9286 SetCursor(infoPtr
->hHotCursor
);
9296 * [I] infoPtr : valid pointer to the listview structure
9297 * [I] hwndLoseFocus : handle of previously focused window
9302 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
9304 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
9306 /* if we have the focus already, there's nothing to do */
9307 if (infoPtr
->bFocus
) return 0;
9309 /* send NM_SETFOCUS notification */
9310 if (!notify(infoPtr
, NM_SETFOCUS
)) return 0;
9312 /* set window focus flag */
9313 infoPtr
->bFocus
= TRUE
;
9315 /* put the focus rect back on */
9316 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
9318 /* redraw all visible selected items */
9319 LISTVIEW_InvalidateSelectedItems(infoPtr
);
9329 * [I] infoPtr : valid pointer to the listview structure
9330 * [I] fRedraw : font handle
9331 * [I] fRedraw : redraw flag
9336 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
9338 HFONT oldFont
= infoPtr
->hFont
;
9340 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
9342 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
9343 if (infoPtr
->hFont
== oldFont
) return 0;
9345 LISTVIEW_SaveTextMetrics(infoPtr
);
9347 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
)
9349 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
9350 LISTVIEW_UpdateSize(infoPtr
);
9351 LISTVIEW_UpdateScroll(infoPtr
);
9354 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
9361 * Message handling for WM_SETREDRAW.
9362 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9365 * [I] infoPtr : valid pointer to the listview structure
9366 * [I] bRedraw: state of redraw flag
9369 * DefWinProc return value
9371 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
9373 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr
->bRedraw
, bRedraw
);
9375 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9376 if ((infoPtr
->bRedraw
&& bRedraw
) || (!infoPtr
->bRedraw
&& !bRedraw
)) return 0;
9378 infoPtr
->bRedraw
= bRedraw
;
9380 if(!bRedraw
) return 0;
9382 if (is_autoarrange(infoPtr
))
9383 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9384 LISTVIEW_UpdateScroll(infoPtr
);
9386 /* despite what the WM_SETREDRAW docs says, apps expect us
9387 * to invalidate the listview here... stupid! */
9388 LISTVIEW_InvalidateList(infoPtr
);
9395 * Resizes the listview control. This function processes WM_SIZE
9396 * messages. At this time, the width and height are not used.
9399 * [I] infoPtr : valid pointer to the listview structure
9400 * [I] Width : new width
9401 * [I] Height : new height
9406 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
9408 RECT rcOld
= infoPtr
->rcList
;
9410 TRACE("(width=%d, height=%d)\n", Width
, Height
);
9412 LISTVIEW_UpdateSize(infoPtr
);
9413 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
9415 /* do not bother with display related stuff if we're not redrawing */
9416 if (!is_redrawing(infoPtr
)) return 0;
9418 if (is_autoarrange(infoPtr
))
9419 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9421 LISTVIEW_UpdateScroll(infoPtr
);
9423 /* refresh all only for lists whose height changed significantly */
9424 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
&&
9425 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
9426 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
9427 LISTVIEW_InvalidateList(infoPtr
);
9434 * Sets the size information.
9437 * [I] infoPtr : valid pointer to the listview structure
9442 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
9444 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9446 TRACE("uView=%d, rcList(old)=%s\n", uView
, wine_dbgstr_rect(&infoPtr
->rcList
));
9448 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
9450 if (uView
== LVS_LIST
)
9452 /* Apparently the "LIST" style is supposed to have the same
9453 * number of items in a column even if there is no scroll bar.
9454 * Since if a scroll bar already exists then the bottom is already
9455 * reduced, only reduce if the scroll bar does not currently exist.
9456 * The "2" is there to mimic the native control. I think it may be
9457 * related to either padding or edges. (GLA 7/2002)
9459 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_HSCROLL
))
9460 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
9461 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
9463 else if (uView
== LVS_REPORT
)
9468 hl
.prc
= &infoPtr
->rcList
;
9470 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9471 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp
.flags
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
9472 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9473 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
9474 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9475 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
9477 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
9478 infoPtr
->rcList
.top
+= (infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
) ? 2 : 0;
9481 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr
->rcList
));
9486 * Processes WM_STYLECHANGED messages.
9489 * [I] infoPtr : valid pointer to the listview structure
9490 * [I] wStyleType : window style type (normal or extended)
9491 * [I] lpss : window style information
9496 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
9497 const STYLESTRUCT
*lpss
)
9499 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
9500 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
9503 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9504 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
9506 if (wStyleType
!= GWL_STYLE
) return 0;
9508 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9509 /* what if LVS_OWNERDATA changed? */
9510 /* or LVS_SINGLESEL */
9511 /* or LVS_SORT{AS,DES}CENDING */
9513 infoPtr
->dwStyle
= lpss
->styleNew
;
9515 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
9516 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
9517 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
9519 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
9520 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
9521 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
9523 if (uNewView
!= uOldView
)
9525 SIZE oldIconSize
= infoPtr
->iconSize
;
9528 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9529 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
9531 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
9532 SetRectEmpty(&infoPtr
->rcFocus
);
9534 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
9535 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
9537 if (uNewView
== LVS_ICON
)
9539 if ((infoPtr
->iconSize
.cx
!= oldIconSize
.cx
) || (infoPtr
->iconSize
.cy
!= oldIconSize
.cy
))
9541 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9542 oldIconSize
.cx
, oldIconSize
.cy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
9543 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
9546 else if (uNewView
== LVS_REPORT
)
9551 hl
.prc
= &infoPtr
->rcList
;
9553 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9554 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9555 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
9556 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9559 LISTVIEW_UpdateItemSize(infoPtr
);
9562 if (uNewView
== LVS_REPORT
)
9564 if ((lpss
->styleOld
^ lpss
->styleNew
) & LVS_NOCOLUMNHEADER
)
9566 if (lpss
->styleNew
& LVS_NOCOLUMNHEADER
)
9568 /* Turn off the header control */
9569 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
9570 TRACE("Hide header control, was 0x%08x\n", style
);
9571 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
| HDS_HIDDEN
);
9573 /* Turn on the header control */
9574 if ((style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
)) & HDS_HIDDEN
)
9576 TRACE("Show header control, was 0x%08x\n", style
);
9577 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, (style
& ~HDS_HIDDEN
) | WS_VISIBLE
);
9583 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
9584 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
9585 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9587 /* update the size of the client area */
9588 LISTVIEW_UpdateSize(infoPtr
);
9590 /* add scrollbars if needed */
9591 LISTVIEW_UpdateScroll(infoPtr
);
9593 /* invalidate client area + erase background */
9594 LISTVIEW_InvalidateList(infoPtr
);
9601 * Window procedure of the listview control.
9604 static LRESULT WINAPI
9605 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9607 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9609 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg
, wParam
, lParam
);
9611 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
9612 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9616 case LVM_APPROXIMATEVIEWRECT
:
9617 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
9618 LOWORD(lParam
), HIWORD(lParam
));
9620 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
9622 /* case LVM_CANCELEDITLABEL: */
9624 case LVM_CREATEDRAGIMAGE
:
9625 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9627 case LVM_DELETEALLITEMS
:
9628 return LISTVIEW_DeleteAllItems(infoPtr
, FALSE
);
9630 case LVM_DELETECOLUMN
:
9631 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
9633 case LVM_DELETEITEM
:
9634 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
9636 case LVM_EDITLABELW
:
9637 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, TRUE
);
9639 case LVM_EDITLABELA
:
9640 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, FALSE
);
9642 /* case LVM_ENABLEGROUPVIEW: */
9644 case LVM_ENSUREVISIBLE
:
9645 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
9648 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
9651 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
9653 case LVM_GETBKCOLOR
:
9654 return infoPtr
->clrBk
;
9656 /* case LVM_GETBKIMAGE: */
9658 case LVM_GETCALLBACKMASK
:
9659 return infoPtr
->uCallbackMask
;
9661 case LVM_GETCOLUMNA
:
9662 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9664 case LVM_GETCOLUMNW
:
9665 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9667 case LVM_GETCOLUMNORDERARRAY
:
9668 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9670 case LVM_GETCOLUMNWIDTH
:
9671 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
9673 case LVM_GETCOUNTPERPAGE
:
9674 return LISTVIEW_GetCountPerPage(infoPtr
);
9676 case LVM_GETEDITCONTROL
:
9677 return (LRESULT
)infoPtr
->hwndEdit
;
9679 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
9680 return infoPtr
->dwLvExStyle
;
9682 /* case LVM_GETGROUPINFO: */
9684 /* case LVM_GETGROUPMETRICS: */
9687 return (LRESULT
)infoPtr
->hwndHeader
;
9689 case LVM_GETHOTCURSOR
:
9690 return (LRESULT
)infoPtr
->hHotCursor
;
9692 case LVM_GETHOTITEM
:
9693 return infoPtr
->nHotItem
;
9695 case LVM_GETHOVERTIME
:
9696 return infoPtr
->dwHoverTime
;
9698 case LVM_GETIMAGELIST
:
9699 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
9701 /* case LVM_GETINSERTMARK: */
9703 /* case LVM_GETINSERTMARKCOLOR: */
9705 /* case LVM_GETINSERTMARKRECT: */
9707 case LVM_GETISEARCHSTRINGA
:
9708 case LVM_GETISEARCHSTRINGW
:
9709 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9713 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9716 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9718 case LVM_GETITEMCOUNT
:
9719 return infoPtr
->nItemCount
;
9721 case LVM_GETITEMPOSITION
:
9722 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9724 case LVM_GETITEMRECT
:
9725 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
9727 case LVM_GETITEMSPACING
:
9728 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
9730 case LVM_GETITEMSTATE
:
9731 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
9733 case LVM_GETITEMTEXTA
:
9734 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9736 case LVM_GETITEMTEXTW
:
9737 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9739 case LVM_GETNEXTITEM
:
9740 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
9742 case LVM_GETNUMBEROFWORKAREAS
:
9743 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9747 if (!lParam
) return FALSE
;
9748 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
||
9749 (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
) return FALSE
;
9750 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
9753 /* case LVM_GETOUTLINECOLOR: */
9755 /* case LVM_GETSELECTEDCOLUMN: */
9757 case LVM_GETSELECTEDCOUNT
:
9758 return LISTVIEW_GetSelectedCount(infoPtr
);
9760 case LVM_GETSELECTIONMARK
:
9761 return infoPtr
->nSelectionMark
;
9763 case LVM_GETSTRINGWIDTHA
:
9764 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, FALSE
);
9766 case LVM_GETSTRINGWIDTHW
:
9767 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, TRUE
);
9769 case LVM_GETSUBITEMRECT
:
9770 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
9772 case LVM_GETTEXTBKCOLOR
:
9773 return infoPtr
->clrTextBk
;
9775 case LVM_GETTEXTCOLOR
:
9776 return infoPtr
->clrText
;
9778 /* case LVM_GETTILEINFO: */
9780 /* case LVM_GETTILEVIEWINFO: */
9782 case LVM_GETTOOLTIPS
:
9783 if( !infoPtr
->hwndToolTip
)
9784 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
9785 return (LRESULT
)infoPtr
->hwndToolTip
;
9787 case LVM_GETTOPINDEX
:
9788 return LISTVIEW_GetTopIndex(infoPtr
);
9790 case LVM_GETUNICODEFORMAT
:
9791 return (infoPtr
->notifyFormat
== NFR_UNICODE
);
9793 /* case LVM_GETVIEW: */
9795 case LVM_GETVIEWRECT
:
9796 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
9798 case LVM_GETWORKAREAS
:
9799 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9802 /* case LVM_HASGROUP: */
9805 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, FALSE
);
9807 case LVM_INSERTCOLUMNA
:
9808 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9810 case LVM_INSERTCOLUMNW
:
9811 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9813 /* case LVM_INSERTGROUP: */
9815 /* case LVM_INSERTGROUPSORTED: */
9817 case LVM_INSERTITEMA
:
9818 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9820 case LVM_INSERTITEMW
:
9821 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9823 /* case LVM_INSERTMARKHITTEST: */
9825 /* case LVM_ISGROUPVIEWENABLED: */
9827 /* case LVM_MAPIDTOINDEX: */
9829 /* case LVM_MAPINDEXTOID: */
9831 /* case LVM_MOVEGROUP: */
9833 /* case LVM_MOVEITEMTOGROUP: */
9835 case LVM_REDRAWITEMS
:
9836 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9838 /* case LVM_REMOVEALLGROUPS: */
9840 /* case LVM_REMOVEGROUP: */
9843 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9845 case LVM_SETBKCOLOR
:
9846 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
9848 /* case LVM_SETBKIMAGE: */
9850 case LVM_SETCALLBACKMASK
:
9851 infoPtr
->uCallbackMask
= (UINT
)wParam
;
9854 case LVM_SETCOLUMNA
:
9855 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9857 case LVM_SETCOLUMNW
:
9858 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9860 case LVM_SETCOLUMNORDERARRAY
:
9861 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9863 case LVM_SETCOLUMNWIDTH
:
9864 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
9866 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
9867 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
9869 /* case LVM_SETGROUPINFO: */
9871 /* case LVM_SETGROUPMETRICS: */
9873 case LVM_SETHOTCURSOR
:
9874 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
9876 case LVM_SETHOTITEM
:
9877 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
9879 case LVM_SETHOVERTIME
:
9880 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)wParam
);
9882 case LVM_SETICONSPACING
:
9883 return LISTVIEW_SetIconSpacing(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
9885 case LVM_SETIMAGELIST
:
9886 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
9888 /* case LVM_SETINFOTIP: */
9890 /* case LVM_SETINSERTMARK: */
9892 /* case LVM_SETINSERTMARKCOLOR: */
9895 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9898 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9900 case LVM_SETITEMCOUNT
:
9901 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
9903 case LVM_SETITEMPOSITION
:
9906 pt
.x
= (short)LOWORD(lParam
);
9907 pt
.y
= (short)HIWORD(lParam
);
9908 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, pt
);
9911 case LVM_SETITEMPOSITION32
:
9912 if (lParam
== 0) return FALSE
;
9913 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, *((POINT
*)lParam
));
9915 case LVM_SETITEMSTATE
:
9916 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
9918 case LVM_SETITEMTEXTA
:
9919 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9921 case LVM_SETITEMTEXTW
:
9922 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9924 /* case LVM_SETOUTLINECOLOR: */
9926 /* case LVM_SETSELECTEDCOLUMN: */
9928 case LVM_SETSELECTIONMARK
:
9929 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
9931 case LVM_SETTEXTBKCOLOR
:
9932 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
9934 case LVM_SETTEXTCOLOR
:
9935 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
9937 /* case LVM_SETTILEINFO: */
9939 /* case LVM_SETTILEVIEWINFO: */
9941 /* case LVM_SETTILEWIDTH: */
9943 case LVM_SETTOOLTIPS
:
9944 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
9946 case LVM_SETUNICODEFORMAT
:
9947 return LISTVIEW_SetUnicodeFormat(infoPtr
, wParam
);
9949 /* case LVM_SETVIEW: */
9951 /* case LVM_SETWORKAREAS: */
9953 /* case LVM_SORTGROUPS: */
9956 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
);
9958 /* LVM_SORTITEMSEX: */
9960 case LVM_SUBITEMHITTEST
:
9961 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
9964 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
9967 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
9970 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
9973 return LISTVIEW_NCCreate(hwnd
, (LPCREATESTRUCTW
)lParam
);
9976 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
9979 return LISTVIEW_Destroy(infoPtr
);
9982 return LISTVIEW_Enable(infoPtr
, (BOOL
)wParam
);
9985 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
9988 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
9991 return (LRESULT
)infoPtr
->hFont
;
9994 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
9997 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
10000 return LISTVIEW_KillFocus(infoPtr
);
10002 case WM_LBUTTONDBLCLK
:
10003 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10005 case WM_LBUTTONDOWN
:
10006 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10009 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10012 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10014 case WM_MOUSEHOVER
:
10015 return LISTVIEW_MouseHover(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10018 return LISTVIEW_NCDestroy(infoPtr
);
10021 if (LISTVIEW_NCPaint(infoPtr
, (HRGN
)wParam
))
10026 if (lParam
&& ((LPNMHDR
)lParam
)->hwndFrom
== infoPtr
->hwndHeader
)
10027 return LISTVIEW_HeaderNotification(infoPtr
, (LPNMHEADERW
)lParam
);
10030 case WM_NOTIFYFORMAT
:
10031 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
10033 case WM_PRINTCLIENT
:
10034 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
10037 return LISTVIEW_Paint(infoPtr
, (HDC
)wParam
);
10039 case WM_RBUTTONDBLCLK
:
10040 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10042 case WM_RBUTTONDOWN
:
10043 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10046 return LISTVIEW_RButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10049 if(LISTVIEW_SetCursor(infoPtr
, (HWND
)wParam
, LOWORD(lParam
), HIWORD(lParam
)))
10054 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
10057 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
10060 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
10063 return LISTVIEW_Size(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
10065 case WM_STYLECHANGED
:
10066 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
10068 case WM_SYSCOLORCHANGE
:
10069 COMCTL32_RefreshSysColors();
10072 /* case WM_TIMER: */
10073 case WM_THEMECHANGED
:
10074 return LISTVIEW_ThemeChanged(infoPtr
);
10077 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
10079 case WM_MOUSEWHEEL
:
10080 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
10081 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10082 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
10084 case WM_WINDOWPOSCHANGED
:
10085 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
10087 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
10088 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
10089 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
10091 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
10093 MEASUREITEMSTRUCT mis
;
10094 mis
.CtlType
= ODT_LISTVIEW
;
10095 mis
.CtlID
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
10099 mis
.itemHeight
= infoPtr
->nItemHeight
;
10100 SendMessageW(infoPtr
->hwndNotify
, WM_MEASUREITEM
, mis
.CtlID
, (LPARAM
)&mis
);
10101 if (infoPtr
->nItemHeight
!= max(mis
.itemHeight
, 1))
10102 infoPtr
->nMeasureItemHeight
= infoPtr
->nItemHeight
= max(mis
.itemHeight
, 1);
10105 LISTVIEW_UpdateSize(infoPtr
);
10106 LISTVIEW_UpdateScroll(infoPtr
);
10108 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10110 /* case WM_WININICHANGE: */
10113 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
) && !COMCTL32_IsReflectedMessage(uMsg
))
10114 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg
, wParam
, lParam
);
10117 /* call default window procedure */
10118 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10125 * Registers the window class.
10133 void LISTVIEW_Register(void)
10135 WNDCLASSW wndClass
;
10137 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
10138 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
10139 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
10140 wndClass
.cbClsExtra
= 0;
10141 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
10142 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
10143 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
10144 wndClass
.lpszClassName
= WC_LISTVIEWW
;
10145 RegisterClassW(&wndClass
);
10150 * Unregisters the window class.
10158 void LISTVIEW_Unregister(void)
10160 UnregisterClassW(WC_LISTVIEWW
, NULL
);
10165 * Handle any WM_COMMAND messages
10168 * [I] infoPtr : valid pointer to the listview structure
10169 * [I] wParam : the first message parameter
10170 * [I] lParam : the second message parameter
10175 static LRESULT
LISTVIEW_Command(const LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
10177 switch (HIWORD(wParam
))
10182 * Adjust the edit window size
10184 WCHAR buffer
[1024];
10185 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
10186 HFONT hFont
, hOldFont
= 0;
10190 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
10191 GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
10192 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
10194 /* Select font to get the right dimension of the string */
10195 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
10198 hOldFont
= SelectObject(hdc
, hFont
);
10201 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
10203 TEXTMETRICW textMetric
;
10205 /* Add Extra spacing for the next character */
10206 GetTextMetricsW(hdc
, &textMetric
);
10207 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10215 rect
.bottom
- rect
.top
,
10216 SWP_DRAWFRAME
|SWP_NOMOVE
);
10219 SelectObject(hdc
, hOldFont
);
10221 ReleaseDC(infoPtr
->hwndEdit
, hdc
);
10227 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);
10236 * Subclassed edit control windproc function
10239 * [I] hwnd : the edit window handle
10240 * [I] uMsg : the message that is to be processed
10241 * [I] wParam : first message parameter
10242 * [I] lParam : second message parameter
10243 * [I] isW : TRUE if input is Unicode
10248 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
10250 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
10251 BOOL cancel
= FALSE
;
10253 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10254 hwnd
, uMsg
, wParam
, lParam
, isW
);
10258 case WM_GETDLGCODE
:
10259 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
10266 WNDPROC editProc
= infoPtr
->EditWndProc
;
10267 infoPtr
->EditWndProc
= 0;
10268 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
10269 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10273 if (VK_ESCAPE
== (INT
)wParam
)
10278 else if (VK_RETURN
== (INT
)wParam
)
10282 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10285 /* kill the edit */
10286 if (infoPtr
->hwndEdit
)
10288 LPWSTR buffer
= NULL
;
10290 infoPtr
->hwndEdit
= 0;
10293 DWORD len
= isW
? GetWindowTextLengthW(hwnd
) : GetWindowTextLengthA(hwnd
);
10297 if ( (buffer
= Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))) )
10299 if (isW
) GetWindowTextW(hwnd
, buffer
, len
+1);
10300 else GetWindowTextA(hwnd
, (CHAR
*)buffer
, len
+1);
10304 LISTVIEW_EndEditLabelT(infoPtr
, buffer
, isW
);
10309 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
10315 * Subclassed edit control Unicode windproc function
10318 * [I] hwnd : the edit window handle
10319 * [I] uMsg : the message that is to be processed
10320 * [I] wParam : first message parameter
10321 * [I] lParam : second message parameter
10325 static LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10327 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
10332 * Subclassed edit control ANSI windproc function
10335 * [I] hwnd : the edit window handle
10336 * [I] uMsg : the message that is to be processed
10337 * [I] wParam : first message parameter
10338 * [I] lParam : second message parameter
10342 static LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10344 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
10349 * Creates a subclassed edit control
10352 * [I] infoPtr : valid pointer to the listview structure
10353 * [I] text : initial text for the edit
10354 * [I] style : the window style
10355 * [I] isW : TRUE if input is Unicode
10359 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, DWORD style
,
10360 INT x
, INT y
, INT width
, INT height
, BOOL isW
)
10362 WCHAR editName
[5] = { 'E', 'd', 'i', 't', '\0' };
10367 TEXTMETRICW textMetric
;
10368 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
10370 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text
, isW
), isW
);
10372 style
|= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
;
10373 hdc
= GetDC(infoPtr
->hwndSelf
);
10375 /* Select the font to get appropriate metric dimensions */
10376 if(infoPtr
->hFont
!= 0)
10377 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
10379 /*Get String Length in pixels */
10380 GetTextExtentPoint32W(hdc
, text
, lstrlenW(text
), &sz
);
10382 /*Add Extra spacing for the next character */
10383 GetTextMetricsW(hdc
, &textMetric
);
10384 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10386 if(infoPtr
->hFont
!= 0)
10387 SelectObject(hdc
, hOldFont
);
10389 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
10391 hedit
= CreateWindowW(editName
, text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
10393 hedit
= CreateWindowA("Edit", (LPCSTR
)text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
10395 if (!hedit
) return 0;
10397 infoPtr
->EditWndProc
= (WNDPROC
)
10398 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
10399 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
10401 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);