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
9 * Copyright 2009-2012 Nikolay Sivov
10 * Copyright 2009 Owen Rudge for CodeWeavers
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * This code was audited for completeness against the documented features
29 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
31 * Unless otherwise noted, we believe this code to be complete, as per
32 * the specification mentioned above.
33 * If you discover missing features, or bugs, please note them below.
37 * Default Message Processing
38 * -- WM_CREATE: create the icon and small icon image lists at this point only if
39 * the LVS_SHAREIMAGELISTS style is not specified.
40 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
41 * or small icon and the LVS_AUTOARRANGE style is specified.
46 * -- Hot item handling, mouse hovering
47 * -- Workareas support
52 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
53 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
54 * -- LVA_SNAPTOGRID not implemented
55 * -- LISTVIEW_ApproximateViewRect partially implemented
56 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
57 * -- LISTVIEW_SetIconSpacing is incomplete
58 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
61 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
62 * linear in the number of items in the list, and this is
63 * unacceptable for large lists.
64 * -- if list is sorted by item text LISTVIEW_InsertItemT could use
65 * binary search to calculate item index (e.g. DPA_Search()).
66 * This requires sorted state to be reliably tracked in item modifiers.
67 * -- we should keep an ordered array of coordinates in iconic mode
68 * this would allow to frame items (iterator_frameditems),
69 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
76 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
82 * -- LVS_NOSCROLL (see Q137520)
86 * -- LVS_EX_BORDERSELECT
90 * -- LVS_EX_MULTIWORKAREAS
92 * -- LVS_EX_SIMPLESELECT
93 * -- LVS_EX_TWOCLICKACTIVATE
94 * -- LVS_EX_UNDERLINECOLD
95 * -- LVS_EX_UNDERLINEHOT
98 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
105 * -- LVM_ENABLEGROUPVIEW
106 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
107 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
108 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
109 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
110 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
111 * -- LVM_GETINSERTMARKRECT
112 * -- LVM_GETNUMBEROFWORKAREAS
113 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
114 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
115 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
116 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
117 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
118 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
119 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
120 * -- LVM_INSERTGROUPSORTED
121 * -- LVM_INSERTMARKHITTEST
122 * -- LVM_ISGROUPVIEWENABLED
124 * -- LVM_MOVEITEMTOGROUP
126 * -- LVM_SETTILEWIDTH
130 * -- ListView_GetHoverTime, ListView_SetHoverTime
131 * -- ListView_GetISearchString
132 * -- ListView_GetNumberOfWorkAreas
133 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
138 * Known differences in message stream from native control (not known if
139 * these differences cause problems):
140 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
141 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
142 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
143 * processing for "USEDOUBLECLICKTIME".
147 #include "wine/port.h"
162 #include "commctrl.h"
163 #include "comctl32.h"
166 #include "wine/debug.h"
167 #include "wine/unicode.h"
169 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
171 typedef struct tagCOLUMN_INFO
173 RECT rcHeader
; /* tracks the header's rectangle */
174 INT fmt
; /* same as LVCOLUMN.fmt */
178 typedef struct tagITEMHDR
182 } ITEMHDR
, *LPITEMHDR
;
184 typedef struct tagSUBITEM_INFO
190 typedef struct tagITEM_ID ITEM_ID
;
192 typedef struct tagITEM_INFO
203 UINT id
; /* item id */
204 HDPA item
; /* link to item data */
207 typedef struct tagRANGE
213 typedef struct tagRANGES
218 typedef struct tagITERATOR
227 typedef struct tagDELAYED_ITEM_EDIT
233 typedef struct tagLISTVIEW_INFO
237 RECT rcList
; /* This rectangle is really the window
238 * client rectangle possibly reduced by the
239 * horizontal scroll bar and/or header - see
240 * LISTVIEW_UpdateSize. This rectangle offset
241 * by the LISTVIEW_GetOrigin value is in
242 * client coordinates */
244 /* notification window */
247 BOOL bDoChangeNotify
; /* send change notification messages? */
254 INT nItemCount
; /* the number of items in the list */
255 HDPA hdpaItems
; /* array ITEM_INFO pointers */
256 HDPA hdpaItemIds
; /* array of ITEM_ID pointers */
257 HDPA hdpaPosX
; /* maintains the (X, Y) coordinates of the */
258 HDPA hdpaPosY
; /* items in LVS_ICON, and LVS_SMALLICON modes */
259 RANGES selectionRanges
;
260 INT nSelectionMark
; /* item to start next multiselection from */
262 BOOL bAutoarrange
; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
265 HDPA hdpaColumns
; /* array of COLUMN_INFO pointers */
266 BOOL colRectsDirty
; /* trigger column rectangles requery from header */
269 BOOL bNoItemMetrics
; /* flags if item metrics are not yet computed */
274 PFNLVCOMPARE pfnCompare
; /* sorting callback pointer */
278 DWORD dwStyle
; /* the cached window GWL_STYLE */
279 DWORD dwLvExStyle
; /* extended listview style */
280 DWORD uView
; /* current view available through LVM_[G,S]ETVIEW */
286 DELAYED_ITEM_EDIT itemEdit
; /* Pointer to this structure will be the timer ID */
289 HIMAGELIST himlNormal
;
290 HIMAGELIST himlSmall
;
291 HIMAGELIST himlState
;
295 POINT currIconPos
; /* this is the position next icon will be placed */
299 INT xTrackLine
; /* The x coefficient of the track line or -1 if none */
301 /* marquee selection */
302 BOOL bMarqueeSelect
; /* marquee selection/highlight underway */
304 RECT marqueeRect
; /* absolute coordinates of marquee selection */
305 RECT marqueeDrawRect
; /* relative coordinates for drawing marquee */
306 POINT marqueeOrigin
; /* absolute coordinates of marquee click origin */
309 BOOL bFocus
; /* control has focus */
311 RECT rcFocus
; /* focus bounds */
318 BOOL bDefaultBkColor
;
323 INT ntmHeight
; /* Some cached metrics of the font used */
324 INT ntmMaxCharWidth
; /* by the listview to draw items */
327 /* mouse operation */
331 POINT ptClickPos
; /* point where the user clicked */
332 INT nLButtonDownItem
; /* tracks item to reset multiselection on WM_LBUTTONUP */
336 /* keyboard operation */
337 DWORD lastKeyPressTimestamp
;
339 INT nSearchParamLength
;
340 WCHAR szSearchParam
[ MAX_PATH
];
343 DWORD cditemmode
; /* Keep the custom draw flags for an item/row */
344 BOOL bIsDrawing
; /* Drawing in progress */
345 INT nMeasureItemHeight
; /* WM_MEASUREITEM result */
346 BOOL bRedraw
; /* WM_SETREDRAW switch */
349 DWORD iVersion
; /* CCM_[G,S]ETVERSION */
355 /* How many we debug buffer to allocate */
356 #define DEBUG_BUFFERS 20
357 /* The size of a single debug buffer */
358 #define DEBUG_BUFFER_SIZE 256
360 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
361 #define SB_INTERNAL -1
363 /* maximum size of a label */
364 #define DISP_TEXT_SIZE 260
366 /* padding for items in list and small icon display modes */
367 #define WIDTH_PADDING 12
369 /* padding for items in list, report and small icon display modes */
370 #define HEIGHT_PADDING 1
372 /* offset of items in report display mode */
373 #define REPORT_MARGINX 2
375 /* padding for icon in large icon display mode
376 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
377 * that HITTEST will see.
378 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
379 * ICON_TOP_PADDING - sum of the two above.
380 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
381 * LABEL_HOR_PADDING - between text and sides of box
382 * LABEL_VERT_PADDING - between bottom of text and end of box
384 * ICON_LR_PADDING - additional width above icon size.
385 * ICON_LR_HALF - half of the above value
387 #define ICON_TOP_PADDING_NOTHITABLE 2
388 #define ICON_TOP_PADDING_HITABLE 2
389 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
390 #define ICON_BOTTOM_PADDING 4
391 #define LABEL_HOR_PADDING 5
392 #define LABEL_VERT_PADDING 7
393 #define ICON_LR_PADDING 16
394 #define ICON_LR_HALF (ICON_LR_PADDING/2)
396 /* default label width for items in list and small icon display modes */
397 #define DEFAULT_LABEL_WIDTH 40
398 /* maximum select rectangle width for empty text item in LV_VIEW_DETAILS */
399 #define MAX_EMPTYTEXT_SELECT_WIDTH 80
401 /* default column width for items in list display mode */
402 #define DEFAULT_COLUMN_WIDTH 128
404 /* Size of "line" scroll for V & H scrolls */
405 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
407 /* Padding between image and label */
408 #define IMAGE_PADDING 2
410 /* Padding behind the label */
411 #define TRAILING_LABEL_PADDING 12
412 #define TRAILING_HEADER_PADDING 11
414 /* Border for the icon caption */
415 #define CAPTION_BORDER 2
417 /* Standard DrawText flags */
418 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
419 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
420 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
422 /* Image index from state */
423 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
425 /* The time in milliseconds to reset the search in the list */
426 #define KEY_DELAY 450
428 /* Dump the LISTVIEW_INFO structure to the debug channel */
429 #define LISTVIEW_DUMP(iP) do { \
430 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
431 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
432 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
433 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
434 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
435 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
436 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
437 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
438 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
439 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
442 static const WCHAR themeClass
[] = {'L','i','s','t','V','i','e','w',0};
445 * forward declarations
447 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
448 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*, INT
, LPRECT
);
449 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*, INT
, LPPOINT
);
450 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*, INT
, LPPOINT
);
451 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*, INT
, LPRECT
);
452 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*, LPPOINT
);
453 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*, LPRECT
);
454 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
455 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*, WPARAM
, LPARAM
);
456 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
457 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
, BOOL
);
458 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*, INT
, UINT
);
459 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, const LVITEMW
*);
460 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
);
461 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
);
462 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
463 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*, INT
, HIMAGELIST
);
464 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*, LPLVHITTESTINFO
, BOOL
, BOOL
);
465 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*, BOOL
, BOOL
);
466 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*, INT
, INT
);
468 /******** Text handling functions *************************************/
470 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
471 * text string. The string may be ANSI or Unicode, in which case
472 * the boolean isW tells us the type of the string.
474 * The name of the function tell what type of strings it expects:
475 * W: Unicode, T: ANSI/Unicode - function of isW
478 static inline BOOL
is_text(LPCWSTR text
)
480 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
483 static inline int textlenT(LPCWSTR text
, BOOL isW
)
485 return !is_text(text
) ? 0 :
486 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
489 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
492 if (isSrcW
) lstrcpynW(dest
, src
, max
);
493 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
495 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
496 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
499 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
501 LPWSTR wstr
= (LPWSTR
)text
;
503 if (!isW
&& is_text(text
))
505 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
506 wstr
= Alloc(len
* sizeof(WCHAR
));
507 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
509 TRACE(" wstr=%s\n", text
== LPSTR_TEXTCALLBACKW
? "(callback)" : debugstr_w(wstr
));
513 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
515 if (!isW
&& is_text(wstr
)) Free (wstr
);
519 * dest is a pointer to a Unicode string
520 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
522 static BOOL
textsetptrT(LPWSTR
*dest
, LPCWSTR src
, BOOL isW
)
526 if (src
== LPSTR_TEXTCALLBACKW
)
528 if (is_text(*dest
)) Free(*dest
);
529 *dest
= LPSTR_TEXTCALLBACKW
;
533 LPWSTR pszText
= textdupTtoW(src
, isW
);
534 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
535 bResult
= Str_SetPtrW(dest
, pszText
);
536 textfreeT(pszText
, isW
);
542 * compares a Unicode to a Unicode/ANSI text string
544 static inline int textcmpWT(LPCWSTR aw
, LPCWSTR bt
, BOOL isW
)
546 if (!aw
) return bt
? -1 : 0;
548 if (aw
== LPSTR_TEXTCALLBACKW
)
549 return bt
== LPSTR_TEXTCALLBACKW
? 1 : -1;
550 if (bt
!= LPSTR_TEXTCALLBACKW
)
552 LPWSTR bw
= textdupTtoW(bt
, isW
);
553 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
561 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
563 n
= min(min(n
, lstrlenW(s1
)), lstrlenW(s2
));
564 return CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
) - CSTR_EQUAL
;
567 /******** Debugging functions *****************************************/
569 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
571 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
572 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
575 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
577 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
578 n
= min(textlenT(text
, isW
), n
);
579 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
582 static char* debug_getbuf(void)
584 static int index
= 0;
585 static char buffers
[DEBUG_BUFFERS
][DEBUG_BUFFER_SIZE
];
586 return buffers
[index
++ % DEBUG_BUFFERS
];
589 static inline const char* debugrange(const RANGE
*lprng
)
591 if (!lprng
) return "(null)";
592 return wine_dbg_sprintf("[%d, %d]", lprng
->lower
, lprng
->upper
);
595 static const char* debugscrollinfo(const SCROLLINFO
*pScrollInfo
)
597 char* buf
= debug_getbuf(), *text
= buf
;
598 int len
, size
= DEBUG_BUFFER_SIZE
;
600 if (pScrollInfo
== NULL
) return "(null)";
601 len
= snprintf(buf
, size
, "{cbSize=%d, ", pScrollInfo
->cbSize
);
602 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
603 if (pScrollInfo
->fMask
& SIF_RANGE
)
604 len
= snprintf(buf
, size
, "nMin=%d, nMax=%d, ", pScrollInfo
->nMin
, pScrollInfo
->nMax
);
606 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
607 if (pScrollInfo
->fMask
& SIF_PAGE
)
608 len
= snprintf(buf
, size
, "nPage=%u, ", pScrollInfo
->nPage
);
610 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
611 if (pScrollInfo
->fMask
& SIF_POS
)
612 len
= snprintf(buf
, size
, "nPos=%d, ", pScrollInfo
->nPos
);
614 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
615 if (pScrollInfo
->fMask
& SIF_TRACKPOS
)
616 len
= snprintf(buf
, size
, "nTrackPos=%d, ", pScrollInfo
->nTrackPos
);
618 if (len
== -1) goto end
; buf
+= len
;
621 buf
= text
+ strlen(text
);
623 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
627 static const char* debugnmlistview(const NMLISTVIEW
*plvnm
)
629 if (!plvnm
) return "(null)";
630 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
631 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
632 plvnm
->iItem
, plvnm
->iSubItem
, plvnm
->uNewState
, plvnm
->uOldState
,
633 plvnm
->uChanged
, wine_dbgstr_point(&plvnm
->ptAction
), plvnm
->lParam
);
636 static const char* debuglvitem_t(const LVITEMW
*lpLVItem
, BOOL isW
)
638 char* buf
= debug_getbuf(), *text
= buf
;
639 int len
, size
= DEBUG_BUFFER_SIZE
;
641 if (lpLVItem
== NULL
) return "(null)";
642 len
= snprintf(buf
, size
, "{iItem=%d, iSubItem=%d, ", lpLVItem
->iItem
, lpLVItem
->iSubItem
);
643 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
644 if (lpLVItem
->mask
& LVIF_STATE
)
645 len
= snprintf(buf
, size
, "state=%x, stateMask=%x, ", lpLVItem
->state
, lpLVItem
->stateMask
);
647 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
648 if (lpLVItem
->mask
& LVIF_TEXT
)
649 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem
->pszText
, isW
, 80), lpLVItem
->cchTextMax
);
651 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
652 if (lpLVItem
->mask
& LVIF_IMAGE
)
653 len
= snprintf(buf
, size
, "iImage=%d, ", lpLVItem
->iImage
);
655 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
656 if (lpLVItem
->mask
& LVIF_PARAM
)
657 len
= snprintf(buf
, size
, "lParam=%lx, ", lpLVItem
->lParam
);
659 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
660 if (lpLVItem
->mask
& LVIF_INDENT
)
661 len
= snprintf(buf
, size
, "iIndent=%d, ", lpLVItem
->iIndent
);
663 if (len
== -1) goto end
; buf
+= len
;
666 buf
= text
+ strlen(text
);
668 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
672 static const char* debuglvcolumn_t(const LVCOLUMNW
*lpColumn
, BOOL isW
)
674 char* buf
= debug_getbuf(), *text
= buf
;
675 int len
, size
= DEBUG_BUFFER_SIZE
;
677 if (lpColumn
== NULL
) return "(null)";
678 len
= snprintf(buf
, size
, "{");
679 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
680 if (lpColumn
->mask
& LVCF_SUBITEM
)
681 len
= snprintf(buf
, size
, "iSubItem=%d, ", lpColumn
->iSubItem
);
683 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
684 if (lpColumn
->mask
& LVCF_FMT
)
685 len
= snprintf(buf
, size
, "fmt=%x, ", lpColumn
->fmt
);
687 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
688 if (lpColumn
->mask
& LVCF_WIDTH
)
689 len
= snprintf(buf
, size
, "cx=%d, ", lpColumn
->cx
);
691 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
692 if (lpColumn
->mask
& LVCF_TEXT
)
693 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn
->pszText
, isW
, 80), lpColumn
->cchTextMax
);
695 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
696 if (lpColumn
->mask
& LVCF_IMAGE
)
697 len
= snprintf(buf
, size
, "iImage=%d, ", lpColumn
->iImage
);
699 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
700 if (lpColumn
->mask
& LVCF_ORDER
)
701 len
= snprintf(buf
, size
, "iOrder=%d, ", lpColumn
->iOrder
);
703 if (len
== -1) goto end
; buf
+= len
;
706 buf
= text
+ strlen(text
);
708 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
712 static const char* debuglvhittestinfo(const LVHITTESTINFO
*lpht
)
714 if (!lpht
) return "(null)";
716 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
717 wine_dbgstr_point(&lpht
->pt
), lpht
->flags
, lpht
->iItem
, lpht
->iSubItem
);
720 /* Return the corresponding text for a given scroll value */
721 static inline LPCSTR
debugscrollcode(int nScrollCode
)
725 case SB_LINELEFT
: return "SB_LINELEFT";
726 case SB_LINERIGHT
: return "SB_LINERIGHT";
727 case SB_PAGELEFT
: return "SB_PAGELEFT";
728 case SB_PAGERIGHT
: return "SB_PAGERIGHT";
729 case SB_THUMBPOSITION
: return "SB_THUMBPOSITION";
730 case SB_THUMBTRACK
: return "SB_THUMBTRACK";
731 case SB_ENDSCROLL
: return "SB_ENDSCROLL";
732 case SB_INTERNAL
: return "SB_INTERNAL";
733 default: return "unknown";
738 /******** Notification functions ************************************/
740 static int get_ansi_notification(UINT unicodeNotificationCode
)
742 switch (unicodeNotificationCode
)
744 case LVN_BEGINLABELEDITA
:
745 case LVN_BEGINLABELEDITW
: return LVN_BEGINLABELEDITA
;
746 case LVN_ENDLABELEDITA
:
747 case LVN_ENDLABELEDITW
: return LVN_ENDLABELEDITA
;
748 case LVN_GETDISPINFOA
:
749 case LVN_GETDISPINFOW
: return LVN_GETDISPINFOA
;
750 case LVN_SETDISPINFOA
:
751 case LVN_SETDISPINFOW
: return LVN_SETDISPINFOA
;
752 case LVN_ODFINDITEMA
:
753 case LVN_ODFINDITEMW
: return LVN_ODFINDITEMA
;
754 case LVN_GETINFOTIPA
:
755 case LVN_GETINFOTIPW
: return LVN_GETINFOTIPA
;
756 /* header forwards */
758 case HDN_TRACKW
: return HDN_TRACKA
;
760 case HDN_ENDTRACKW
: return HDN_ENDTRACKA
;
761 case HDN_BEGINDRAG
: return HDN_BEGINDRAG
;
762 case HDN_ENDDRAG
: return HDN_ENDDRAG
;
763 case HDN_ITEMCHANGINGA
:
764 case HDN_ITEMCHANGINGW
: return HDN_ITEMCHANGINGA
;
765 case HDN_ITEMCHANGEDA
:
766 case HDN_ITEMCHANGEDW
: return HDN_ITEMCHANGEDA
;
768 case HDN_ITEMCLICKW
: return HDN_ITEMCLICKA
;
769 case HDN_DIVIDERDBLCLICKA
:
770 case HDN_DIVIDERDBLCLICKW
: return HDN_DIVIDERDBLCLICKA
;
773 FIXME("unknown notification %x\n", unicodeNotificationCode
);
774 return unicodeNotificationCode
;
777 /* forwards header notifications to listview parent */
778 static LRESULT
notify_forward_header(const LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
782 HD_TEXTFILTERA textfilter
;
783 LPSTR text
= NULL
, filter
= NULL
;
786 /* on unicode format exit earlier */
787 if (infoPtr
->notifyFormat
== NFR_UNICODE
)
788 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, lpnmh
->hdr
.idFrom
,
791 /* header always supplies unicode notifications,
792 all we have to do is to convert strings to ANSI */
793 nmhA
= *(const NMHEADERA
*)lpnmh
;
796 hditema
= *(HDITEMA
*)lpnmh
->pitem
;
797 nmhA
.pitem
= &hditema
;
798 /* convert item text */
799 if (lpnmh
->pitem
->mask
& HDI_TEXT
)
801 hditema
.pszText
= NULL
;
802 Str_SetPtrWtoA(&hditema
.pszText
, lpnmh
->pitem
->pszText
);
803 text
= hditema
.pszText
;
805 /* convert filter text */
806 if ((lpnmh
->pitem
->mask
& HDI_FILTER
) && (lpnmh
->pitem
->type
== HDFT_ISSTRING
) &&
807 lpnmh
->pitem
->pvFilter
)
809 hditema
.pvFilter
= &textfilter
;
810 textfilter
= *(HD_TEXTFILTERA
*)(lpnmh
->pitem
->pvFilter
);
811 textfilter
.pszText
= NULL
;
812 Str_SetPtrWtoA(&textfilter
.pszText
, ((HD_TEXTFILTERW
*)lpnmh
->pitem
->pvFilter
)->pszText
);
813 filter
= textfilter
.pszText
;
816 nmhA
.hdr
.code
= get_ansi_notification(lpnmh
->hdr
.code
);
818 ret
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, nmhA
.hdr
.idFrom
,
828 static LRESULT
notify_hdr(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
832 TRACE("(code=%d)\n", code
);
834 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
835 pnmh
->idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
837 result
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, pnmh
->idFrom
, (LPARAM
)pnmh
);
839 TRACE(" <= %ld\n", result
);
844 static inline BOOL
notify(const LISTVIEW_INFO
*infoPtr
, INT code
)
847 HWND hwnd
= infoPtr
->hwndSelf
;
848 notify_hdr(infoPtr
, code
, &nmh
);
849 return IsWindow(hwnd
);
852 static inline void notify_itemactivate(const LISTVIEW_INFO
*infoPtr
, const LVHITTESTINFO
*htInfo
)
863 item
.mask
= LVIF_PARAM
|LVIF_STATE
;
864 item
.iItem
= htInfo
->iItem
;
866 item
.stateMask
= (UINT
)-1;
867 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) {
868 nmia
.lParam
= item
.lParam
;
869 nmia
.uOldState
= item
.state
;
870 nmia
.uNewState
= item
.state
| LVIS_ACTIVATING
;
871 nmia
.uChanged
= LVIF_STATE
;
874 nmia
.iItem
= htInfo
->iItem
;
875 nmia
.iSubItem
= htInfo
->iSubItem
;
876 nmia
.ptAction
= htInfo
->pt
;
878 if (GetKeyState(VK_SHIFT
) & 0x8000) nmia
.uKeyFlags
|= LVKF_SHIFT
;
879 if (GetKeyState(VK_CONTROL
) & 0x8000) nmia
.uKeyFlags
|= LVKF_CONTROL
;
880 if (GetKeyState(VK_MENU
) & 0x8000) nmia
.uKeyFlags
|= LVKF_ALT
;
882 notify_hdr(infoPtr
, LVN_ITEMACTIVATE
, (LPNMHDR
)&nmia
);
885 static inline LRESULT
notify_listview(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
887 TRACE("(code=%d, plvnm=%s)\n", code
, debugnmlistview(plvnm
));
888 return notify_hdr(infoPtr
, code
, (LPNMHDR
)plvnm
);
891 static BOOL
notify_click(const LISTVIEW_INFO
*infoPtr
, INT code
, const LVHITTESTINFO
*lvht
)
895 HWND hwnd
= infoPtr
->hwndSelf
;
897 TRACE("code=%d, lvht=%s\n", code
, debuglvhittestinfo(lvht
));
898 ZeroMemory(&nmia
, sizeof(nmia
));
899 nmia
.iItem
= lvht
->iItem
;
900 nmia
.iSubItem
= lvht
->iSubItem
;
901 nmia
.ptAction
= lvht
->pt
;
902 item
.mask
= LVIF_PARAM
;
903 item
.iItem
= lvht
->iItem
;
905 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmia
.lParam
= item
.lParam
;
906 notify_hdr(infoPtr
, code
, (LPNMHDR
)&nmia
);
907 return IsWindow(hwnd
);
910 static BOOL
notify_deleteitem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
914 HWND hwnd
= infoPtr
->hwndSelf
;
916 ZeroMemory(&nmlv
, sizeof (NMLISTVIEW
));
918 item
.mask
= LVIF_PARAM
;
921 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
922 notify_listview(infoPtr
, LVN_DELETEITEM
, &nmlv
);
923 return IsWindow(hwnd
);
927 Send notification. depends on dispinfoW having same
928 structure as dispinfoA.
929 infoPtr : listview struct
930 code : *Unicode* notification code
931 pdi : dispinfo structure (can be unicode or ansi)
932 isW : TRUE if dispinfo is Unicode
934 static BOOL
notify_dispinfoT(const LISTVIEW_INFO
*infoPtr
, UINT code
, LPNMLVDISPINFOW pdi
, BOOL isW
)
936 INT length
= 0, ret_length
;
937 LPWSTR buffer
= NULL
, ret_text
;
938 BOOL return_ansi
= FALSE
;
939 BOOL return_unicode
= FALSE
;
942 if ((pdi
->item
.mask
& LVIF_TEXT
) && is_text(pdi
->item
.pszText
))
944 return_unicode
= ( isW
&& infoPtr
->notifyFormat
== NFR_ANSI
);
945 return_ansi
= (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
);
948 ret_length
= pdi
->item
.cchTextMax
;
949 ret_text
= pdi
->item
.pszText
;
951 if (return_unicode
|| return_ansi
)
953 if (code
!= LVN_GETDISPINFOW
)
955 length
= return_ansi
?
956 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
957 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
961 length
= pdi
->item
.cchTextMax
;
962 *pdi
->item
.pszText
= 0; /* make sure we don't process garbage */
965 buffer
= Alloc( (return_ansi
? sizeof(WCHAR
) : sizeof(CHAR
)) * length
);
966 if (!buffer
) return FALSE
;
969 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
972 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) buffer
,
975 pdi
->item
.pszText
= buffer
;
976 pdi
->item
.cchTextMax
= length
;
979 if (infoPtr
->notifyFormat
== NFR_ANSI
)
980 code
= get_ansi_notification(code
);
982 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi
->item
, infoPtr
->notifyFormat
!= NFR_ANSI
));
983 ret
= notify_hdr(infoPtr
, code
, &pdi
->hdr
);
984 TRACE(" resulting code=%d\n", pdi
->hdr
.code
);
986 if (return_ansi
|| return_unicode
)
988 if (return_ansi
&& (pdi
->hdr
.code
== LVN_GETDISPINFOA
))
990 strcpy((char*)ret_text
, (char*)pdi
->item
.pszText
);
992 else if (return_unicode
&& (pdi
->hdr
.code
== LVN_GETDISPINFOW
))
994 strcpyW(ret_text
, pdi
->item
.pszText
);
996 else if (return_ansi
) /* note : pointer can be changed by app ! */
998 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) ret_text
,
999 ret_length
, NULL
, NULL
);
1002 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
1003 ret_text
, ret_length
);
1005 pdi
->item
.pszText
= ret_text
; /* restores our buffer */
1006 pdi
->item
.cchTextMax
= ret_length
;
1012 /* if dipsinfo holder changed notification code then convert */
1013 if (!isW
&& (pdi
->hdr
.code
== LVN_GETDISPINFOW
) && (pdi
->item
.mask
& LVIF_TEXT
))
1015 length
= WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
1017 buffer
= Alloc(length
* sizeof(CHAR
));
1018 if (!buffer
) return FALSE
;
1020 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) buffer
,
1021 ret_length
, NULL
, NULL
);
1023 strcpy((LPSTR
)pdi
->item
.pszText
, (LPSTR
)buffer
);
1030 static void customdraw_fill(NMLVCUSTOMDRAW
*lpnmlvcd
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
,
1031 const RECT
*rcBounds
, const LVITEMW
*lplvItem
)
1033 ZeroMemory(lpnmlvcd
, sizeof(NMLVCUSTOMDRAW
));
1034 lpnmlvcd
->nmcd
.hdc
= hdc
;
1035 lpnmlvcd
->nmcd
.rc
= *rcBounds
;
1036 lpnmlvcd
->clrTextBk
= infoPtr
->clrTextBk
;
1037 lpnmlvcd
->clrText
= infoPtr
->clrText
;
1038 if (!lplvItem
) return;
1039 lpnmlvcd
->nmcd
.dwItemSpec
= lplvItem
->iItem
+ 1;
1040 lpnmlvcd
->iSubItem
= lplvItem
->iSubItem
;
1041 if (lplvItem
->state
& LVIS_SELECTED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_SELECTED
;
1042 if (lplvItem
->state
& LVIS_FOCUSED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_FOCUS
;
1043 if (lplvItem
->iItem
== infoPtr
->nHotItem
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_HOT
;
1044 lpnmlvcd
->nmcd
.lItemlParam
= lplvItem
->lParam
;
1047 static inline DWORD
notify_customdraw (const LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, NMLVCUSTOMDRAW
*lpnmlvcd
)
1049 BOOL isForItem
= (lpnmlvcd
->nmcd
.dwItemSpec
!= 0);
1052 lpnmlvcd
->nmcd
.dwDrawStage
= dwDrawStage
;
1053 if (isForItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_ITEM
;
1054 if (lpnmlvcd
->iSubItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_SUBITEM
;
1055 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
--;
1056 result
= notify_hdr(infoPtr
, NM_CUSTOMDRAW
, &lpnmlvcd
->nmcd
.hdr
);
1057 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
++;
1061 static void prepaint_setup (const LISTVIEW_INFO
*infoPtr
, HDC hdc
, NMLVCUSTOMDRAW
*lpnmlvcd
, BOOL SubItem
)
1063 if (lpnmlvcd
->clrTextBk
== CLR_DEFAULT
)
1064 lpnmlvcd
->clrTextBk
= comctl32_color
.clrWindow
;
1065 if (lpnmlvcd
->clrText
== CLR_DEFAULT
)
1066 lpnmlvcd
->clrText
= comctl32_color
.clrWindowText
;
1068 /* apparently, for selected items, we have to override the returned values */
1071 if (lpnmlvcd
->nmcd
.uItemState
& CDIS_SELECTED
)
1073 if (infoPtr
->bFocus
)
1075 lpnmlvcd
->clrTextBk
= comctl32_color
.clrHighlight
;
1076 lpnmlvcd
->clrText
= comctl32_color
.clrHighlightText
;
1078 else if (infoPtr
->dwStyle
& LVS_SHOWSELALWAYS
)
1080 lpnmlvcd
->clrTextBk
= comctl32_color
.clr3dFace
;
1081 lpnmlvcd
->clrText
= comctl32_color
.clrBtnText
;
1086 /* Set the text attributes */
1087 if (lpnmlvcd
->clrTextBk
!= CLR_NONE
)
1089 SetBkMode(hdc
, OPAQUE
);
1090 SetBkColor(hdc
,lpnmlvcd
->clrTextBk
);
1093 SetBkMode(hdc
, TRANSPARENT
);
1094 SetTextColor(hdc
, lpnmlvcd
->clrText
);
1097 static inline DWORD
notify_postpaint (const LISTVIEW_INFO
*infoPtr
, NMLVCUSTOMDRAW
*lpnmlvcd
)
1099 return notify_customdraw(infoPtr
, CDDS_POSTPAINT
, lpnmlvcd
);
1102 /* returns TRUE when repaint needed, FALSE otherwise */
1103 static BOOL
notify_measureitem(LISTVIEW_INFO
*infoPtr
)
1105 MEASUREITEMSTRUCT mis
;
1106 mis
.CtlType
= ODT_LISTVIEW
;
1107 mis
.CtlID
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1111 mis
.itemHeight
= infoPtr
->nItemHeight
;
1112 SendMessageW(infoPtr
->hwndNotify
, WM_MEASUREITEM
, mis
.CtlID
, (LPARAM
)&mis
);
1113 if (infoPtr
->nItemHeight
!= max(mis
.itemHeight
, 1))
1115 infoPtr
->nMeasureItemHeight
= infoPtr
->nItemHeight
= max(mis
.itemHeight
, 1);
1121 /******** Item iterator functions **********************************/
1123 static RANGES
ranges_create(int count
);
1124 static void ranges_destroy(RANGES ranges
);
1125 static BOOL
ranges_add(RANGES ranges
, RANGE range
);
1126 static BOOL
ranges_del(RANGES ranges
, RANGE range
);
1127 static void ranges_dump(RANGES ranges
);
1129 static inline BOOL
ranges_additem(RANGES ranges
, INT nItem
)
1131 RANGE range
= { nItem
, nItem
+ 1 };
1133 return ranges_add(ranges
, range
);
1136 static inline BOOL
ranges_delitem(RANGES ranges
, INT nItem
)
1138 RANGE range
= { nItem
, nItem
+ 1 };
1140 return ranges_del(ranges
, range
);
1144 * ITERATOR DOCUMENTATION
1146 * The iterator functions allow for easy, and convenient iteration
1147 * over items of interest in the list. Typically, you create a
1148 * iterator, use it, and destroy it, as such:
1151 * iterator_xxxitems(&i, ...);
1152 * while (iterator_{prev,next}(&i)
1154 * //code which uses i.nItem
1156 * iterator_destroy(&i);
1158 * where xxx is either: framed, or visible.
1159 * Note that it is important that the code destroys the iterator
1160 * after it's done with it, as the creation of the iterator may
1161 * allocate memory, which thus needs to be freed.
1163 * You can iterate both forwards, and backwards through the list,
1164 * by using iterator_next or iterator_prev respectively.
1166 * Lower numbered items are draw on top of higher number items in
1167 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1168 * items may overlap). So, to test items, you should use
1170 * which lists the items top to bottom (in Z-order).
1171 * For drawing items, you should use
1173 * which lists the items bottom to top (in Z-order).
1174 * If you keep iterating over the items after the end-of-items
1175 * marker (-1) is returned, the iterator will start from the
1176 * beginning. Typically, you don't need to test for -1,
1177 * because iterator_{next,prev} will return TRUE if more items
1178 * are to be iterated over, or FALSE otherwise.
1180 * Note: the iterator is defined to be bidirectional. That is,
1181 * any number of prev followed by any number of next, or
1182 * five versa, should leave the iterator at the same item:
1183 * prev * n, next * n = next * n, prev * n
1185 * The iterator has a notion of an out-of-order, special item,
1186 * which sits at the start of the list. This is used in
1187 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1188 * which needs to be first, as it may overlap other items.
1190 * The code is a bit messy because we have:
1191 * - a special item to deal with
1192 * - simple range, or composite range
1194 * If you find bugs, or want to add features, please make sure you
1195 * always check/modify *both* iterator_prev, and iterator_next.
1199 * This function iterates through the items in increasing order,
1200 * but prefixed by the special item, then -1. That is:
1201 * special, 1, 2, 3, ..., n, -1.
1202 * Each item is listed only once.
1204 static inline BOOL
iterator_next(ITERATOR
* i
)
1208 i
->nItem
= i
->nSpecial
;
1209 if (i
->nItem
!= -1) return TRUE
;
1211 if (i
->nItem
== i
->nSpecial
)
1213 if (i
->ranges
) i
->index
= 0;
1219 if (i
->nItem
== i
->nSpecial
) i
->nItem
++;
1220 if (i
->nItem
< i
->range
.upper
) return TRUE
;
1225 if (i
->index
< DPA_GetPtrCount(i
->ranges
->hdpa
))
1226 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, i
->index
++);
1229 else if (i
->nItem
>= i
->range
.upper
) goto end
;
1231 i
->nItem
= i
->range
.lower
;
1232 if (i
->nItem
>= 0) goto testitem
;
1239 * This function iterates through the items in decreasing order,
1240 * followed by the special item, then -1. That is:
1241 * n, n-1, ..., 3, 2, 1, special, -1.
1242 * Each item is listed only once.
1244 static inline BOOL
iterator_prev(ITERATOR
* i
)
1251 if (i
->ranges
) i
->index
= DPA_GetPtrCount(i
->ranges
->hdpa
);
1254 if (i
->nItem
== i
->nSpecial
)
1262 if (i
->nItem
== i
->nSpecial
) i
->nItem
--;
1263 if (i
->nItem
>= i
->range
.lower
) return TRUE
;
1269 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, --i
->index
);
1272 else if (!start
&& i
->nItem
< i
->range
.lower
) goto end
;
1274 i
->nItem
= i
->range
.upper
;
1275 if (i
->nItem
> 0) goto testitem
;
1277 return (i
->nItem
= i
->nSpecial
) != -1;
1280 static RANGE
iterator_range(const ITERATOR
*i
)
1284 if (!i
->ranges
) return i
->range
;
1286 if (DPA_GetPtrCount(i
->ranges
->hdpa
) > 0)
1288 range
.lower
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, 0)).lower
;
1289 range
.upper
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, DPA_GetPtrCount(i
->ranges
->hdpa
) - 1)).upper
;
1291 else range
.lower
= range
.upper
= 0;
1297 * Releases resources associated with this iterator.
1299 static inline void iterator_destroy(const ITERATOR
*i
)
1301 ranges_destroy(i
->ranges
);
1305 * Create an empty iterator.
1307 static inline BOOL
iterator_empty(ITERATOR
* i
)
1309 ZeroMemory(i
, sizeof(*i
));
1310 i
->nItem
= i
->nSpecial
= i
->range
.lower
= i
->range
.upper
= -1;
1315 * Create an iterator over a range.
1317 static inline BOOL
iterator_rangeitems(ITERATOR
* i
, RANGE range
)
1325 * Create an iterator over a bunch of ranges.
1326 * Please note that the iterator will take ownership of the ranges,
1327 * and will free them upon destruction.
1329 static inline BOOL
iterator_rangesitems(ITERATOR
* i
, RANGES ranges
)
1337 * Creates an iterator over the items which intersect frame.
1338 * Uses absolute coordinates rather than compensating for the current offset.
1340 static BOOL
iterator_frameditems_absolute(ITERATOR
* i
, const LISTVIEW_INFO
* infoPtr
, const RECT
*frame
)
1342 RECT rcItem
, rcTemp
;
1344 /* in case we fail, we want to return an empty iterator */
1345 if (!iterator_empty(i
)) return FALSE
;
1347 TRACE("(frame=%s)\n", wine_dbgstr_rect(frame
));
1349 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)
1353 if (infoPtr
->uView
== LV_VIEW_ICON
&& infoPtr
->nFocusedItem
!= -1)
1355 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcItem
);
1356 if (IntersectRect(&rcTemp
, &rcItem
, frame
))
1357 i
->nSpecial
= infoPtr
->nFocusedItem
;
1359 if (!(iterator_rangesitems(i
, ranges_create(50)))) return FALSE
;
1360 /* to do better here, we need to have PosX, and PosY sorted */
1361 TRACE("building icon ranges:\n");
1362 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
1364 rcItem
.left
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1365 rcItem
.top
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1366 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1367 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1368 if (IntersectRect(&rcTemp
, &rcItem
, frame
))
1369 ranges_additem(i
->ranges
, nItem
);
1373 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
1377 if (frame
->left
>= infoPtr
->nItemWidth
) return TRUE
;
1378 if (frame
->top
>= infoPtr
->nItemHeight
* infoPtr
->nItemCount
) return TRUE
;
1380 range
.lower
= max(frame
->top
/ infoPtr
->nItemHeight
, 0);
1381 range
.upper
= min((frame
->bottom
- 1) / infoPtr
->nItemHeight
, infoPtr
->nItemCount
- 1) + 1;
1382 if (range
.upper
<= range
.lower
) return TRUE
;
1383 if (!iterator_rangeitems(i
, range
)) return FALSE
;
1384 TRACE(" report=%s\n", debugrange(&i
->range
));
1388 INT nPerCol
= max((infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
, 1);
1389 INT nFirstRow
= max(frame
->top
/ infoPtr
->nItemHeight
, 0);
1390 INT nLastRow
= min((frame
->bottom
- 1) / infoPtr
->nItemHeight
, nPerCol
- 1);
1397 if (infoPtr
->nItemWidth
)
1399 nFirstCol
= max(frame
->left
/ infoPtr
->nItemWidth
, 0);
1400 nLastCol
= min((frame
->right
- 1) / infoPtr
->nItemWidth
, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1404 nFirstCol
= max(frame
->left
, 0);
1405 nLastCol
= min(frame
->right
- 1, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1408 lower
= nFirstCol
* nPerCol
+ nFirstRow
;
1410 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1411 nPerCol
, nFirstRow
, nLastRow
, nFirstCol
, nLastCol
, lower
);
1413 if (nLastCol
< nFirstCol
|| nLastRow
< nFirstRow
) return TRUE
;
1415 if (!(iterator_rangesitems(i
, ranges_create(nLastCol
- nFirstCol
+ 1)))) return FALSE
;
1416 TRACE("building list ranges:\n");
1417 for (nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++)
1419 item_range
.lower
= nCol
* nPerCol
+ nFirstRow
;
1420 if(item_range
.lower
>= infoPtr
->nItemCount
) break;
1421 item_range
.upper
= min(nCol
* nPerCol
+ nLastRow
+ 1, infoPtr
->nItemCount
);
1422 TRACE(" list=%s\n", debugrange(&item_range
));
1423 ranges_add(i
->ranges
, item_range
);
1431 * Creates an iterator over the items which intersect lprc.
1433 static BOOL
iterator_frameditems(ITERATOR
* i
, const LISTVIEW_INFO
* infoPtr
, const RECT
*lprc
)
1438 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc
));
1440 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1441 OffsetRect(&frame
, -Origin
.x
, -Origin
.y
);
1443 return iterator_frameditems_absolute(i
, infoPtr
, &frame
);
1447 * Creates an iterator over the items which intersect the visible region of hdc.
1449 static BOOL
iterator_visibleitems(ITERATOR
*i
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1451 POINT Origin
, Position
;
1452 RECT rcItem
, rcClip
;
1455 rgntype
= GetClipBox(hdc
, &rcClip
);
1456 if (rgntype
== NULLREGION
) return iterator_empty(i
);
1457 if (!iterator_frameditems(i
, infoPtr
, &rcClip
)) return FALSE
;
1458 if (rgntype
== SIMPLEREGION
) return TRUE
;
1460 /* first deal with the special item */
1461 if (i
->nSpecial
!= -1)
1463 LISTVIEW_GetItemBox(infoPtr
, i
->nSpecial
, &rcItem
);
1464 if (!RectVisible(hdc
, &rcItem
)) i
->nSpecial
= -1;
1467 /* if we can't deal with the region, we'll just go with the simple range */
1468 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1469 TRACE("building visible range:\n");
1470 if (!i
->ranges
&& i
->range
.lower
< i
->range
.upper
)
1472 if (!(i
->ranges
= ranges_create(50))) return TRUE
;
1473 if (!ranges_add(i
->ranges
, i
->range
))
1475 ranges_destroy(i
->ranges
);
1481 /* now delete the invisible items from the list */
1482 while(iterator_next(i
))
1484 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
1485 rcItem
.left
= (infoPtr
->uView
== LV_VIEW_DETAILS
) ? Origin
.x
: Position
.x
+ Origin
.x
;
1486 rcItem
.top
= Position
.y
+ Origin
.y
;
1487 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1488 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1489 if (!RectVisible(hdc
, &rcItem
))
1490 ranges_delitem(i
->ranges
, i
->nItem
);
1492 /* the iterator should restart on the next iterator_next */
1498 /* Remove common elements from two iterators */
1499 /* Passed iterators have to point on the first elements */
1500 static BOOL
iterator_remove_common_items(ITERATOR
*iter1
, ITERATOR
*iter2
)
1502 if(!iter1
->ranges
|| !iter2
->ranges
) {
1505 if(iter1
->ranges
|| iter2
->ranges
||
1506 (iter1
->range
.lower
<iter2
->range
.lower
&& iter1
->range
.upper
>iter2
->range
.upper
) ||
1507 (iter1
->range
.lower
>iter2
->range
.lower
&& iter1
->range
.upper
<iter2
->range
.upper
)) {
1508 ERR("result is not a one range iterator\n");
1512 if(iter1
->range
.lower
==-1 || iter2
->range
.lower
==-1)
1515 lower
= iter1
->range
.lower
;
1516 upper
= iter1
->range
.upper
;
1518 if(lower
< iter2
->range
.lower
)
1519 iter1
->range
.upper
= iter2
->range
.lower
;
1520 else if(upper
> iter2
->range
.upper
)
1521 iter1
->range
.lower
= iter2
->range
.upper
;
1523 iter1
->range
.lower
= iter1
->range
.upper
= -1;
1525 if(iter2
->range
.lower
< lower
)
1526 iter2
->range
.upper
= lower
;
1527 else if(iter2
->range
.upper
> upper
)
1528 iter2
->range
.lower
= upper
;
1530 iter2
->range
.lower
= iter2
->range
.upper
= -1;
1535 iterator_next(iter1
);
1536 iterator_next(iter2
);
1539 if(iter1
->nItem
==-1 || iter2
->nItem
==-1)
1542 if(iter1
->nItem
== iter2
->nItem
) {
1543 int delete = iter1
->nItem
;
1545 iterator_prev(iter1
);
1546 iterator_prev(iter2
);
1547 ranges_delitem(iter1
->ranges
, delete);
1548 ranges_delitem(iter2
->ranges
, delete);
1549 iterator_next(iter1
);
1550 iterator_next(iter2
);
1551 } else if(iter1
->nItem
> iter2
->nItem
)
1552 iterator_next(iter2
);
1554 iterator_next(iter1
);
1557 iter1
->nItem
= iter1
->range
.lower
= iter1
->range
.upper
= -1;
1558 iter2
->nItem
= iter2
->range
.lower
= iter2
->range
.upper
= -1;
1562 /******** Misc helper functions ************************************/
1564 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
1565 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
1567 if (isW
) return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
1568 else return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
1571 static inline BOOL
is_autoarrange(const LISTVIEW_INFO
*infoPtr
)
1573 return ((infoPtr
->dwStyle
& LVS_AUTOARRANGE
) || infoPtr
->bAutoarrange
) &&
1574 (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
);
1577 static void toggle_checkbox_state(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1579 DWORD state
= STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_STATEIMAGEMASK
));
1580 if(state
== 1 || state
== 2)
1584 lvitem
.state
= INDEXTOSTATEIMAGEMASK(state
);
1585 lvitem
.stateMask
= LVIS_STATEIMAGEMASK
;
1586 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvitem
);
1590 /* this should be called after window style got updated,
1591 it used to reset view state to match current window style */
1592 static inline void map_style_view(LISTVIEW_INFO
*infoPtr
)
1594 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
1597 infoPtr
->uView
= LV_VIEW_ICON
;
1600 infoPtr
->uView
= LV_VIEW_DETAILS
;
1603 infoPtr
->uView
= LV_VIEW_SMALLICON
;
1606 infoPtr
->uView
= LV_VIEW_LIST
;
1610 /* computes next item id value */
1611 static DWORD
get_next_itemid(const LISTVIEW_INFO
*infoPtr
)
1613 INT count
= DPA_GetPtrCount(infoPtr
->hdpaItemIds
);
1617 ITEM_ID
*lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, count
- 1);
1618 return lpID
->id
+ 1;
1623 /******** Internal API functions ************************************/
1625 static inline COLUMN_INFO
* LISTVIEW_GetColumnInfo(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
)
1627 static COLUMN_INFO mainItem
;
1629 if (nSubItem
== 0 && DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0) return &mainItem
;
1630 assert (nSubItem
>= 0 && nSubItem
< DPA_GetPtrCount(infoPtr
->hdpaColumns
));
1632 /* update cached column rectangles */
1633 if (infoPtr
->colRectsDirty
)
1636 LISTVIEW_INFO
*Ptr
= (LISTVIEW_INFO
*)infoPtr
;
1639 for (i
= 0; i
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); i
++) {
1640 info
= DPA_GetPtr(infoPtr
->hdpaColumns
, i
);
1641 SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMRECT
, i
, (LPARAM
)&info
->rcHeader
);
1643 Ptr
->colRectsDirty
= FALSE
;
1646 return DPA_GetPtr(infoPtr
->hdpaColumns
, nSubItem
);
1649 static INT
LISTVIEW_CreateHeader(LISTVIEW_INFO
*infoPtr
)
1651 DWORD dFlags
= WS_CHILD
| HDS_HORZ
| HDS_FULLDRAG
| HDS_DRAGDROP
;
1654 if (infoPtr
->hwndHeader
) return 0;
1656 TRACE("Creating header for list %p\n", infoPtr
->hwndSelf
);
1658 /* setup creation flags */
1659 dFlags
|= (LVS_NOSORTHEADER
& infoPtr
->dwStyle
) ? 0 : HDS_BUTTONS
;
1660 dFlags
|= (LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
) ? HDS_HIDDEN
: 0;
1662 hInst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
1665 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
, dFlags
,
1666 0, 0, 0, 0, infoPtr
->hwndSelf
, NULL
, hInst
, NULL
);
1667 if (!infoPtr
->hwndHeader
) return -1;
1669 /* set header unicode format */
1670 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, TRUE
, 0);
1672 /* set header font */
1673 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, TRUE
);
1675 LISTVIEW_UpdateSize(infoPtr
);
1680 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
, LPRECT lprc
)
1682 *lprc
= LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->rcHeader
;
1685 static inline BOOL
LISTVIEW_IsHeaderEnabled(const LISTVIEW_INFO
*infoPtr
)
1687 return (infoPtr
->uView
== LV_VIEW_DETAILS
||
1688 infoPtr
->dwLvExStyle
& LVS_EX_HEADERINALLVIEWS
) &&
1689 !(infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
);
1692 static inline BOOL
LISTVIEW_GetItemW(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
1694 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
1697 /* used to handle collapse main item column case */
1698 static inline BOOL
LISTVIEW_DrawFocusRect(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1702 if (infoPtr
->rcFocus
.left
< infoPtr
->rcFocus
.right
)
1704 DWORD dwOldBkColor
, dwOldTextColor
;
1706 dwOldBkColor
= SetBkColor(hdc
, RGB(255, 255, 255));
1707 dwOldTextColor
= SetBkColor(hdc
, RGB(0, 0, 0));
1708 Ret
= DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
1709 SetBkColor(hdc
, dwOldBkColor
);
1710 SetBkColor(hdc
, dwOldTextColor
);
1715 /* Listview invalidation functions: use _only_ these functions to invalidate */
1717 static inline BOOL
is_redrawing(const LISTVIEW_INFO
*infoPtr
)
1719 return infoPtr
->bRedraw
;
1722 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO
*infoPtr
, const RECT
* rect
)
1724 if(!is_redrawing(infoPtr
)) return;
1725 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect
));
1726 InvalidateRect(infoPtr
->hwndSelf
, rect
, TRUE
);
1729 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
1733 if(!is_redrawing(infoPtr
)) return;
1734 LISTVIEW_GetItemBox(infoPtr
, nItem
, &rcBox
);
1735 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1738 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
)
1740 POINT Origin
, Position
;
1743 if(!is_redrawing(infoPtr
)) return;
1744 assert (infoPtr
->uView
== LV_VIEW_DETAILS
);
1745 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1746 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
1747 LISTVIEW_GetHeaderRect(infoPtr
, nSubItem
, &rcBox
);
1749 rcBox
.bottom
= infoPtr
->nItemHeight
;
1750 OffsetRect(&rcBox
, Origin
.x
+ Position
.x
, Origin
.y
+ Position
.y
);
1751 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1754 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO
*infoPtr
)
1756 LISTVIEW_InvalidateRect(infoPtr
, NULL
);
1759 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
1763 if(!is_redrawing(infoPtr
)) return;
1764 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
1765 rcCol
.top
= infoPtr
->rcList
.top
;
1766 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
1767 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
1772 * Retrieves the number of items that can fit vertically in the client area.
1775 * [I] infoPtr : valid pointer to the listview structure
1778 * Number of items per row.
1780 static inline INT
LISTVIEW_GetCountPerRow(const LISTVIEW_INFO
*infoPtr
)
1782 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1784 return max(nListWidth
/(infoPtr
->nItemWidth
? infoPtr
->nItemWidth
: 1), 1);
1789 * Retrieves the number of items that can fit horizontally in the client
1793 * [I] infoPtr : valid pointer to the listview structure
1796 * Number of items per column.
1798 static inline INT
LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO
*infoPtr
)
1800 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1802 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
1806 /*************************************************************************
1807 * LISTVIEW_ProcessLetterKeys
1809 * Processes keyboard messages generated by pressing the letter keys
1811 * What this does is perform a case insensitive search from the
1812 * current position with the following quirks:
1813 * - If two chars or more are pressed in quick succession we search
1814 * for the corresponding string (e.g. 'abc').
1815 * - If there is a delay we wipe away the current search string and
1816 * restart with just that char.
1817 * - If the user keeps pressing the same character, whether slowly or
1818 * fast, so that the search string is entirely composed of this
1819 * character ('aaaaa' for instance), then we search for first item
1820 * that starting with that character.
1821 * - If the user types the above character in quick succession, then
1822 * we must also search for the corresponding string ('aaaaa'), and
1823 * go to that string if there is a match.
1826 * [I] hwnd : handle to the window
1827 * [I] charCode : the character code, the actual character
1828 * [I] keyData : key data
1836 * - The current implementation has a list of characters it will
1837 * accept and it ignores everything else. In particular it will
1838 * ignore accentuated characters which seems to match what
1839 * Windows does. But I'm not sure it makes sense to follow
1841 * - We don't sound a beep when the search fails.
1845 * TREEVIEW_ProcessLetterKeys
1847 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
1849 WCHAR buffer
[MAX_PATH
];
1850 INT endidx
, startidx
;
1856 /* simple parameter checking */
1857 if (!charCode
|| !keyData
|| infoPtr
->nItemCount
== 0) return 0;
1859 /* only allow the valid WM_CHARs through */
1860 if (!isalnumW(charCode
) &&
1861 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
1862 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
1863 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
1864 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
1865 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
1866 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
1867 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
1868 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
1869 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
1872 /* update the search parameters */
1873 prevTime
= infoPtr
->lastKeyPressTimestamp
;
1874 infoPtr
->lastKeyPressTimestamp
= GetTickCount();
1875 diff
= infoPtr
->lastKeyPressTimestamp
- prevTime
;
1877 if (diff
>= 0 && diff
< KEY_DELAY
)
1879 if (infoPtr
->nSearchParamLength
< MAX_PATH
- 1)
1880 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++] = charCode
;
1882 if (infoPtr
->charCode
!= charCode
)
1883 infoPtr
->charCode
= charCode
= 0;
1887 infoPtr
->charCode
= charCode
;
1888 infoPtr
->szSearchParam
[0] = charCode
;
1889 infoPtr
->nSearchParamLength
= 1;
1892 /* and search from the current position */
1894 endidx
= infoPtr
->nItemCount
;
1896 /* should start from next after focused item, so next item that matches
1897 will be selected, if there isn't any and focused matches it will be selected
1898 on second search stage from beginning of the list */
1899 if (infoPtr
->nFocusedItem
>= 0 && infoPtr
->nItemCount
> 1)
1900 startidx
= infoPtr
->nFocusedItem
+ 1;
1904 /* let application handle this for virtual listview */
1905 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
1909 memset(&nmlv
.lvfi
, 0, sizeof(nmlv
.lvfi
));
1910 nmlv
.lvfi
.flags
= (LVFI_WRAP
| LVFI_PARTIAL
);
1911 nmlv
.lvfi
.psz
= infoPtr
->szSearchParam
;
1912 nmlv
.iStart
= startidx
;
1914 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
] = 0;
1916 nItem
= notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
1922 /* first search in [startidx, endidx), on failure continue in [0, startidx) */
1925 /* start from first item if not found with >= startidx */
1926 if (i
== infoPtr
->nItemCount
&& startidx
> 0)
1932 for (i
= startidx
; i
< endidx
; i
++)
1935 item
.mask
= LVIF_TEXT
;
1938 item
.pszText
= buffer
;
1939 item
.cchTextMax
= MAX_PATH
;
1940 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
1942 if (lstrncmpiW(item
.pszText
, infoPtr
->szSearchParam
, infoPtr
->nSearchParamLength
) == 0)
1947 else if (nItem
== -1 && lstrncmpiW(item
.pszText
, infoPtr
->szSearchParam
, 1) == 0)
1949 /* this would work but we must keep looking for a longer match */
1954 if ( nItem
!= -1 || /* found something */
1955 endidx
!= infoPtr
->nItemCount
|| /* second search done */
1956 (startidx
== 0 && endidx
== infoPtr
->nItemCount
) /* full range for first search */ )
1962 LISTVIEW_KeySelection(infoPtr
, nItem
, FALSE
);
1967 /*************************************************************************
1968 * LISTVIEW_UpdateHeaderSize [Internal]
1970 * Function to resize the header control
1973 * [I] hwnd : handle to a window
1974 * [I] nNewScrollPos : scroll pos to set
1979 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
1984 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
1986 if (!infoPtr
->hwndHeader
) return;
1988 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
1989 point
[0].x
= winRect
.left
;
1990 point
[0].y
= winRect
.top
;
1991 point
[1].x
= winRect
.right
;
1992 point
[1].y
= winRect
.bottom
;
1994 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
1995 point
[0].x
= -nNewScrollPos
;
1996 point
[1].x
+= nNewScrollPos
;
1998 SetWindowPos(infoPtr
->hwndHeader
,0,
1999 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
2000 (infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
|
2001 SWP_NOZORDER
| SWP_NOACTIVATE
);
2006 * Update the scrollbars. This functions should be called whenever
2007 * the content, size or view changes.
2010 * [I] infoPtr : valid pointer to the listview structure
2015 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO
*infoPtr
)
2017 SCROLLINFO horzInfo
, vertInfo
;
2020 if ((infoPtr
->dwStyle
& LVS_NOSCROLL
) || !is_redrawing(infoPtr
)) return;
2022 ZeroMemory(&horzInfo
, sizeof(SCROLLINFO
));
2023 horzInfo
.cbSize
= sizeof(SCROLLINFO
);
2024 horzInfo
.nPage
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2026 /* for now, we'll set info.nMax to the _count_, and adjust it later */
2027 if (infoPtr
->uView
== LV_VIEW_LIST
)
2029 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
2030 horzInfo
.nMax
= (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
;
2032 /* scroll by at least one column per page */
2033 if(horzInfo
.nPage
< infoPtr
->nItemWidth
)
2034 horzInfo
.nPage
= infoPtr
->nItemWidth
;
2036 if (infoPtr
->nItemWidth
)
2037 horzInfo
.nPage
/= infoPtr
->nItemWidth
;
2039 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2041 horzInfo
.nMax
= infoPtr
->nItemWidth
;
2043 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2047 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) horzInfo
.nMax
= rcView
.right
- rcView
.left
;
2050 if (LISTVIEW_IsHeaderEnabled(infoPtr
))
2052 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
))
2057 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
2058 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
2060 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcHeader
);
2061 horzInfo
.nMax
= rcHeader
.right
;
2062 TRACE("horzInfo.nMax=%d\n", horzInfo
.nMax
);
2066 horzInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
2067 horzInfo
.nMax
= max(horzInfo
.nMax
- 1, 0);
2068 dx
= GetScrollPos(infoPtr
->hwndSelf
, SB_HORZ
);
2069 dx
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
, TRUE
);
2070 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo
));
2072 /* Setting the horizontal scroll can change the listview size
2073 * (and potentially everything else) so we need to recompute
2074 * everything again for the vertical scroll
2077 ZeroMemory(&vertInfo
, sizeof(SCROLLINFO
));
2078 vertInfo
.cbSize
= sizeof(SCROLLINFO
);
2079 vertInfo
.nPage
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2081 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2083 vertInfo
.nMax
= infoPtr
->nItemCount
;
2085 /* scroll by at least one page */
2086 if(vertInfo
.nPage
< infoPtr
->nItemHeight
)
2087 vertInfo
.nPage
= infoPtr
->nItemHeight
;
2089 if (infoPtr
->nItemHeight
> 0)
2090 vertInfo
.nPage
/= infoPtr
->nItemHeight
;
2092 else if (infoPtr
->uView
!= LV_VIEW_LIST
) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2096 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) vertInfo
.nMax
= rcView
.bottom
- rcView
.top
;
2099 vertInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
2100 vertInfo
.nMax
= max(vertInfo
.nMax
- 1, 0);
2101 dy
= GetScrollPos(infoPtr
->hwndSelf
, SB_VERT
);
2102 dy
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &vertInfo
, TRUE
);
2103 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo
));
2105 /* Change of the range may have changed the scroll pos. If so move the content */
2106 if (dx
!= 0 || dy
!= 0)
2109 listRect
= infoPtr
->rcList
;
2110 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &listRect
, &listRect
, 0, 0,
2111 SW_ERASE
| SW_INVALIDATE
);
2114 /* Update the Header Control */
2115 if (infoPtr
->hwndHeader
)
2117 horzInfo
.fMask
= SIF_POS
;
2118 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
);
2119 LISTVIEW_UpdateHeaderSize(infoPtr
, horzInfo
.nPos
);
2126 * Shows/hides the focus rectangle.
2129 * [I] infoPtr : valid pointer to the listview structure
2130 * [I] fShow : TRUE to show the focus, FALSE to hide it.
2135 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO
*infoPtr
, BOOL fShow
)
2139 TRACE("fShow=%d, nItem=%d\n", fShow
, infoPtr
->nFocusedItem
);
2141 if (infoPtr
->nFocusedItem
< 0) return;
2143 /* we need some gymnastics in ICON mode to handle large items */
2144 if (infoPtr
->uView
== LV_VIEW_ICON
)
2148 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcBox
);
2149 if ((rcBox
.bottom
- rcBox
.top
) > infoPtr
->nItemHeight
)
2151 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
2156 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) return;
2158 /* for some reason, owner draw should work only in report mode */
2159 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
2164 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2165 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2167 item
.iItem
= infoPtr
->nFocusedItem
;
2169 item
.mask
= LVIF_PARAM
;
2170 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto done
;
2172 ZeroMemory(&dis
, sizeof(dis
));
2173 dis
.CtlType
= ODT_LISTVIEW
;
2174 dis
.CtlID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
2175 dis
.itemID
= item
.iItem
;
2176 dis
.itemAction
= ODA_FOCUS
;
2177 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
2178 dis
.hwndItem
= infoPtr
->hwndSelf
;
2180 LISTVIEW_GetItemBox(infoPtr
, dis
.itemID
, &dis
.rcItem
);
2181 dis
.itemData
= item
.lParam
;
2183 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
2185 SelectObject(hdc
, hOldFont
);
2189 LISTVIEW_DrawFocusRect(infoPtr
, hdc
);
2192 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2196 * Invalidates all visible selected items.
2198 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO
*infoPtr
)
2202 iterator_frameditems(&i
, infoPtr
, &infoPtr
->rcList
);
2203 while(iterator_next(&i
))
2205 if (LISTVIEW_GetItemState(infoPtr
, i
.nItem
, LVIS_SELECTED
))
2206 LISTVIEW_InvalidateItem(infoPtr
, i
.nItem
);
2208 iterator_destroy(&i
);
2213 * DESCRIPTION: [INTERNAL]
2214 * Computes an item's (left,top) corner, relative to rcView.
2215 * That is, the position has NOT been made relative to the Origin.
2216 * This is deliberate, to avoid computing the Origin over, and
2217 * over again, when this function is called in a loop. Instead,
2218 * one can factor the computation of the Origin before the loop,
2219 * and offset the value returned by this function, on every iteration.
2222 * [I] infoPtr : valid pointer to the listview structure
2223 * [I] nItem : item number
2224 * [O] lpptOrig : item top, left corner
2229 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
2231 assert(nItem
>= 0 && nItem
< infoPtr
->nItemCount
);
2233 if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
2235 lpptPosition
->x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2236 lpptPosition
->y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2238 else if (infoPtr
->uView
== LV_VIEW_LIST
)
2240 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
2241 lpptPosition
->x
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
2242 lpptPosition
->y
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
2244 else /* LV_VIEW_DETAILS */
2246 lpptPosition
->x
= REPORT_MARGINX
;
2247 /* item is always at zero indexed column */
2248 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2249 lpptPosition
->x
+= LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
;
2250 lpptPosition
->y
= nItem
* infoPtr
->nItemHeight
;
2255 * DESCRIPTION: [INTERNAL]
2256 * Compute the rectangles of an item. This is to localize all
2257 * the computations in one place. If you are not interested in some
2258 * of these values, simply pass in a NULL -- the function is smart
2259 * enough to compute only what's necessary. The function computes
2260 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
2261 * one, the BOX rectangle. This rectangle is very cheap to compute,
2262 * and is guaranteed to contain all the other rectangles. Computing
2263 * the ICON rect is also cheap, but all the others are potentially
2264 * expensive. This gives an easy and effective optimization when
2265 * searching (like point inclusion, or rectangle intersection):
2266 * first test against the BOX, and if TRUE, test against the desired
2268 * If the function does not have all the necessary information
2269 * to computed the requested rectangles, will crash with a
2270 * failed assertion. This is done so we catch all programming
2271 * errors, given that the function is called only from our code.
2273 * We have the following 'special' meanings for a few fields:
2274 * * If LVIS_FOCUSED is set, we assume the item has the focus
2275 * This is important in ICON mode, where it might get a larger
2276 * then usual rectangle
2278 * Please note that subitem support works only in REPORT mode.
2281 * [I] infoPtr : valid pointer to the listview structure
2282 * [I] lpLVItem : item to compute the measures for
2283 * [O] lprcBox : ptr to Box rectangle
2284 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
2285 * [0] lprcSelectBox : ptr to select box rectangle
2286 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
2287 * [O] lprcIcon : ptr to Icon rectangle
2288 * Same as LVM_GETITEMRECT with LVIR_ICON
2289 * [O] lprcStateIcon: ptr to State Icon rectangle
2290 * [O] lprcLabel : ptr to Label rectangle
2291 * Same as LVM_GETITEMRECT with LVIR_LABEL
2296 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
,
2297 LPRECT lprcBox
, LPRECT lprcSelectBox
,
2298 LPRECT lprcIcon
, LPRECT lprcStateIcon
, LPRECT lprcLabel
)
2300 BOOL doSelectBox
= FALSE
, doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
2301 RECT Box
, SelectBox
, Icon
, Label
;
2302 COLUMN_INFO
*lpColumnInfo
= NULL
;
2303 SIZE labelSize
= { 0, 0 };
2305 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem
, TRUE
));
2307 /* Be smart and try to figure out the minimum we have to do */
2308 if (lpLVItem
->iSubItem
) assert(infoPtr
->uView
== LV_VIEW_DETAILS
);
2309 if (infoPtr
->uView
== LV_VIEW_ICON
&& (lprcBox
|| lprcLabel
))
2311 assert((lpLVItem
->mask
& LVIF_STATE
) && (lpLVItem
->stateMask
& LVIS_FOCUSED
));
2312 if (lpLVItem
->state
& LVIS_FOCUSED
) oversizedBox
= doLabel
= TRUE
;
2314 if (lprcSelectBox
) doSelectBox
= TRUE
;
2315 if (lprcLabel
) doLabel
= TRUE
;
2316 if (doLabel
|| lprcIcon
|| lprcStateIcon
) doIcon
= TRUE
;
2323 /************************************************************/
2324 /* compute the box rectangle (it should be cheap to do) */
2325 /************************************************************/
2326 if (lpLVItem
->iSubItem
|| infoPtr
->uView
== LV_VIEW_DETAILS
)
2327 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpLVItem
->iSubItem
);
2329 if (lpLVItem
->iSubItem
)
2331 Box
= lpColumnInfo
->rcHeader
;
2336 Box
.right
= infoPtr
->nItemWidth
;
2339 Box
.bottom
= infoPtr
->nItemHeight
;
2341 /******************************************************************/
2342 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2343 /******************************************************************/
2346 LONG state_width
= 0;
2348 if (infoPtr
->himlState
&& lpLVItem
->iSubItem
== 0)
2349 state_width
= infoPtr
->iconStateSize
.cx
;
2351 if (infoPtr
->uView
== LV_VIEW_ICON
)
2353 Icon
.left
= Box
.left
+ state_width
;
2354 if (infoPtr
->himlNormal
)
2355 Icon
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
- state_width
) / 2;
2356 Icon
.top
= Box
.top
+ ICON_TOP_PADDING
;
2357 Icon
.right
= Icon
.left
;
2358 Icon
.bottom
= Icon
.top
;
2359 if (infoPtr
->himlNormal
)
2361 Icon
.right
+= infoPtr
->iconSize
.cx
;
2362 Icon
.bottom
+= infoPtr
->iconSize
.cy
;
2365 else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2367 Icon
.left
= Box
.left
+ state_width
;
2369 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& lpLVItem
->iSubItem
== 0)
2371 /* we need the indent in report mode */
2372 assert(lpLVItem
->mask
& LVIF_INDENT
);
2373 Icon
.left
+= infoPtr
->iconSize
.cx
* lpLVItem
->iIndent
+ REPORT_MARGINX
;
2377 Icon
.right
= Icon
.left
;
2378 if (infoPtr
->himlSmall
&&
2379 (!lpColumnInfo
|| lpLVItem
->iSubItem
== 0 || (lpColumnInfo
->fmt
& LVCFMT_IMAGE
) ||
2380 ((infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
) && lpLVItem
->iImage
!= I_IMAGECALLBACK
)))
2381 Icon
.right
+= infoPtr
->iconSize
.cx
;
2382 Icon
.bottom
= Icon
.top
+ infoPtr
->iconSize
.cy
;
2384 if(lprcIcon
) *lprcIcon
= Icon
;
2385 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon
));
2387 /* TODO: is this correct? */
2390 lprcStateIcon
->left
= Icon
.left
- state_width
;
2391 lprcStateIcon
->right
= Icon
.left
;
2392 lprcStateIcon
->top
= Icon
.top
;
2393 lprcStateIcon
->bottom
= lprcStateIcon
->top
+ infoPtr
->iconSize
.cy
;
2394 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon
));
2397 else Icon
.right
= 0;
2399 /************************************************************/
2400 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2401 /************************************************************/
2404 /* calculate how far to the right can the label stretch */
2405 Label
.right
= Box
.right
;
2406 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2408 if (lpLVItem
->iSubItem
== 0)
2410 /* we need a zero based rect here */
2411 Label
= lpColumnInfo
->rcHeader
;
2412 OffsetRect(&Label
, -Label
.left
, 0);
2416 if (lpLVItem
->iSubItem
|| ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && infoPtr
->uView
== LV_VIEW_DETAILS
))
2418 labelSize
.cx
= infoPtr
->nItemWidth
;
2419 labelSize
.cy
= infoPtr
->nItemHeight
;
2423 /* we need the text in non owner draw mode */
2424 assert(lpLVItem
->mask
& LVIF_TEXT
);
2425 if (is_text(lpLVItem
->pszText
))
2427 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2428 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2429 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2433 /* compute rough rectangle where the label will go */
2434 SetRectEmpty(&rcText
);
2435 rcText
.right
= infoPtr
->nItemWidth
- TRAILING_LABEL_PADDING
;
2436 rcText
.bottom
= infoPtr
->nItemHeight
;
2437 if (infoPtr
->uView
== LV_VIEW_ICON
)
2438 rcText
.bottom
-= ICON_TOP_PADDING
+ infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2440 /* now figure out the flags */
2441 if (infoPtr
->uView
== LV_VIEW_ICON
)
2442 uFormat
= oversizedBox
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
;
2444 uFormat
= LV_SL_DT_FLAGS
;
2446 DrawTextW (hdc
, lpLVItem
->pszText
, -1, &rcText
, uFormat
| DT_CALCRECT
);
2448 if (rcText
.right
!= rcText
.left
)
2449 labelSize
.cx
= min(rcText
.right
- rcText
.left
+ TRAILING_LABEL_PADDING
, infoPtr
->nItemWidth
);
2451 labelSize
.cy
= rcText
.bottom
- rcText
.top
;
2453 SelectObject(hdc
, hOldFont
);
2454 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2458 if (infoPtr
->uView
== LV_VIEW_ICON
)
2460 Label
.left
= Box
.left
+ (infoPtr
->nItemWidth
- labelSize
.cx
) / 2;
2461 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
2462 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2463 Label
.right
= Label
.left
+ labelSize
.cx
;
2464 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2465 if (!oversizedBox
&& labelSize
.cy
> infoPtr
->ntmHeight
)
2467 labelSize
.cy
= min(Box
.bottom
- Label
.top
, labelSize
.cy
);
2468 labelSize
.cy
/= infoPtr
->ntmHeight
;
2469 labelSize
.cy
= max(labelSize
.cy
, 1);
2470 labelSize
.cy
*= infoPtr
->ntmHeight
;
2472 Label
.bottom
= Label
.top
+ labelSize
.cy
+ HEIGHT_PADDING
;
2474 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2476 Label
.left
= Icon
.right
;
2477 Label
.top
= Box
.top
;
2478 Label
.right
= lpLVItem
->iSubItem
? lpColumnInfo
->rcHeader
.right
:
2479 lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
2480 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2482 else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2484 Label
.left
= Icon
.right
;
2485 Label
.top
= Box
.top
;
2486 Label
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2487 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2490 if (lprcLabel
) *lprcLabel
= Label
;
2491 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label
));
2494 /************************************************************/
2495 /* compute SELECT bounding box */
2496 /************************************************************/
2499 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2501 SelectBox
.left
= Icon
.left
;
2502 SelectBox
.top
= Box
.top
;
2503 SelectBox
.bottom
= Box
.bottom
;
2506 SelectBox
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2508 SelectBox
.right
= min(Label
.left
+ MAX_EMPTYTEXT_SELECT_WIDTH
, Label
.right
);
2512 UnionRect(&SelectBox
, &Icon
, &Label
);
2514 if (lprcSelectBox
) *lprcSelectBox
= SelectBox
;
2515 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox
));
2518 /* Fix the Box if necessary */
2521 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
2522 else *lprcBox
= Box
;
2524 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box
));
2528 * DESCRIPTION: [INTERNAL]
2531 * [I] infoPtr : valid pointer to the listview structure
2532 * [I] nItem : item number
2533 * [O] lprcBox : ptr to Box rectangle
2538 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprcBox
)
2540 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2541 POINT Position
, Origin
;
2544 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
2545 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
2547 /* Be smart and try to figure out the minimum we have to do */
2549 if (infoPtr
->uView
== LV_VIEW_ICON
&& infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
2550 lvItem
.mask
|= LVIF_TEXT
;
2551 lvItem
.iItem
= nItem
;
2552 lvItem
.iSubItem
= 0;
2553 lvItem
.pszText
= szDispText
;
2554 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2555 if (lvItem
.mask
) LISTVIEW_GetItemW(infoPtr
, &lvItem
);
2556 if (infoPtr
->uView
== LV_VIEW_ICON
)
2558 lvItem
.mask
|= LVIF_STATE
;
2559 lvItem
.stateMask
= LVIS_FOCUSED
;
2560 lvItem
.state
= (lvItem
.mask
& LVIF_TEXT
? LVIS_FOCUSED
: 0);
2562 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprcBox
, 0, 0, 0, 0);
2564 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
&&
2565 SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, 0, 0))
2567 OffsetRect(lprcBox
, Origin
.x
, Position
.y
+ Origin
.y
);
2570 OffsetRect(lprcBox
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
2573 /* LISTVIEW_MapIdToIndex helper */
2574 static INT CALLBACK
MapIdSearchCompare(LPVOID p1
, LPVOID p2
, LPARAM lParam
)
2576 ITEM_ID
*id1
= (ITEM_ID
*)p1
;
2577 ITEM_ID
*id2
= (ITEM_ID
*)p2
;
2579 if (id1
->id
== id2
->id
) return 0;
2581 return (id1
->id
< id2
->id
) ? -1 : 1;
2586 * Returns the item index for id specified.
2589 * [I] infoPtr : valid pointer to the listview structure
2590 * [I] iID : item id to get index for
2593 * Item index, or -1 on failure.
2595 static INT
LISTVIEW_MapIdToIndex(const LISTVIEW_INFO
*infoPtr
, UINT iID
)
2600 TRACE("iID=%d\n", iID
);
2602 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return -1;
2603 if (infoPtr
->nItemCount
== 0) return -1;
2606 index
= DPA_Search(infoPtr
->hdpaItemIds
, &ID
, -1, MapIdSearchCompare
, 0, DPAS_SORTED
);
2610 ITEM_ID
*lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, index
);
2611 return DPA_GetPtrIndex(infoPtr
->hdpaItems
, lpID
->item
);
2619 * Returns the item id for index given.
2622 * [I] infoPtr : valid pointer to the listview structure
2623 * [I] iItem : item index to get id for
2628 static DWORD
LISTVIEW_MapIndexToId(const LISTVIEW_INFO
*infoPtr
, INT iItem
)
2633 TRACE("iItem=%d\n", iItem
);
2635 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return -1;
2636 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
) return -1;
2638 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, iItem
);
2639 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
2641 return lpItem
->id
->id
;
2646 * Returns the current icon position, and advances it along the top.
2647 * The returned position is not offset by Origin.
2650 * [I] infoPtr : valid pointer to the listview structure
2651 * [O] lpPos : will get the current icon position
2656 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2658 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2660 *lpPos
= infoPtr
->currIconPos
;
2662 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2663 if (infoPtr
->currIconPos
.x
+ infoPtr
->nItemWidth
<= nListWidth
) return;
2665 infoPtr
->currIconPos
.x
= 0;
2666 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2672 * Returns the current icon position, and advances it down the left edge.
2673 * The returned position is not offset by Origin.
2676 * [I] infoPtr : valid pointer to the listview structure
2677 * [O] lpPos : will get the current icon position
2682 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2684 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2686 *lpPos
= infoPtr
->currIconPos
;
2688 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2689 if (infoPtr
->currIconPos
.y
+ infoPtr
->nItemHeight
<= nListHeight
) return;
2691 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2692 infoPtr
->currIconPos
.y
= 0;
2698 * Moves an icon to the specified position.
2699 * It takes care of invalidating the item, etc.
2702 * [I] infoPtr : valid pointer to the listview structure
2703 * [I] nItem : the item to move
2704 * [I] lpPos : the new icon position
2705 * [I] isNew : flags the item as being new
2711 static BOOL
LISTVIEW_MoveIconTo(const LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*lppt
, BOOL isNew
)
2717 old
.x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2718 old
.y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2720 if (lppt
->x
== old
.x
&& lppt
->y
== old
.y
) return TRUE
;
2721 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2724 /* Allocating a POINTER for every item is too resource intensive,
2725 * so we'll keep the (x,y) in different arrays */
2726 if (!DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)(LONG_PTR
)lppt
->x
)) return FALSE
;
2727 if (!DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)(LONG_PTR
)lppt
->y
)) return FALSE
;
2729 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2736 * Arranges listview items in icon display mode.
2739 * [I] infoPtr : valid pointer to the listview structure
2740 * [I] nAlignCode : alignment code
2746 static BOOL
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
2748 void (*next_pos
)(LISTVIEW_INFO
*, LPPOINT
);
2752 if (infoPtr
->uView
!= LV_VIEW_ICON
&& infoPtr
->uView
!= LV_VIEW_SMALLICON
) return FALSE
;
2754 TRACE("nAlignCode=%d\n", nAlignCode
);
2756 if (nAlignCode
== LVA_DEFAULT
)
2758 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
) nAlignCode
= LVA_ALIGNLEFT
;
2759 else nAlignCode
= LVA_ALIGNTOP
;
2764 case LVA_ALIGNLEFT
: next_pos
= LISTVIEW_NextIconPosLeft
; break;
2765 case LVA_ALIGNTOP
: next_pos
= LISTVIEW_NextIconPosTop
; break;
2766 case LVA_SNAPTOGRID
: next_pos
= LISTVIEW_NextIconPosTop
; break; /* FIXME */
2767 default: return FALSE
;
2770 infoPtr
->bAutoarrange
= TRUE
;
2771 infoPtr
->currIconPos
.x
= infoPtr
->currIconPos
.y
= 0;
2772 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2774 next_pos(infoPtr
, &pos
);
2775 LISTVIEW_MoveIconTo(infoPtr
, i
, &pos
, FALSE
);
2783 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2784 * For LVS_REPORT always returns empty rectangle.
2787 * [I] infoPtr : valid pointer to the listview structure
2788 * [O] lprcView : bounding rectangle
2794 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2798 SetRectEmpty(lprcView
);
2800 switch (infoPtr
->uView
)
2803 case LV_VIEW_SMALLICON
:
2804 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2806 x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, i
);
2807 y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, i
);
2808 lprcView
->right
= max(lprcView
->right
, x
);
2809 lprcView
->bottom
= max(lprcView
->bottom
, y
);
2811 if (infoPtr
->nItemCount
> 0)
2813 lprcView
->right
+= infoPtr
->nItemWidth
;
2814 lprcView
->bottom
+= infoPtr
->nItemHeight
;
2819 y
= LISTVIEW_GetCountPerColumn(infoPtr
);
2820 x
= infoPtr
->nItemCount
/ y
;
2821 if (infoPtr
->nItemCount
% y
) x
++;
2822 lprcView
->right
= x
* infoPtr
->nItemWidth
;
2823 lprcView
->bottom
= y
* infoPtr
->nItemHeight
;
2830 * Retrieves the bounding rectangle of all the items.
2833 * [I] infoPtr : valid pointer to the listview structure
2834 * [O] lprcView : bounding rectangle
2840 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2844 TRACE("(lprcView=%p)\n", lprcView
);
2846 if (!lprcView
) return FALSE
;
2848 LISTVIEW_GetAreaRect(infoPtr
, lprcView
);
2850 if (infoPtr
->uView
!= LV_VIEW_DETAILS
)
2852 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
2853 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
2856 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView
));
2863 * Retrieves the subitem pointer associated with the subitem index.
2866 * [I] hdpaSubItems : DPA handle for a specific item
2867 * [I] nSubItem : index of subitem
2870 * SUCCESS : subitem pointer
2873 static SUBITEM_INFO
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
, INT nSubItem
)
2875 SUBITEM_INFO
*lpSubItem
;
2878 /* we should binary search here if need be */
2879 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
2881 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
2882 if (lpSubItem
->iSubItem
== nSubItem
)
2892 * Calculates the desired item width.
2895 * [I] infoPtr : valid pointer to the listview structure
2898 * The desired item width.
2900 static INT
LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO
*infoPtr
)
2904 TRACE("uView=%d\n", infoPtr
->uView
);
2906 if (infoPtr
->uView
== LV_VIEW_ICON
)
2907 nItemWidth
= infoPtr
->iconSpacing
.cx
;
2908 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2910 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2915 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
2916 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
2918 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcHeader
);
2919 nItemWidth
= rcHeader
.right
;
2922 else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
2924 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2928 lvItem
.mask
= LVIF_TEXT
;
2929 lvItem
.iSubItem
= 0;
2931 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2934 lvItem
.pszText
= szDispText
;
2935 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2936 if (LISTVIEW_GetItemW(infoPtr
, &lvItem
))
2937 nItemWidth
= max(LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
),
2941 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
2942 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
2944 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
+ WIDTH_PADDING
);
2952 * Calculates the desired item height.
2955 * [I] infoPtr : valid pointer to the listview structure
2958 * The desired item height.
2960 static INT
LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO
*infoPtr
)
2964 TRACE("uView=%d\n", infoPtr
->uView
);
2966 if (infoPtr
->uView
== LV_VIEW_ICON
)
2967 nItemHeight
= infoPtr
->iconSpacing
.cy
;
2970 nItemHeight
= infoPtr
->ntmHeight
;
2971 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
2973 if (infoPtr
->himlState
)
2974 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
2975 if (infoPtr
->himlSmall
)
2976 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
2977 if (infoPtr
->himlState
|| infoPtr
->himlSmall
)
2978 nItemHeight
+= HEIGHT_PADDING
;
2979 if (infoPtr
->nMeasureItemHeight
> 0)
2980 nItemHeight
= infoPtr
->nMeasureItemHeight
;
2983 return max(nItemHeight
, 1);
2988 * Updates the width, and height of an item.
2991 * [I] infoPtr : valid pointer to the listview structure
2996 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO
*infoPtr
)
2998 infoPtr
->nItemWidth
= LISTVIEW_CalculateItemWidth(infoPtr
);
2999 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
3005 * Retrieves and saves important text metrics info for the current
3009 * [I] infoPtr : valid pointer to the listview structure
3012 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
3014 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
3015 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
3016 HFONT hOldFont
= SelectObject(hdc
, hFont
);
3020 if (GetTextMetricsW(hdc
, &tm
))
3022 infoPtr
->ntmHeight
= tm
.tmHeight
;
3023 infoPtr
->ntmMaxCharWidth
= tm
.tmMaxCharWidth
;
3026 if (GetTextExtentPoint32A(hdc
, "...", 3, &sz
))
3027 infoPtr
->nEllipsisWidth
= sz
.cx
;
3029 SelectObject(hdc
, hOldFont
);
3030 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
3032 TRACE("tmHeight=%d\n", infoPtr
->ntmHeight
);
3037 * A compare function for ranges
3040 * [I] range1 : pointer to range 1;
3041 * [I] range2 : pointer to range 2;
3045 * > 0 : if range 1 > range 2
3046 * < 0 : if range 2 > range 1
3047 * = 0 : if range intersects range 2
3049 static INT CALLBACK
ranges_cmp(LPVOID range1
, LPVOID range2
, LPARAM flags
)
3053 if (((RANGE
*)range1
)->upper
<= ((RANGE
*)range2
)->lower
)
3055 else if (((RANGE
*)range2
)->upper
<= ((RANGE
*)range1
)->lower
)
3060 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1
), debugrange(range2
), cmp
);
3065 #define ranges_check(ranges, desc) if (TRACE_ON(listview)) ranges_assert(ranges, desc, __FILE__, __LINE__)
3067 static void ranges_assert(RANGES ranges
, LPCSTR desc
, const char *file
, int line
)
3072 TRACE("*** Checking %s:%d:%s ***\n", file
, line
, desc
);
3074 assert (DPA_GetPtrCount(ranges
->hdpa
) >= 0);
3075 ranges_dump(ranges
);
3076 if (DPA_GetPtrCount(ranges
->hdpa
) > 0)
3078 prev
= DPA_GetPtr(ranges
->hdpa
, 0);
3079 assert (prev
->lower
>= 0 && prev
->lower
< prev
->upper
);
3080 for (i
= 1; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3082 curr
= DPA_GetPtr(ranges
->hdpa
, i
);
3083 assert (prev
->upper
<= curr
->lower
);
3084 assert (curr
->lower
< curr
->upper
);
3088 TRACE("--- Done checking---\n");
3091 static RANGES
ranges_create(int count
)
3093 RANGES ranges
= Alloc(sizeof(struct tagRANGES
));
3094 if (!ranges
) return NULL
;
3095 ranges
->hdpa
= DPA_Create(count
);
3096 if (ranges
->hdpa
) return ranges
;
3101 static void ranges_clear(RANGES ranges
)
3105 for(i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3106 Free(DPA_GetPtr(ranges
->hdpa
, i
));
3107 DPA_DeleteAllPtrs(ranges
->hdpa
);
3111 static void ranges_destroy(RANGES ranges
)
3113 if (!ranges
) return;
3114 ranges_clear(ranges
);
3115 DPA_Destroy(ranges
->hdpa
);
3119 static RANGES
ranges_clone(RANGES ranges
)
3124 if (!(clone
= ranges_create(DPA_GetPtrCount(ranges
->hdpa
)))) goto fail
;
3126 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3128 RANGE
*newrng
= Alloc(sizeof(RANGE
));
3129 if (!newrng
) goto fail
;
3130 *newrng
= *((RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
));
3131 DPA_SetPtr(clone
->hdpa
, i
, newrng
);
3136 TRACE ("clone failed\n");
3137 ranges_destroy(clone
);
3141 static RANGES
ranges_diff(RANGES ranges
, RANGES sub
)
3145 for (i
= 0; i
< DPA_GetPtrCount(sub
->hdpa
); i
++)
3146 ranges_del(ranges
, *((RANGE
*)DPA_GetPtr(sub
->hdpa
, i
)));
3151 static void ranges_dump(RANGES ranges
)
3155 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3156 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges
->hdpa
, i
)));
3159 static inline BOOL
ranges_contain(RANGES ranges
, INT nItem
)
3161 RANGE srchrng
= { nItem
, nItem
+ 1 };
3163 TRACE("(nItem=%d)\n", nItem
);
3164 ranges_check(ranges
, "before contain");
3165 return DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
) != -1;
3168 static INT
ranges_itemcount(RANGES ranges
)
3172 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3174 RANGE
*sel
= DPA_GetPtr(ranges
->hdpa
, i
);
3175 count
+= sel
->upper
- sel
->lower
;
3181 static BOOL
ranges_shift(RANGES ranges
, INT nItem
, INT delta
, INT nUpper
)
3183 RANGE srchrng
= { nItem
, nItem
+ 1 }, *chkrng
;
3186 index
= DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
3187 if (index
== -1) return TRUE
;
3189 for (; index
< DPA_GetPtrCount(ranges
->hdpa
); index
++)
3191 chkrng
= DPA_GetPtr(ranges
->hdpa
, index
);
3192 if (chkrng
->lower
>= nItem
)
3193 chkrng
->lower
= max(min(chkrng
->lower
+ delta
, nUpper
- 1), 0);
3194 if (chkrng
->upper
> nItem
)
3195 chkrng
->upper
= max(min(chkrng
->upper
+ delta
, nUpper
), 0);
3200 static BOOL
ranges_add(RANGES ranges
, RANGE range
)
3205 TRACE("(%s)\n", debugrange(&range
));
3206 ranges_check(ranges
, "before add");
3208 /* try find overlapping regions first */
3209 srchrgn
.lower
= range
.lower
- 1;
3210 srchrgn
.upper
= range
.upper
+ 1;
3211 index
= DPA_Search(ranges
->hdpa
, &srchrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
);
3217 TRACE("Adding new range\n");
3219 /* create the brand new range to insert */
3220 newrgn
= Alloc(sizeof(RANGE
));
3221 if(!newrgn
) goto fail
;
3224 /* figure out where to insert it */
3225 index
= DPA_Search(ranges
->hdpa
, newrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
3226 TRACE("index=%d\n", index
);
3227 if (index
== -1) index
= 0;
3229 /* and get it over with */
3230 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
3238 RANGE
*chkrgn
, *mrgrgn
;
3239 INT fromindex
, mergeindex
;
3241 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
3242 TRACE("Merge with %s @%d\n", debugrange(chkrgn
), index
);
3244 chkrgn
->lower
= min(range
.lower
, chkrgn
->lower
);
3245 chkrgn
->upper
= max(range
.upper
, chkrgn
->upper
);
3247 TRACE("New range %s @%d\n", debugrange(chkrgn
), index
);
3249 /* merge now common ranges */
3251 srchrgn
.lower
= chkrgn
->lower
- 1;
3252 srchrgn
.upper
= chkrgn
->upper
+ 1;
3256 mergeindex
= DPA_Search(ranges
->hdpa
, &srchrgn
, fromindex
, ranges_cmp
, 0, 0);
3257 if (mergeindex
== -1) break;
3258 if (mergeindex
== index
)
3260 fromindex
= index
+ 1;
3264 TRACE("Merge with index %i\n", mergeindex
);
3266 mrgrgn
= DPA_GetPtr(ranges
->hdpa
, mergeindex
);
3267 chkrgn
->lower
= min(chkrgn
->lower
, mrgrgn
->lower
);
3268 chkrgn
->upper
= max(chkrgn
->upper
, mrgrgn
->upper
);
3270 DPA_DeletePtr(ranges
->hdpa
, mergeindex
);
3271 if (mergeindex
< index
) index
--;
3275 ranges_check(ranges
, "after add");
3279 ranges_check(ranges
, "failed add");
3283 static BOOL
ranges_del(RANGES ranges
, RANGE range
)
3288 TRACE("(%s)\n", debugrange(&range
));
3289 ranges_check(ranges
, "before del");
3291 /* we don't use DPAS_SORTED here, since we need *
3292 * to find the first overlapping range */
3293 index
= DPA_Search(ranges
->hdpa
, &range
, 0, ranges_cmp
, 0, 0);
3296 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
3298 TRACE("Matches range %s @%d\n", debugrange(chkrgn
), index
);
3300 /* case 1: Same range */
3301 if ( (chkrgn
->upper
== range
.upper
) &&
3302 (chkrgn
->lower
== range
.lower
) )
3304 DPA_DeletePtr(ranges
->hdpa
, index
);
3308 /* case 2: engulf */
3309 else if ( (chkrgn
->upper
<= range
.upper
) &&
3310 (chkrgn
->lower
>= range
.lower
) )
3312 DPA_DeletePtr(ranges
->hdpa
, index
);
3315 /* case 3: overlap upper */
3316 else if ( (chkrgn
->upper
<= range
.upper
) &&
3317 (chkrgn
->lower
< range
.lower
) )
3319 chkrgn
->upper
= range
.lower
;
3321 /* case 4: overlap lower */
3322 else if ( (chkrgn
->upper
> range
.upper
) &&
3323 (chkrgn
->lower
>= range
.lower
) )
3325 chkrgn
->lower
= range
.upper
;
3328 /* case 5: fully internal */
3333 if (!(newrgn
= Alloc(sizeof(RANGE
)))) goto fail
;
3334 newrgn
->lower
= chkrgn
->lower
;
3335 newrgn
->upper
= range
.lower
;
3336 chkrgn
->lower
= range
.upper
;
3337 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
3345 index
= DPA_Search(ranges
->hdpa
, &range
, index
, ranges_cmp
, 0, 0);
3348 ranges_check(ranges
, "after del");
3352 ranges_check(ranges
, "failed del");
3358 * Removes all selection ranges
3361 * [I] infoPtr : valid pointer to the listview structure
3362 * [I] toSkip : item range to skip removing the selection
3368 static BOOL
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO
*infoPtr
, RANGES toSkip
)
3377 lvItem
.stateMask
= LVIS_SELECTED
;
3379 /* need to clone the DPA because callbacks can change it */
3380 if (!(clone
= ranges_clone(infoPtr
->selectionRanges
))) return FALSE
;
3381 iterator_rangesitems(&i
, ranges_diff(clone
, toSkip
));
3382 while(iterator_next(&i
))
3383 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &lvItem
);
3384 /* note that the iterator destructor will free the cloned range */
3385 iterator_destroy(&i
);
3390 static inline BOOL
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3394 if (!(toSkip
= ranges_create(1))) return FALSE
;
3395 if (nItem
!= -1) ranges_additem(toSkip
, nItem
);
3396 LISTVIEW_DeselectAllSkipItems(infoPtr
, toSkip
);
3397 ranges_destroy(toSkip
);
3401 static inline BOOL
LISTVIEW_DeselectAll(LISTVIEW_INFO
*infoPtr
)
3403 return LISTVIEW_DeselectAllSkipItem(infoPtr
, -1);
3408 * Retrieves the number of items that are marked as selected.
3411 * [I] infoPtr : valid pointer to the listview structure
3414 * Number of items selected.
3416 static INT
LISTVIEW_GetSelectedCount(const LISTVIEW_INFO
*infoPtr
)
3418 INT nSelectedCount
= 0;
3420 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3423 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
3425 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
3430 nSelectedCount
= ranges_itemcount(infoPtr
->selectionRanges
);
3432 TRACE("nSelectedCount=%d\n", nSelectedCount
);
3433 return nSelectedCount
;
3438 * Manages the item focus.
3441 * [I] infoPtr : valid pointer to the listview structure
3442 * [I] nItem : item index
3445 * TRUE : focused item changed
3446 * FALSE : focused item has NOT changed
3448 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3450 INT oldFocus
= infoPtr
->nFocusedItem
;
3453 if (nItem
== infoPtr
->nFocusedItem
) return FALSE
;
3455 lvItem
.state
= nItem
== -1 ? 0 : LVIS_FOCUSED
;
3456 lvItem
.stateMask
= LVIS_FOCUSED
;
3457 LISTVIEW_SetItemState(infoPtr
, nItem
== -1 ? infoPtr
->nFocusedItem
: nItem
, &lvItem
);
3459 return oldFocus
!= infoPtr
->nFocusedItem
;
3462 /* Helper function for LISTVIEW_ShiftIndices *only* */
3463 static INT
shift_item(const LISTVIEW_INFO
*infoPtr
, INT nShiftItem
, INT nItem
, INT direction
)
3465 if (nShiftItem
< nItem
) return nShiftItem
;
3467 if (nShiftItem
> nItem
) return nShiftItem
+ direction
;
3469 if (direction
> 0) return nShiftItem
+ direction
;
3471 return min(nShiftItem
, infoPtr
->nItemCount
- 1);
3476 * Updates the various indices after an item has been inserted or deleted.
3479 * [I] infoPtr : valid pointer to the listview structure
3480 * [I] nItem : item index
3481 * [I] direction : Direction of shift, +1 or -1.
3486 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
3491 /* temporarily disable change notification while shifting items */
3492 bOldChange
= infoPtr
->bDoChangeNotify
;
3493 infoPtr
->bDoChangeNotify
= FALSE
;
3495 TRACE("Shifting %iu, %i steps\n", nItem
, direction
);
3497 ranges_shift(infoPtr
->selectionRanges
, nItem
, direction
, infoPtr
->nItemCount
);
3499 assert(abs(direction
) == 1);
3501 infoPtr
->nSelectionMark
= shift_item(infoPtr
, infoPtr
->nSelectionMark
, nItem
, direction
);
3503 nNewFocus
= shift_item(infoPtr
, infoPtr
->nFocusedItem
, nItem
, direction
);
3504 if (nNewFocus
!= infoPtr
->nFocusedItem
)
3505 LISTVIEW_SetItemFocus(infoPtr
, nNewFocus
);
3507 /* But we are not supposed to modify nHotItem! */
3509 infoPtr
->bDoChangeNotify
= bOldChange
;
3515 * Adds a block of selections.
3518 * [I] infoPtr : valid pointer to the listview structure
3519 * [I] nItem : item index
3522 * Whether the window is still valid.
3524 static BOOL
LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3526 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
3527 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
3528 HWND hwndSelf
= infoPtr
->hwndSelf
;
3529 NMLVODSTATECHANGE nmlv
;
3534 /* Temporarily disable change notification
3535 * If the control is LVS_OWNERDATA, we need to send
3536 * only one LVN_ODSTATECHANGED notification.
3537 * See MSDN documentation for LVN_ITEMCHANGED.
3539 bOldChange
= infoPtr
->bDoChangeNotify
;
3540 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3542 if (nFirst
== -1) nFirst
= nItem
;
3544 item
.state
= LVIS_SELECTED
;
3545 item
.stateMask
= LVIS_SELECTED
;
3547 for (i
= nFirst
; i
<= nLast
; i
++)
3548 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
3550 ZeroMemory(&nmlv
, sizeof(nmlv
));
3551 nmlv
.iFrom
= nFirst
;
3554 nmlv
.uOldState
= item
.state
;
3556 notify_hdr(infoPtr
, LVN_ODSTATECHANGED
, (LPNMHDR
)&nmlv
);
3557 if (!IsWindow(hwndSelf
))
3559 infoPtr
->bDoChangeNotify
= bOldChange
;
3566 * Sets a single group selection.
3569 * [I] infoPtr : valid pointer to the listview structure
3570 * [I] nItem : item index
3575 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3582 if (!(selection
= ranges_create(100))) return;
3584 item
.state
= LVIS_SELECTED
;
3585 item
.stateMask
= LVIS_SELECTED
;
3587 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
3589 if (infoPtr
->nSelectionMark
== -1)
3591 infoPtr
->nSelectionMark
= nItem
;
3592 ranges_additem(selection
, nItem
);
3598 sel
.lower
= min(infoPtr
->nSelectionMark
, nItem
);
3599 sel
.upper
= max(infoPtr
->nSelectionMark
, nItem
) + 1;
3600 ranges_add(selection
, sel
);
3605 RECT rcItem
, rcSel
, rcSelMark
;
3608 rcItem
.left
= LVIR_BOUNDS
;
3609 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return;
3610 rcSelMark
.left
= LVIR_BOUNDS
;
3611 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) return;
3612 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
3613 iterator_frameditems(&i
, infoPtr
, &rcSel
);
3614 while(iterator_next(&i
))
3616 LISTVIEW_GetItemPosition(infoPtr
, i
.nItem
, &ptItem
);
3617 if (PtInRect(&rcSel
, ptItem
)) ranges_additem(selection
, i
.nItem
);
3619 iterator_destroy(&i
);
3622 /* disable per item notifications on LVS_OWNERDATA style
3623 FIXME: single LVN_ODSTATECHANGED should be used */
3624 bOldChange
= infoPtr
->bDoChangeNotify
;
3625 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3627 LISTVIEW_DeselectAllSkipItems(infoPtr
, selection
);
3630 iterator_rangesitems(&i
, selection
);
3631 while(iterator_next(&i
))
3632 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &item
);
3633 /* this will also destroy the selection */
3634 iterator_destroy(&i
);
3636 infoPtr
->bDoChangeNotify
= bOldChange
;
3638 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3643 * Sets a single selection.
3646 * [I] infoPtr : valid pointer to the listview structure
3647 * [I] nItem : item index
3652 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3656 TRACE("nItem=%d\n", nItem
);
3658 LISTVIEW_DeselectAllSkipItem(infoPtr
, nItem
);
3660 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3661 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3662 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3664 infoPtr
->nSelectionMark
= nItem
;
3669 * Set selection(s) with keyboard.
3672 * [I] infoPtr : valid pointer to the listview structure
3673 * [I] nItem : item index
3674 * [I] space : VK_SPACE code sent
3677 * SUCCESS : TRUE (needs to be repainted)
3678 * FAILURE : FALSE (nothing has changed)
3680 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL space
)
3682 /* FIXME: pass in the state */
3683 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
3684 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
3685 BOOL bResult
= FALSE
;
3687 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem
, wShift
, wCtrl
);
3688 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3692 if (infoPtr
->dwStyle
& LVS_SINGLESEL
|| (wShift
== 0 && wCtrl
== 0))
3693 LISTVIEW_SetSelection(infoPtr
, nItem
);
3697 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3701 lvItem
.state
= ~LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3702 lvItem
.stateMask
= LVIS_SELECTED
;
3705 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3706 if (lvItem
.state
& LVIS_SELECTED
)
3707 infoPtr
->nSelectionMark
= nItem
;
3709 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3712 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3715 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3719 static BOOL
LISTVIEW_GetItemAtPt(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, POINT pt
)
3721 LVHITTESTINFO lvHitTestInfo
;
3723 ZeroMemory(&lvHitTestInfo
, sizeof(lvHitTestInfo
));
3724 lvHitTestInfo
.pt
.x
= pt
.x
;
3725 lvHitTestInfo
.pt
.y
= pt
.y
;
3727 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
3729 lpLVItem
->mask
= LVIF_PARAM
;
3730 lpLVItem
->iItem
= lvHitTestInfo
.iItem
;
3731 lpLVItem
->iSubItem
= 0;
3733 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
3736 static inline BOOL
LISTVIEW_IsHotTracking(const LISTVIEW_INFO
*infoPtr
)
3738 return ((infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) ||
3739 (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
) ||
3740 (infoPtr
->dwLvExStyle
& LVS_EX_TWOCLICKACTIVATE
));
3745 * Called when the mouse is being actively tracked and has hovered for a specified
3749 * [I] infoPtr : valid pointer to the listview structure
3750 * [I] fwKeys : key indicator
3751 * [I] x,y : mouse position
3754 * 0 if the message was processed, non-zero if there was an error
3757 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3758 * over the item for a certain period of time.
3761 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, INT x
, INT y
)
3765 if (notify_hdr(infoPtr
, NM_HOVER
, &hdr
)) return 0;
3767 if (LISTVIEW_IsHotTracking(infoPtr
))
3775 if (LISTVIEW_GetItemAtPt(infoPtr
, &item
, pt
))
3776 LISTVIEW_SetSelection(infoPtr
, item
.iItem
);
3778 SetFocus(infoPtr
->hwndSelf
);
3784 #define SCROLL_LEFT 0x1
3785 #define SCROLL_RIGHT 0x2
3786 #define SCROLL_UP 0x4
3787 #define SCROLL_DOWN 0x8
3791 * Utility routine to draw and highlight items within a marquee selection rectangle.
3794 * [I] infoPtr : valid pointer to the listview structure
3795 * [I] coords_orig : original co-ordinates of the cursor
3796 * [I] coords_offs : offsetted coordinates of the cursor
3797 * [I] offset : offset amount
3798 * [I] scroll : Bitmask of which directions we should scroll, if at all
3803 static void LISTVIEW_MarqueeHighlight(LISTVIEW_INFO
*infoPtr
, const POINT
*coords_orig
,
3804 const POINT
*coords_offs
, const POINT
*offset
,
3807 BOOL controlDown
= FALSE
;
3809 ITERATOR old_elems
, new_elems
;
3812 if (coords_offs
->x
> infoPtr
->marqueeOrigin
.x
)
3814 rect
.left
= infoPtr
->marqueeOrigin
.x
;
3815 rect
.right
= coords_offs
->x
;
3819 rect
.left
= coords_offs
->x
;
3820 rect
.right
= infoPtr
->marqueeOrigin
.x
;
3823 if (coords_offs
->y
> infoPtr
->marqueeOrigin
.y
)
3825 rect
.top
= infoPtr
->marqueeOrigin
.y
;
3826 rect
.bottom
= coords_offs
->y
;
3830 rect
.top
= coords_offs
->y
;
3831 rect
.bottom
= infoPtr
->marqueeOrigin
.y
;
3834 /* Cancel out the old marquee rectangle and draw the new one */
3835 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeDrawRect
);
3837 /* Scroll by the appropriate distance if applicable - speed up scrolling as
3838 the cursor is further away */
3840 if ((scroll
& SCROLL_LEFT
) && (coords_orig
->x
<= 0))
3841 LISTVIEW_Scroll(infoPtr
, coords_orig
->x
, 0);
3843 if ((scroll
& SCROLL_RIGHT
) && (coords_orig
->x
>= infoPtr
->rcList
.right
))
3844 LISTVIEW_Scroll(infoPtr
, (coords_orig
->x
- infoPtr
->rcList
.right
), 0);
3846 if ((scroll
& SCROLL_UP
) && (coords_orig
->y
<= 0))
3847 LISTVIEW_Scroll(infoPtr
, 0, coords_orig
->y
);
3849 if ((scroll
& SCROLL_DOWN
) && (coords_orig
->y
>= infoPtr
->rcList
.bottom
))
3850 LISTVIEW_Scroll(infoPtr
, 0, (coords_orig
->y
- infoPtr
->rcList
.bottom
));
3852 iterator_frameditems_absolute(&old_elems
, infoPtr
, &infoPtr
->marqueeRect
);
3854 CopyRect(&infoPtr
->marqueeRect
, &rect
);
3856 CopyRect(&infoPtr
->marqueeDrawRect
, &rect
);
3857 OffsetRect(&infoPtr
->marqueeDrawRect
, offset
->x
, offset
->y
);
3859 iterator_frameditems_absolute(&new_elems
, infoPtr
, &infoPtr
->marqueeRect
);
3860 iterator_remove_common_items(&old_elems
, &new_elems
);
3862 /* Iterate over no longer selected items */
3863 while (iterator_next(&old_elems
))
3865 if (old_elems
.nItem
> -1)
3867 if (LISTVIEW_GetItemState(infoPtr
, old_elems
.nItem
, LVIS_SELECTED
) == LVIS_SELECTED
)
3870 item
.state
= LVIS_SELECTED
;
3872 item
.stateMask
= LVIS_SELECTED
;
3874 LISTVIEW_SetItemState(infoPtr
, old_elems
.nItem
, &item
);
3877 iterator_destroy(&old_elems
);
3880 /* Iterate over newly selected items */
3881 if (GetKeyState(VK_CONTROL
) & 0x8000)
3884 while (iterator_next(&new_elems
))
3886 if (new_elems
.nItem
> -1)
3888 /* If CTRL is pressed, invert. If not, always select the item. */
3889 if ((controlDown
) && (LISTVIEW_GetItemState(infoPtr
, new_elems
.nItem
, LVIS_SELECTED
)))
3892 item
.state
= LVIS_SELECTED
;
3894 item
.stateMask
= LVIS_SELECTED
;
3896 LISTVIEW_SetItemState(infoPtr
, new_elems
.nItem
, &item
);
3899 iterator_destroy(&new_elems
);
3901 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeDrawRect
);
3906 * Called when we are in a marquee selection that involves scrolling the listview (ie,
3907 * the cursor is outside the bounds of the client area). This is a TIMERPROC.
3910 * [I] hwnd : Handle to the listview
3911 * [I] uMsg : WM_TIMER (ignored)
3912 * [I] idEvent : The timer ID interpreted as a pointer to a LISTVIEW_INFO struct
3913 * [I] dwTimer : The elapsed time (ignored)
3918 static VOID CALLBACK
LISTVIEW_ScrollTimer(HWND hWnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
3920 LISTVIEW_INFO
*infoPtr
;
3921 SCROLLINFO scrollInfo
;
3927 infoPtr
= (LISTVIEW_INFO
*) idEvent
;
3932 /* Get the current cursor position and convert to client coordinates */
3933 GetCursorPos(&coords_orig
);
3934 ScreenToClient(hWnd
, &coords_orig
);
3936 /* Ensure coordinates are within client bounds */
3937 coords_offs
.x
= max(min(coords_orig
.x
, infoPtr
->rcList
.right
), 0);
3938 coords_offs
.y
= max(min(coords_orig
.y
, infoPtr
->rcList
.bottom
), 0);
3941 LISTVIEW_GetOrigin(infoPtr
, &offset
);
3943 /* Offset coordinates by the appropriate amount */
3944 coords_offs
.x
-= offset
.x
;
3945 coords_offs
.y
-= offset
.y
;
3947 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3948 scrollInfo
.fMask
= SIF_ALL
;
3950 /* Work out in which directions we can scroll */
3951 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3953 if (scrollInfo
.nPos
!= scrollInfo
.nMin
)
3954 scroll
|= SCROLL_UP
;
3956 if (((scrollInfo
.nPage
+ scrollInfo
.nPos
) - 1) != scrollInfo
.nMax
)
3957 scroll
|= SCROLL_DOWN
;
3960 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
3962 if (scrollInfo
.nPos
!= scrollInfo
.nMin
)
3963 scroll
|= SCROLL_LEFT
;
3965 if (((scrollInfo
.nPage
+ scrollInfo
.nPos
) - 1) != scrollInfo
.nMax
)
3966 scroll
|= SCROLL_RIGHT
;
3969 if (((coords_orig
.x
<= 0) && (scroll
& SCROLL_LEFT
)) ||
3970 ((coords_orig
.y
<= 0) && (scroll
& SCROLL_UP
)) ||
3971 ((coords_orig
.x
>= infoPtr
->rcList
.right
) && (scroll
& SCROLL_RIGHT
)) ||
3972 ((coords_orig
.y
>= infoPtr
->rcList
.bottom
) && (scroll
& SCROLL_DOWN
)))
3974 LISTVIEW_MarqueeHighlight(infoPtr
, &coords_orig
, &coords_offs
, &offset
, scroll
);
3980 * Called whenever WM_MOUSEMOVE is received.
3983 * [I] infoPtr : valid pointer to the listview structure
3984 * [I] fwKeys : key indicator
3985 * [I] x,y : mouse position
3988 * 0 if the message is processed, non-zero if there was an error
3990 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3992 if (!(fwKeys
& MK_LBUTTON
))
3993 infoPtr
->bLButtonDown
= FALSE
;
3995 if (infoPtr
->bLButtonDown
)
3999 LVHITTESTINFO lvHitTestInfo
;
4000 WORD wDragWidth
= GetSystemMetrics(SM_CXDRAG
);
4001 WORD wDragHeight
= GetSystemMetrics(SM_CYDRAG
);
4003 if (infoPtr
->bMarqueeSelect
)
4013 LISTVIEW_GetOrigin(infoPtr
, &offset
);
4015 /* Ensure coordinates are within client bounds */
4016 coords_offs
.x
= max(min(x
, infoPtr
->rcList
.right
), 0);
4017 coords_offs
.y
= max(min(y
, infoPtr
->rcList
.bottom
), 0);
4019 /* Offset coordinates by the appropriate amount */
4020 coords_offs
.x
-= offset
.x
;
4021 coords_offs
.y
-= offset
.y
;
4023 /* Enable the timer if we're going outside our bounds, in case the user doesn't
4024 move the mouse again */
4026 if ((x
<= 0) || (y
<= 0) || (x
>= infoPtr
->rcList
.right
) ||
4027 (y
>= infoPtr
->rcList
.bottom
))
4029 if (!infoPtr
->bScrolling
)
4031 infoPtr
->bScrolling
= TRUE
;
4032 SetTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
, 1, LISTVIEW_ScrollTimer
);
4037 infoPtr
->bScrolling
= FALSE
;
4038 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
);
4041 LISTVIEW_MarqueeHighlight(infoPtr
, &coords_orig
, &coords_offs
, &offset
, 0);
4045 rect
.left
= infoPtr
->ptClickPos
.x
- wDragWidth
;
4046 rect
.right
= infoPtr
->ptClickPos
.x
+ wDragWidth
;
4047 rect
.top
= infoPtr
->ptClickPos
.y
- wDragHeight
;
4048 rect
.bottom
= infoPtr
->ptClickPos
.y
+ wDragHeight
;
4053 lvHitTestInfo
.pt
= tmp
;
4054 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
4056 /* reset item marker */
4057 if (infoPtr
->nLButtonDownItem
!= lvHitTestInfo
.iItem
)
4058 infoPtr
->nLButtonDownItem
= -1;
4060 if (!PtInRect(&rect
, tmp
))
4062 /* this path covers the following:
4063 1. WM_LBUTTONDOWN over selected item (sets focus on it)
4064 2. change focus with keys
4065 3. move mouse over item from step 1 selects it and moves focus on it */
4066 if (infoPtr
->nLButtonDownItem
!= -1 &&
4067 !LISTVIEW_GetItemState(infoPtr
, infoPtr
->nLButtonDownItem
, LVIS_SELECTED
))
4071 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
4072 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
4074 LISTVIEW_SetItemState(infoPtr
, infoPtr
->nLButtonDownItem
, &lvItem
);
4075 infoPtr
->nLButtonDownItem
= -1;
4078 if (!infoPtr
->bDragging
)
4080 lvHitTestInfo
.pt
= infoPtr
->ptClickPos
;
4081 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
4083 /* If the click is outside the range of an item, begin a
4084 highlight. If not, begin an item drag. */
4085 if (lvHitTestInfo
.iItem
== -1)
4089 /* If we're allowing multiple selections, send notification.
4090 If return value is non-zero, cancel. */
4091 if (!(infoPtr
->dwStyle
& LVS_SINGLESEL
) && (notify_hdr(infoPtr
, LVN_MARQUEEBEGIN
, &hdr
) == 0))
4093 /* Store the absolute coordinates of the click */
4095 LISTVIEW_GetOrigin(infoPtr
, &offset
);
4097 infoPtr
->marqueeOrigin
.x
= infoPtr
->ptClickPos
.x
- offset
.x
;
4098 infoPtr
->marqueeOrigin
.y
= infoPtr
->ptClickPos
.y
- offset
.y
;
4100 /* Begin selection and capture mouse */
4101 infoPtr
->bMarqueeSelect
= TRUE
;
4102 SetCapture(infoPtr
->hwndSelf
);
4109 ZeroMemory(&nmlv
, sizeof(nmlv
));
4110 nmlv
.iItem
= lvHitTestInfo
.iItem
;
4111 nmlv
.ptAction
= infoPtr
->ptClickPos
;
4113 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
4114 infoPtr
->bDragging
= TRUE
;
4122 /* see if we are supposed to be tracking mouse hovering */
4123 if (LISTVIEW_IsHotTracking(infoPtr
)) {
4124 TRACKMOUSEEVENT trackinfo
;
4126 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
4127 trackinfo
.dwFlags
= TME_QUERY
;
4129 /* see if we are already tracking this hwnd */
4130 _TrackMouseEvent(&trackinfo
);
4132 if(!(trackinfo
.dwFlags
& TME_HOVER
) || trackinfo
.hwndTrack
!= infoPtr
->hwndSelf
) {
4133 trackinfo
.dwFlags
= TME_HOVER
;
4134 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
4135 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
4137 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
4138 _TrackMouseEvent(&trackinfo
);
4147 * Tests whether the item is assignable to a list with style lStyle
4149 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
4151 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
4152 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
4153 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
4161 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
4164 * [I] infoPtr : valid pointer to the listview structure
4165 * [I] lpLVItem : valid pointer to new item attributes
4166 * [I] isNew : the item being set is being inserted
4167 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4168 * [O] bChanged : will be set to TRUE if the item really changed
4174 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
4180 /* stateMask is ignored for LVM_INSERTITEM */
4181 UINT stateMask
= isNew
? ~0 : lpLVItem
->stateMask
;
4185 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
4187 if (lpLVItem
->mask
== 0) return TRUE
;
4189 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4191 /* a virtual listview only stores selection and focus */
4192 if (lpLVItem
->mask
& ~LVIF_STATE
)
4198 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
4199 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
4203 /* we need to get the lParam and state of the item */
4204 item
.iItem
= lpLVItem
->iItem
;
4205 item
.iSubItem
= lpLVItem
->iSubItem
;
4206 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
4207 item
.stateMask
= (infoPtr
->dwStyle
& LVS_OWNERDATA
) ? LVIS_FOCUSED
| LVIS_SELECTED
: ~0;
4211 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
4213 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
4214 /* determine what fields will change */
4215 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & stateMask
& ~infoPtr
->uCallbackMask
))
4216 uChanged
|= LVIF_STATE
;
4218 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
4219 uChanged
|= LVIF_IMAGE
;
4221 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
4222 uChanged
|= LVIF_PARAM
;
4224 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
4225 uChanged
|= LVIF_INDENT
;
4227 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
4228 uChanged
|= LVIF_TEXT
;
4230 TRACE("uChanged=0x%x\n", uChanged
);
4231 if (!uChanged
) return TRUE
;
4234 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4235 nmlv
.iItem
= lpLVItem
->iItem
;
4236 nmlv
.uNewState
= (item
.state
& ~stateMask
) | (lpLVItem
->state
& stateMask
);
4237 nmlv
.uOldState
= item
.state
;
4238 nmlv
.uChanged
= uChanged
;
4239 nmlv
.lParam
= item
.lParam
;
4241 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
4242 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
4244 if(lpItem
&& !isNew
&& infoPtr
->bDoChangeNotify
)
4246 HWND hwndSelf
= infoPtr
->hwndSelf
;
4248 if (notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
4250 if (!IsWindow(hwndSelf
))
4254 /* copy information */
4255 if (lpLVItem
->mask
& LVIF_TEXT
)
4256 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
4258 if (lpLVItem
->mask
& LVIF_IMAGE
)
4259 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
4261 if (lpLVItem
->mask
& LVIF_PARAM
)
4262 lpItem
->lParam
= lpLVItem
->lParam
;
4264 if (lpLVItem
->mask
& LVIF_INDENT
)
4265 lpItem
->iIndent
= lpLVItem
->iIndent
;
4267 if (uChanged
& LVIF_STATE
)
4269 if (lpItem
&& (stateMask
& ~infoPtr
->uCallbackMask
))
4271 lpItem
->state
&= ~stateMask
;
4272 lpItem
->state
|= (lpLVItem
->state
& stateMask
);
4274 if (lpLVItem
->state
& stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
4276 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
4277 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
4279 else if (stateMask
& LVIS_SELECTED
)
4281 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
4283 /* if we are asked to change focus, and we manage it, do it */
4284 if (stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
4286 if (lpLVItem
->state
& LVIS_FOCUSED
)
4288 /* update selection mark */
4289 if (infoPtr
->nFocusedItem
== -1 && infoPtr
->nSelectionMark
== -1)
4290 infoPtr
->nSelectionMark
= lpLVItem
->iItem
;
4292 if (infoPtr
->nFocusedItem
!= -1)
4294 /* remove current focus */
4295 item
.mask
= LVIF_STATE
;
4297 item
.stateMask
= LVIS_FOCUSED
;
4299 /* recurse with redrawing an item */
4300 LISTVIEW_SetItemState(infoPtr
, infoPtr
->nFocusedItem
, &item
);
4303 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
4304 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, infoPtr
->uView
== LV_VIEW_LIST
);
4306 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
4308 infoPtr
->nFocusedItem
= -1;
4313 /* if we're inserting the item, we're done */
4314 if (isNew
) return TRUE
;
4316 /* send LVN_ITEMCHANGED notification */
4317 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
4318 if (infoPtr
->bDoChangeNotify
) notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
4325 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
4328 * [I] infoPtr : valid pointer to the listview structure
4329 * [I] lpLVItem : valid pointer to new subitem attributes
4330 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4331 * [O] bChanged : will be set to TRUE if the item really changed
4337 static BOOL
set_sub_item(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
4340 SUBITEM_INFO
*lpSubItem
;
4342 /* we do not support subitems for virtual listviews */
4343 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
4345 /* set subitem only if column is present */
4346 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4348 /* First do some sanity checks */
4349 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
4350 particularly useful. We currently do not actually do anything with
4351 the flag on subitems.
4353 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
| LVIF_DI_SETITEM
)) return FALSE
;
4354 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
))) return TRUE
;
4356 /* get the subitem structure, and create it if not there */
4357 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
4358 assert (hdpaSubItems
);
4360 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
4363 SUBITEM_INFO
*tmpSubItem
;
4366 lpSubItem
= Alloc(sizeof(SUBITEM_INFO
));
4367 if (!lpSubItem
) return FALSE
;
4368 /* we could binary search here, if need be...*/
4369 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4371 tmpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
4372 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
4374 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
4379 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
4380 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
4384 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
))
4386 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
4390 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
4392 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
4401 * Sets item attributes.
4404 * [I] infoPtr : valid pointer to the listview structure
4405 * [I] lpLVItem : new item attributes
4406 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4412 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, LVITEMW
*lpLVItem
, BOOL isW
)
4414 HWND hwndSelf
= infoPtr
->hwndSelf
;
4415 LPWSTR pszText
= NULL
;
4416 BOOL bResult
, bChanged
= FALSE
;
4419 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
4421 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
4424 /* Store old item area */
4425 LISTVIEW_GetItemBox(infoPtr
, lpLVItem
->iItem
, &oldItemArea
);
4427 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
4428 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_text(lpLVItem
->pszText
))
4430 pszText
= lpLVItem
->pszText
;
4431 lpLVItem
->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
4434 /* actually set the fields */
4435 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
4437 if (lpLVItem
->iSubItem
)
4438 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
4440 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
4441 if (!IsWindow(hwndSelf
))
4444 /* redraw item, if necessary */
4445 if (bChanged
&& !infoPtr
->bIsDrawing
)
4447 /* this little optimization eliminates some nasty flicker */
4448 if ( infoPtr
->uView
== LV_VIEW_DETAILS
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
4449 !(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) &&
4450 lpLVItem
->iSubItem
> 0 && lpLVItem
->iSubItem
<= DPA_GetPtrCount(infoPtr
->hdpaColumns
) )
4451 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
4454 LISTVIEW_InvalidateRect(infoPtr
, &oldItemArea
);
4455 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
4461 textfreeT(lpLVItem
->pszText
, isW
);
4462 lpLVItem
->pszText
= pszText
;
4470 * Retrieves the index of the item at coordinate (0, 0) of the client area.
4473 * [I] infoPtr : valid pointer to the listview structure
4478 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*infoPtr
)
4481 SCROLLINFO scrollInfo
;
4483 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
4484 scrollInfo
.fMask
= SIF_POS
;
4486 if (infoPtr
->uView
== LV_VIEW_LIST
)
4488 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
4489 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
4491 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
4493 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
4494 nItem
= scrollInfo
.nPos
;
4498 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
4499 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
4502 TRACE("nItem=%d\n", nItem
);
4510 * Erases the background of the given rectangle
4513 * [I] infoPtr : valid pointer to the listview structure
4514 * [I] hdc : device context handle
4515 * [I] lprcBox : clipping rectangle
4521 static inline BOOL
LISTVIEW_FillBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
4523 if (!infoPtr
->hBkBrush
) return FALSE
;
4525 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, wine_dbgstr_rect(lprcBox
), infoPtr
->hBkBrush
);
4527 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
4535 * [I] infoPtr : valid pointer to the listview structure
4536 * [I] hdc : device context handle
4537 * [I] nItem : item index
4538 * [I] nSubItem : subitem index
4539 * [I] pos : item position in client coordinates
4540 * [I] cdmode : custom draw mode
4546 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, INT nSubItem
, POINT pos
, DWORD cdmode
)
4549 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
4550 static WCHAR szCallback
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
4551 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
4553 RECT rcSelect
, rcBox
, rcIcon
, rcLabel
, rcStateIcon
;
4554 NMLVCUSTOMDRAW nmlvcd
;
4559 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc
, nItem
, nSubItem
, wine_dbgstr_point(&pos
));
4561 /* get information needed for drawing the item */
4562 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_PARAM
;
4563 if (nSubItem
== 0) lvItem
.mask
|= LVIF_STATE
;
4564 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
4565 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
| LVIS_CUT
;
4566 lvItem
.iItem
= nItem
;
4567 lvItem
.iSubItem
= nSubItem
;
4570 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4571 lvItem
.pszText
= szDispText
;
4572 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
4573 if (nSubItem
> 0 && (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
4574 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
4575 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= szCallback
;
4576 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
4578 /* now check if we need to update the focus rectangle */
4579 lprcFocus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
4581 if (!lprcFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
4582 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcSelect
, &rcIcon
, &rcStateIcon
, &rcLabel
);
4583 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
4584 OffsetRect(&rcSelect
, pos
.x
, pos
.y
);
4585 OffsetRect(&rcIcon
, pos
.x
, pos
.y
);
4586 OffsetRect(&rcStateIcon
, pos
.x
, pos
.y
);
4587 OffsetRect(&rcLabel
, pos
.x
, pos
.y
);
4588 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
4589 wine_dbgstr_rect(&rcBox
), wine_dbgstr_rect(&rcSelect
),
4590 wine_dbgstr_rect(&rcIcon
), wine_dbgstr_rect(&rcLabel
));
4592 /* fill in the custom draw structure */
4593 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
4595 hOldFont
= GetCurrentObject(hdc
, OBJ_FONT
);
4596 if (nSubItem
> 0) cdmode
= infoPtr
->cditemmode
;
4597 if (cdmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
4598 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
4599 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4600 if (nSubItem
== 0) infoPtr
->cditemmode
= cdsubitemmode
;
4601 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
4602 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
4603 if (nSubItem
== 0 && cdsubitemmode
== CDRF_NOTIFYITEMDRAW
)
4605 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
4606 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
4608 if (nSubItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
4609 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
4610 else if ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) == FALSE
)
4611 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, TRUE
);
4613 /* in full row select, subitems, will just use main item's colors */
4614 if (nSubItem
&& infoPtr
->uView
== LV_VIEW_DETAILS
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
4615 nmlvcd
.clrTextBk
= CLR_NONE
;
4617 /* FIXME: temporary hack */
4618 rcSelect
.left
= rcLabel
.left
;
4620 /* draw the selection background, if we're drawing the main item */
4623 /* in icon mode, the label rect is really what we want to draw the
4625 if (infoPtr
->uView
== LV_VIEW_ICON
)
4628 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
4630 /* we have to update left focus bound too if item isn't in leftmost column
4631 and reduce right box bound */
4632 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
4636 if ((leftmost
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, 0, 0)))
4638 INT Originx
= pos
.x
- LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
;
4639 INT index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
4640 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
4642 rcBox
.right
= LISTVIEW_GetColumnInfo(infoPtr
, index
)->rcHeader
.right
+ Originx
;
4643 rcSelect
.left
= LISTVIEW_GetColumnInfo(infoPtr
, leftmost
)->rcHeader
.left
+ Originx
;
4647 rcSelect
.right
= rcBox
.right
;
4650 if (nmlvcd
.clrTextBk
!= CLR_NONE
)
4651 ExtTextOutW(hdc
, rcSelect
.left
, rcSelect
.top
, ETO_OPAQUE
, &rcSelect
, NULL
, 0, NULL
);
4652 /* store new focus rectangle */
4653 if (infoPtr
->nFocusedItem
== nItem
) infoPtr
->rcFocus
= rcSelect
;
4657 if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && (nSubItem
== 0))
4659 UINT uStateImage
= STATEIMAGEINDEX(lvItem
.state
);
4662 TRACE("uStateImage=%d\n", uStateImage
);
4663 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
,
4664 rcStateIcon
.left
, rcStateIcon
.top
, ILD_NORMAL
);
4669 himl
= (infoPtr
->uView
== LV_VIEW_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
4670 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
))
4674 TRACE("iImage=%d\n", lvItem
.iImage
);
4676 if (lvItem
.state
& (LVIS_SELECTED
| LVIS_CUT
) && infoPtr
->bFocus
)
4677 style
= ILD_SELECTED
;
4681 ImageList_DrawEx(himl
, lvItem
.iImage
, hdc
, rcIcon
.left
, rcIcon
.top
,
4682 rcIcon
.right
- rcIcon
.left
, rcIcon
.bottom
- rcIcon
.top
, infoPtr
->clrBk
,
4683 lvItem
.state
& LVIS_CUT
? RGB(255, 255, 255) : CLR_DEFAULT
,
4687 /* Don't bother painting item being edited */
4688 if (infoPtr
->hwndEdit
&& nItem
== infoPtr
->nEditLabelItem
&& nSubItem
== 0) goto postpaint
;
4690 /* figure out the text drawing flags */
4691 uFormat
= (infoPtr
->uView
== LV_VIEW_ICON
? (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
4692 if (infoPtr
->uView
== LV_VIEW_ICON
)
4693 uFormat
= (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
4696 switch (LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
4698 case LVCFMT_RIGHT
: uFormat
|= DT_RIGHT
; break;
4699 case LVCFMT_CENTER
: uFormat
|= DT_CENTER
; break;
4700 default: uFormat
|= DT_LEFT
;
4703 if (!(uFormat
& (DT_RIGHT
| DT_CENTER
)))
4705 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
4706 else rcLabel
.left
+= LABEL_HOR_PADDING
;
4708 else if (uFormat
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
4710 /* for GRIDLINES reduce the bottom so the text formats correctly */
4711 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
4714 if ((!(lvItem
.state
& LVIS_SELECTED
) || !infoPtr
->bFocus
) && (infoPtr
->dwLvExStyle
& LVS_EX_TRANSPARENTSHADOWTEXT
))
4715 DrawShadowText(hdc
, lvItem
.pszText
, -1, &rcLabel
, uFormat
, RGB(255, 255, 255), RGB(0, 0, 0), 2, 2);
4717 DrawTextW(hdc
, lvItem
.pszText
, -1, &rcLabel
, uFormat
);
4720 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
4721 notify_postpaint(infoPtr
, &nmlvcd
);
4722 if (cdsubitemmode
& CDRF_NEWFONT
)
4723 SelectObject(hdc
, hOldFont
);
4729 * Draws listview items when in owner draw mode.
4732 * [I] infoPtr : valid pointer to the listview structure
4733 * [I] hdc : device context handle
4738 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4740 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
4741 DWORD cditemmode
= CDRF_DODEFAULT
;
4742 NMLVCUSTOMDRAW nmlvcd
;
4743 POINT Origin
, Position
;
4749 ZeroMemory(&dis
, sizeof(dis
));
4751 /* Get scroll info once before loop */
4752 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4754 /* iterate through the invalidated rows */
4755 while(iterator_next(i
))
4757 item
.iItem
= i
->nItem
;
4759 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4760 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4761 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
4763 dis
.CtlType
= ODT_LISTVIEW
;
4765 dis
.itemID
= item
.iItem
;
4766 dis
.itemAction
= ODA_DRAWENTIRE
;
4768 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
4769 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
4770 dis
.hwndItem
= infoPtr
->hwndSelf
;
4772 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
4773 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
4774 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
4775 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
4776 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
4777 dis
.itemData
= item
.lParam
;
4779 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), wine_dbgstr_rect(&dis
.rcItem
));
4782 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4783 * structure for the rest. of the paint cycle
4785 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
4786 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
4787 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4789 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
4791 prepaint_setup (infoPtr
, hdc
, &nmlvcd
, FALSE
);
4792 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
4795 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
4796 notify_postpaint(infoPtr
, &nmlvcd
);
4802 * Draws listview items when in report display mode.
4805 * [I] infoPtr : valid pointer to the listview structure
4806 * [I] hdc : device context handle
4807 * [I] cdmode : custom draw mode
4812 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4815 RECT rcClip
, rcItem
;
4816 POINT Origin
, Position
;
4823 /* figure out what to draw */
4824 rgntype
= GetClipBox(hdc
, &rcClip
);
4825 if (rgntype
== NULLREGION
) return;
4827 /* Get scroll info once before loop */
4828 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4830 colRanges
= ranges_create(DPA_GetPtrCount(infoPtr
->hdpaColumns
));
4832 /* narrow down the columns we need to paint */
4833 for(col
= 0; col
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); col
++)
4835 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, col
, 0);
4837 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
4838 if ((rcItem
.right
+ Origin
.x
>= rcClip
.left
) && (rcItem
.left
+ Origin
.x
< rcClip
.right
))
4839 ranges_additem(colRanges
, index
);
4841 iterator_rangesitems(&j
, colRanges
);
4843 /* in full row select, we _have_ to draw the main item */
4844 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4847 /* iterate through the invalidated rows */
4848 while(iterator_next(i
))
4850 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4851 Position
.y
+= Origin
.y
;
4853 /* iterate through the invalidated columns */
4854 while(iterator_next(&j
))
4856 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4857 Position
.x
= (j
.nItem
== 0) ? rcItem
.left
+ Origin
.x
: Origin
.x
;
4859 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
4862 rcItem
.bottom
= infoPtr
->nItemHeight
;
4863 OffsetRect(&rcItem
, Origin
.x
, Position
.y
);
4864 if (!RectVisible(hdc
, &rcItem
)) continue;
4867 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, j
.nItem
, Position
, cdmode
);
4870 iterator_destroy(&j
);
4875 * Draws the gridlines if necessary when in report display mode.
4878 * [I] infoPtr : valid pointer to the listview structure
4879 * [I] hdc : device context handle
4884 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
4890 RECT rcClip
, rcItem
= {0};
4898 /* figure out what to draw */
4899 rgntype
= GetClipBox(hdc
, &rcClip
);
4900 if (rgntype
== NULLREGION
) return;
4902 /* Get scroll info once before loop */
4903 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4905 colRanges
= ranges_create(DPA_GetPtrCount(infoPtr
->hdpaColumns
));
4907 /* narrow down the columns we need to paint */
4908 for(col
= 0; col
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); col
++)
4910 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, col
, 0);
4912 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
4913 if ((rcItem
.right
+ Origin
.x
>= rcClip
.left
) && (rcItem
.left
+ Origin
.x
< rcClip
.right
))
4914 ranges_additem(colRanges
, index
);
4917 /* is right most vertical line visible? */
4918 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
4920 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
4921 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
4922 rmost
= (rcItem
.right
+ Origin
.x
< rcClip
.right
);
4925 if ((hPen
= CreatePen( PS_SOLID
, 1, comctl32_color
.clr3dFace
)))
4927 hOldPen
= SelectObject ( hdc
, hPen
);
4929 /* draw the vertical lines for the columns */
4930 iterator_rangesitems(&j
, colRanges
);
4931 while(iterator_next(&j
))
4933 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4934 if (rcItem
.left
== 0) continue; /* skip leftmost column */
4935 rcItem
.left
+= Origin
.x
;
4936 rcItem
.right
+= Origin
.x
;
4937 rcItem
.top
= infoPtr
->rcList
.top
;
4938 rcItem
.bottom
= infoPtr
->rcList
.bottom
;
4939 TRACE("vert col=%d, rcItem=%s\n", j
.nItem
, wine_dbgstr_rect(&rcItem
));
4940 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4941 LineTo (hdc
, rcItem
.left
, rcItem
.bottom
);
4943 iterator_destroy(&j
);
4944 /* draw rightmost grid line if visible */
4947 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
4948 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
4949 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
4951 rcItem
.right
+= Origin
.x
;
4953 MoveToEx (hdc
, rcItem
.right
, infoPtr
->rcList
.top
, NULL
);
4954 LineTo (hdc
, rcItem
.right
, infoPtr
->rcList
.bottom
);
4957 /* draw the horizontal lines for the rows */
4958 itemheight
= LISTVIEW_CalculateItemHeight(infoPtr
);
4959 rcItem
.left
= infoPtr
->rcList
.left
;
4960 rcItem
.right
= infoPtr
->rcList
.right
;
4961 for(y
= Origin
.y
> 1 ? Origin
.y
- 1 : itemheight
- 1 + Origin
.y
% itemheight
; y
<=infoPtr
->rcList
.bottom
; y
+=itemheight
)
4963 rcItem
.bottom
= rcItem
.top
= y
;
4964 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem
));
4965 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4966 LineTo (hdc
, rcItem
.right
, rcItem
.top
);
4969 SelectObject( hdc
, hOldPen
);
4970 DeleteObject( hPen
);
4976 * Draws listview items when in list display mode.
4979 * [I] infoPtr : valid pointer to the listview structure
4980 * [I] hdc : device context handle
4981 * [I] cdmode : custom draw mode
4986 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4988 POINT Origin
, Position
;
4990 /* Get scroll info once before loop */
4991 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4993 while(iterator_prev(i
))
4995 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4996 Position
.x
+= Origin
.x
;
4997 Position
.y
+= Origin
.y
;
4999 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, 0, Position
, cdmode
);
5006 * Draws listview items.
5009 * [I] infoPtr : valid pointer to the listview structure
5010 * [I] hdc : device context handle
5011 * [I] prcErase : rect to be erased before refresh (may be NULL)
5016 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*prcErase
)
5018 COLORREF oldTextColor
= 0, oldBkColor
= 0, oldClrTextBk
, oldClrText
;
5019 NMLVCUSTOMDRAW nmlvcd
;
5026 HBITMAP hbmp
= NULL
;
5029 LISTVIEW_DUMP(infoPtr
);
5031 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
5032 TRACE("double buffering\n");
5034 hdc
= CreateCompatibleDC(hdcOrig
);
5036 ERR("Failed to create DC for backbuffer\n");
5039 hbmp
= CreateCompatibleBitmap(hdcOrig
, infoPtr
->rcList
.right
,
5040 infoPtr
->rcList
.bottom
);
5042 ERR("Failed to create bitmap for backbuffer\n");
5047 SelectObject(hdc
, hbmp
);
5048 SelectObject(hdc
, infoPtr
->hFont
);
5050 if(GetClipBox(hdcOrig
, &rcClient
))
5051 IntersectClipRect(hdc
, rcClient
.left
, rcClient
.top
, rcClient
.right
, rcClient
.bottom
);
5053 /* Save dc values we're gonna trash while drawing
5054 * FIXME: Should be done in LISTVIEW_DrawItem() */
5055 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
5056 oldBkMode
= GetBkMode(hdc
);
5057 oldBkColor
= GetBkColor(hdc
);
5058 oldTextColor
= GetTextColor(hdc
);
5061 infoPtr
->bIsDrawing
= TRUE
;
5064 LISTVIEW_FillBkgnd(infoPtr
, hdc
, prcErase
);
5065 } else if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
5066 /* If no erasing was done (usually because RedrawWindow was called
5067 * with RDW_INVALIDATE only) we need to copy the old contents into
5068 * the backbuffer before continuing. */
5069 BitBlt(hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
5070 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
5071 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
5072 hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
5075 /* FIXME: Shouldn't need to do this */
5076 oldClrTextBk
= infoPtr
->clrTextBk
;
5077 oldClrText
= infoPtr
->clrText
;
5079 infoPtr
->cditemmode
= CDRF_DODEFAULT
;
5081 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
5082 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
5083 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
5084 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
5085 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
5087 /* Use these colors to draw the items */
5088 infoPtr
->clrTextBk
= nmlvcd
.clrTextBk
;
5089 infoPtr
->clrText
= nmlvcd
.clrText
;
5091 /* nothing to draw */
5092 if(infoPtr
->nItemCount
== 0) goto enddraw
;
5094 /* figure out what we need to draw */
5095 iterator_visibleitems(&i
, infoPtr
, hdc
);
5096 range
= iterator_range(&i
);
5098 /* send cache hint notification */
5099 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5103 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
5104 nmlv
.iFrom
= range
.lower
;
5105 nmlv
.iTo
= range
.upper
- 1;
5106 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
5109 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
5110 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
5113 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
5114 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
5115 else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */
5116 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
5118 /* if we have a focus rect and it's visible, draw it */
5119 if (infoPtr
->bFocus
&& range
.lower
<= infoPtr
->nFocusedItem
&&
5120 (range
.upper
- 1) >= infoPtr
->nFocusedItem
)
5121 LISTVIEW_DrawFocusRect(infoPtr
, hdc
);
5123 iterator_destroy(&i
);
5126 /* For LVS_EX_GRIDLINES go and draw lines */
5127 /* This includes the case where there were *no* items */
5128 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
5129 LISTVIEW_RefreshReportGrid(infoPtr
, hdc
);
5131 /* Draw marquee rectangle if appropriate */
5132 if (infoPtr
->bMarqueeSelect
)
5134 SetBkColor(hdc
, RGB(255, 255, 255));
5135 SetTextColor(hdc
, RGB(0, 0, 0));
5136 DrawFocusRect(hdc
, &infoPtr
->marqueeDrawRect
);
5139 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
5140 notify_postpaint(infoPtr
, &nmlvcd
);
5142 infoPtr
->clrTextBk
= oldClrTextBk
;
5143 infoPtr
->clrText
= oldClrText
;
5146 BitBlt(hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
5147 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
5148 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
5149 hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
5154 SelectObject(hdc
, hOldFont
);
5155 SetBkMode(hdc
, oldBkMode
);
5156 SetBkColor(hdc
, oldBkColor
);
5157 SetTextColor(hdc
, oldTextColor
);
5160 infoPtr
->bIsDrawing
= FALSE
;
5166 * Calculates the approximate width and height of a given number of items.
5169 * [I] infoPtr : valid pointer to the listview structure
5170 * [I] nItemCount : number of items
5171 * [I] wWidth : width
5172 * [I] wHeight : height
5175 * Returns a DWORD. The width in the low word and the height in high word.
5177 static DWORD
LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
5178 WORD wWidth
, WORD wHeight
)
5180 DWORD dwViewRect
= 0;
5182 if (nItemCount
== -1)
5183 nItemCount
= infoPtr
->nItemCount
;
5185 if (infoPtr
->uView
== LV_VIEW_LIST
)
5187 INT nItemCountPerColumn
= 1;
5188 INT nColumnCount
= 0;
5190 if (wHeight
== 0xFFFF)
5192 /* use current height */
5193 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
5196 if (wHeight
< infoPtr
->nItemHeight
)
5197 wHeight
= infoPtr
->nItemHeight
;
5201 if (infoPtr
->nItemHeight
> 0)
5203 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
5204 if (nItemCountPerColumn
== 0)
5205 nItemCountPerColumn
= 1;
5207 if (nItemCount
% nItemCountPerColumn
!= 0)
5208 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
5210 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
5214 /* Microsoft padding magic */
5215 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
5216 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
5218 dwViewRect
= MAKELONG(wWidth
, wHeight
);
5220 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
5224 if (infoPtr
->nItemCount
> 0)
5226 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
5227 wWidth
= rcBox
.right
- rcBox
.left
;
5228 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
5232 /* use current height and width */
5233 if (wHeight
== 0xffff)
5234 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
5235 if (wWidth
== 0xffff)
5236 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
5239 dwViewRect
= MAKELONG(wWidth
, wHeight
);
5241 else if (infoPtr
->uView
== LV_VIEW_ICON
)
5247 nItemWidth
= infoPtr
->iconSpacing
.cx
;
5248 nItemHeight
= infoPtr
->iconSpacing
.cy
;
5250 if (wWidth
== 0xffff)
5251 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
5253 if (wWidth
< nItemWidth
)
5254 wWidth
= nItemWidth
;
5256 cols
= wWidth
/ nItemWidth
;
5257 if (cols
> nItemCount
)
5264 rows
= nItemCount
/ cols
;
5265 if (nItemCount
% cols
)
5271 wHeight
= (nItemHeight
* rows
)+2;
5272 wWidth
= (nItemWidth
* cols
)+2;
5274 dwViewRect
= MAKELONG(wWidth
, wHeight
);
5276 else if (infoPtr
->uView
== LV_VIEW_SMALLICON
)
5277 FIXME("uView == LV_VIEW_SMALLICON: not implemented\n");
5284 * Cancel edit label with saving item text.
5287 * [I] infoPtr : valid pointer to the listview structure
5290 * Always returns TRUE.
5292 static LRESULT
LISTVIEW_CancelEditLabel(LISTVIEW_INFO
*infoPtr
)
5294 if (infoPtr
->hwndEdit
)
5296 /* handle value will be lost after LISTVIEW_EndEditLabelT */
5297 HWND edit
= infoPtr
->hwndEdit
;
5299 LISTVIEW_EndEditLabelT(infoPtr
, TRUE
, IsWindowUnicode(infoPtr
->hwndEdit
));
5300 SendMessageW(edit
, WM_CLOSE
, 0, 0);
5308 * Create a drag image list for the specified item.
5311 * [I] infoPtr : valid pointer to the listview structure
5312 * [I] iItem : index of item
5313 * [O] lppt : Upper-left corner of the image
5316 * Returns a handle to the image list if successful, NULL otherwise.
5318 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
5324 HBITMAP hbmp
, hOldbmp
;
5325 HIMAGELIST dragList
= 0;
5326 TRACE("iItem=%d Count=%d\n", iItem
, infoPtr
->nItemCount
);
5328 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
|| !lppt
)
5331 rcItem
.left
= LVIR_BOUNDS
;
5332 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
5335 lppt
->x
= rcItem
.left
;
5336 lppt
->y
= rcItem
.top
;
5338 size
.cx
= rcItem
.right
- rcItem
.left
;
5339 size
.cy
= rcItem
.bottom
- rcItem
.top
;
5341 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
5342 hdc
= CreateCompatibleDC(hdcOrig
);
5343 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
5344 hOldbmp
= SelectObject(hdc
, hbmp
);
5346 rcItem
.left
= rcItem
.top
= 0;
5347 rcItem
.right
= size
.cx
;
5348 rcItem
.bottom
= size
.cy
;
5349 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
5352 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, 0, pos
, infoPtr
->cditemmode
))
5354 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
5355 SelectObject(hdc
, hOldbmp
);
5356 ImageList_Add(dragList
, hbmp
, 0);
5359 SelectObject(hdc
, hOldbmp
);
5363 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
5365 TRACE("ret=%p\n", dragList
);
5373 * Removes all listview items and subitems.
5376 * [I] infoPtr : valid pointer to the listview structure
5382 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
, BOOL destroy
)
5385 HDPA hdpaSubItems
= NULL
;
5394 /* we do it directly, to avoid notifications */
5395 ranges_clear(infoPtr
->selectionRanges
);
5396 infoPtr
->nSelectionMark
= -1;
5397 infoPtr
->nFocusedItem
= -1;
5398 SetRectEmpty(&infoPtr
->rcFocus
);
5399 /* But we are supposed to leave nHotItem as is! */
5402 /* send LVN_DELETEALLITEMS notification */
5403 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
5405 bSuppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
5407 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
5409 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
5411 /* send LVN_DELETEITEM notification, if not suppressed
5412 and if it is not a virtual listview */
5413 if (!bSuppress
) notify_deleteitem(infoPtr
, i
);
5414 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
5415 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5416 /* free id struct */
5417 j
= DPA_GetPtrIndex(infoPtr
->hdpaItemIds
, lpItem
->id
);
5418 lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, j
);
5419 DPA_DeletePtr(infoPtr
->hdpaItemIds
, j
);
5421 /* both item and subitem start with ITEMHDR header */
5422 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
5424 hdrItem
= DPA_GetPtr(hdpaSubItems
, j
);
5425 if (is_text(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
5428 DPA_Destroy(hdpaSubItems
);
5429 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
5431 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
5432 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
5433 infoPtr
->nItemCount
--;
5438 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
5439 LISTVIEW_UpdateScroll(infoPtr
);
5441 LISTVIEW_InvalidateList(infoPtr
);
5448 * Scrolls, and updates the columns, when a column is changing width.
5451 * [I] infoPtr : valid pointer to the listview structure
5452 * [I] nColumn : column to scroll
5453 * [I] dx : amount of scroll, in pixels
5458 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
5460 COLUMN_INFO
*lpColumnInfo
;
5466 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
5467 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
5468 rcCol
= lpColumnInfo
->rcHeader
;
5469 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
5470 rcCol
.left
= rcCol
.right
;
5472 /* adjust the other columns */
5473 hdi
.mask
= HDI_ORDER
;
5474 if (SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdi
))
5476 INT nOrder
= hdi
.iOrder
;
5477 for (nCol
= 0; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
5479 hdi
.mask
= HDI_ORDER
;
5480 SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nCol
, (LPARAM
)&hdi
);
5481 if (hdi
.iOrder
>= nOrder
) {
5482 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
5483 lpColumnInfo
->rcHeader
.left
+= dx
;
5484 lpColumnInfo
->rcHeader
.right
+= dx
;
5489 /* do not update screen if not in report mode */
5490 if (!is_redrawing(infoPtr
) || infoPtr
->uView
!= LV_VIEW_DETAILS
) return;
5492 /* Need to reset the item width when inserting a new column */
5493 infoPtr
->nItemWidth
+= dx
;
5495 LISTVIEW_UpdateScroll(infoPtr
);
5496 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
5498 /* scroll to cover the deleted column, and invalidate for redraw */
5499 rcOld
= infoPtr
->rcList
;
5500 rcOld
.left
= ptOrigin
.x
+ rcCol
.left
+ dx
;
5501 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
5506 * Removes a column from the listview control.
5509 * [I] infoPtr : valid pointer to the listview structure
5510 * [I] nColumn : column index
5516 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
5520 TRACE("nColumn=%d\n", nColumn
);
5522 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0
5523 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5525 /* While the MSDN specifically says that column zero should not be deleted,
5526 what actually happens is that the column itself is deleted but no items or subitems
5530 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
5532 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nColumn
, 0))
5535 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
5536 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
5538 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
5540 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
5542 INT nItem
, nSubItem
, i
;
5544 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
5546 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
5549 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
5551 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
5552 if (lpSubItem
->iSubItem
== nColumn
)
5555 lpDelItem
= lpSubItem
;
5557 else if (lpSubItem
->iSubItem
> nColumn
)
5559 lpSubItem
->iSubItem
--;
5563 /* if we found our subitem, zap it */
5567 if (is_text(lpDelItem
->hdr
.pszText
))
5568 Free(lpDelItem
->hdr
.pszText
);
5573 /* free dpa memory */
5574 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
5579 /* update the other column info */
5580 LISTVIEW_UpdateItemSize(infoPtr
);
5581 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
5582 LISTVIEW_InvalidateList(infoPtr
);
5584 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
5591 * Invalidates the listview after an item's insertion or deletion.
5594 * [I] infoPtr : valid pointer to the listview structure
5595 * [I] nItem : item index
5596 * [I] dir : -1 if deleting, 1 if inserting
5601 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
5603 INT nPerCol
, nItemCol
, nItemRow
;
5607 /* if we don't refresh, what's the point of scrolling? */
5608 if (!is_redrawing(infoPtr
)) return;
5610 assert (abs(dir
) == 1);
5612 /* arrange icons if autoarrange is on */
5613 if (is_autoarrange(infoPtr
))
5615 BOOL arrange
= TRUE
;
5616 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
5617 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
5618 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
5621 /* scrollbars need updating */
5622 LISTVIEW_UpdateScroll(infoPtr
);
5624 /* figure out the item's position */
5625 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
5626 nPerCol
= infoPtr
->nItemCount
+ 1;
5627 else if (infoPtr
->uView
== LV_VIEW_LIST
)
5628 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
5629 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
5632 nItemCol
= nItem
/ nPerCol
;
5633 nItemRow
= nItem
% nPerCol
;
5634 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5636 /* move the items below up a slot */
5637 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
5638 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
5639 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
5640 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
5641 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
5642 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
5643 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
5645 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll
), wine_dbgstr_rect(&infoPtr
->rcList
));
5646 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
5647 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
5650 /* report has only that column, so we're done */
5651 if (infoPtr
->uView
== LV_VIEW_DETAILS
) return;
5653 /* now for LISTs, we have to deal with the columns to the right */
5654 rcScroll
.left
= (nItemCol
+ 1) * infoPtr
->nItemWidth
;
5656 rcScroll
.right
= (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
5657 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
5658 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
5659 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
5660 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
5661 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
5666 * Removes an item from the listview control.
5669 * [I] infoPtr : valid pointer to the listview structure
5670 * [I] nItem : item index
5676 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
5679 const BOOL is_icon
= (infoPtr
->uView
== LV_VIEW_SMALLICON
|| infoPtr
->uView
== LV_VIEW_ICON
);
5681 TRACE("(nItem=%d)\n", nItem
);
5683 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5685 /* remove selection, and focus */
5687 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
5688 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
5690 /* send LVN_DELETEITEM notification. */
5691 if (!notify_deleteitem(infoPtr
, nItem
)) return FALSE
;
5693 /* we need to do this here, because we'll be deleting stuff */
5695 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
5697 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
5705 hdpaSubItems
= DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
5706 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5708 /* free id struct */
5709 i
= DPA_GetPtrIndex(infoPtr
->hdpaItemIds
, lpItem
->id
);
5710 lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, i
);
5711 DPA_DeletePtr(infoPtr
->hdpaItemIds
, i
);
5713 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
5715 hdrItem
= DPA_GetPtr(hdpaSubItems
, i
);
5716 if (is_text(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
5719 DPA_Destroy(hdpaSubItems
);
5724 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
5725 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
5728 infoPtr
->nItemCount
--;
5729 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
5731 /* now is the invalidation fun */
5733 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
5740 * Callback implementation for editlabel control
5743 * [I] infoPtr : valid pointer to the listview structure
5744 * [I] storeText : store edit box text as item text
5745 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
5751 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, BOOL storeText
, BOOL isW
)
5753 HWND hwndSelf
= infoPtr
->hwndSelf
;
5754 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
5755 NMLVDISPINFOW dispInfo
;
5756 INT editedItem
= infoPtr
->nEditLabelItem
;
5758 WCHAR
*pszText
= NULL
;
5763 DWORD len
= isW
? GetWindowTextLengthW(infoPtr
->hwndEdit
) : GetWindowTextLengthA(infoPtr
->hwndEdit
);
5767 if ((pszText
= Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))))
5769 if (isW
) GetWindowTextW(infoPtr
->hwndEdit
, pszText
, len
+1);
5770 else GetWindowTextA(infoPtr
->hwndEdit
, (CHAR
*)pszText
, len
+1);
5775 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
5777 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5778 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
5779 dispInfo
.item
.iItem
= editedItem
;
5780 dispInfo
.item
.iSubItem
= 0;
5781 dispInfo
.item
.stateMask
= ~0;
5782 dispInfo
.item
.pszText
= szDispText
;
5783 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
5784 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
))
5791 same
= (lstrcmpW(dispInfo
.item
.pszText
, pszText
) == 0);
5794 LPWSTR tmp
= textdupTtoW(pszText
, FALSE
);
5795 same
= (lstrcmpW(dispInfo
.item
.pszText
, tmp
) == 0);
5796 textfreeT(tmp
, FALSE
);
5799 /* add the text from the edit in */
5800 dispInfo
.item
.mask
|= LVIF_TEXT
;
5801 dispInfo
.item
.pszText
= same
? NULL
: pszText
;
5802 dispInfo
.item
.cchTextMax
= textlenT(dispInfo
.item
.pszText
, isW
);
5804 /* Do we need to update the Item Text */
5805 res
= notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
);
5807 infoPtr
->nEditLabelItem
= -1;
5808 infoPtr
->hwndEdit
= 0;
5810 if (!res
) goto cleanup
;
5812 if (!IsWindow(hwndSelf
))
5817 if (!pszText
) return TRUE
;
5824 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
5826 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, editedItem
);
5827 ITEM_INFO
* lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5828 if (lpItem
&& lpItem
->hdr
.pszText
== LPSTR_TEXTCALLBACKW
)
5830 LISTVIEW_InvalidateItem(infoPtr
, editedItem
);
5836 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5837 dispInfo
.item
.mask
= LVIF_TEXT
;
5838 dispInfo
.item
.iItem
= editedItem
;
5839 dispInfo
.item
.iSubItem
= 0;
5840 dispInfo
.item
.pszText
= pszText
;
5841 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
5842 res
= LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
5852 * Subclassed edit control windproc function
5855 * [I] hwnd : the edit window handle
5856 * [I] uMsg : the message that is to be processed
5857 * [I] wParam : first message parameter
5858 * [I] lParam : second message parameter
5859 * [I] isW : TRUE if input is Unicode
5864 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
5866 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
5869 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
5870 hwnd
, uMsg
, wParam
, lParam
, isW
);
5875 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
5879 WNDPROC editProc
= infoPtr
->EditWndProc
;
5880 infoPtr
->EditWndProc
= 0;
5881 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
5882 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
5886 if (VK_ESCAPE
== (INT
)wParam
)
5891 else if (VK_RETURN
== (INT
)wParam
)
5895 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
5899 if (infoPtr
->hwndEdit
)
5900 LISTVIEW_EndEditLabelT(infoPtr
, save
, isW
);
5902 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
5908 * Subclassed edit control Unicode windproc function
5911 * [I] hwnd : the edit window handle
5912 * [I] uMsg : the message that is to be processed
5913 * [I] wParam : first message parameter
5914 * [I] lParam : second message parameter
5918 static LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
5920 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
5925 * Subclassed edit control ANSI windproc function
5928 * [I] hwnd : the edit window handle
5929 * [I] uMsg : the message that is to be processed
5930 * [I] wParam : first message parameter
5931 * [I] lParam : second message parameter
5935 static LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
5937 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
5942 * Creates a subclassed edit control
5945 * [I] infoPtr : valid pointer to the listview structure
5946 * [I] text : initial text for the edit
5947 * [I] style : the window style
5948 * [I] isW : TRUE if input is Unicode
5952 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, BOOL isW
)
5954 static const DWORD style
= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
|WS_VISIBLE
;
5955 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
5958 TRACE("(%p, text=%s, isW=%d)\n", infoPtr
, debugtext_t(text
, isW
), isW
);
5960 /* window will be resized and positioned after LVN_BEGINLABELEDIT */
5962 hedit
= CreateWindowW(WC_EDITW
, text
, style
, 0, 0, 0, 0, infoPtr
->hwndSelf
, 0, hinst
, 0);
5964 hedit
= CreateWindowA(WC_EDITA
, (LPCSTR
)text
, style
, 0, 0, 0, 0, infoPtr
->hwndSelf
, 0, hinst
, 0);
5966 if (!hedit
) return 0;
5968 infoPtr
->EditWndProc
= (WNDPROC
)
5969 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
5970 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
5972 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);
5973 SendMessageW(hedit
, EM_SETLIMITTEXT
, DISP_TEXT_SIZE
-1, 0);
5980 * Begin in place editing of specified list view item
5983 * [I] infoPtr : valid pointer to the listview structure
5984 * [I] nItem : item index
5985 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
5991 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
5993 WCHAR disptextW
[DISP_TEXT_SIZE
] = { 0 };
5994 HWND hwndSelf
= infoPtr
->hwndSelf
;
5995 NMLVDISPINFOW dispInfo
;
5996 HFONT hOldFont
= NULL
;
6002 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
6004 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
6006 /* remove existing edit box */
6007 if (infoPtr
->hwndEdit
)
6009 SetFocus(infoPtr
->hwndSelf
);
6010 infoPtr
->hwndEdit
= 0;
6013 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6015 infoPtr
->nEditLabelItem
= nItem
;
6017 LISTVIEW_SetSelection(infoPtr
, nItem
);
6018 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
6019 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
6021 rect
.left
= LVIR_LABEL
;
6022 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
6024 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
6025 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
6026 dispInfo
.item
.iItem
= nItem
;
6027 dispInfo
.item
.iSubItem
= 0;
6028 dispInfo
.item
.stateMask
= ~0;
6029 dispInfo
.item
.pszText
= disptextW
;
6030 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
6031 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
6033 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, isW
);
6034 if (!infoPtr
->hwndEdit
) return 0;
6036 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
6038 if (!IsWindow(hwndSelf
))
6040 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
6041 infoPtr
->hwndEdit
= 0;
6045 TRACE("disp text=%s\n", debugtext_t(dispInfo
.item
.pszText
, isW
));
6047 /* position and display edit box */
6048 hdc
= GetDC(infoPtr
->hwndSelf
);
6050 /* select the font to get appropriate metric dimensions */
6052 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
6054 /* use real edit box content, it could be altered during LVN_BEGINLABELEDIT notification */
6055 GetWindowTextW(infoPtr
->hwndEdit
, disptextW
, DISP_TEXT_SIZE
);
6056 TRACE("edit box text=%s\n", debugstr_w(disptextW
));
6058 /* get string length in pixels */
6059 GetTextExtentPoint32W(hdc
, disptextW
, lstrlenW(disptextW
), &sz
);
6061 /* add extra spacing for the next character */
6062 GetTextMetricsW(hdc
, &tm
);
6063 sz
.cx
+= tm
.tmMaxCharWidth
* 2;
6066 SelectObject(hdc
, hOldFont
);
6068 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6070 sz
.cy
= rect
.bottom
- rect
.top
+ 2;
6073 TRACE("moving edit=(%d,%d)-(%d,%d)\n", rect
.left
, rect
.top
, sz
.cx
, sz
.cy
);
6074 MoveWindow(infoPtr
->hwndEdit
, rect
.left
, rect
.top
, sz
.cx
, sz
.cy
, FALSE
);
6075 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
6076 SetFocus(infoPtr
->hwndEdit
);
6077 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
6078 return infoPtr
->hwndEdit
;
6084 * Ensures the specified item is visible, scrolling into view if necessary.
6087 * [I] infoPtr : valid pointer to the listview structure
6088 * [I] nItem : item index
6089 * [I] bPartial : partially or entirely visible
6095 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
6097 INT nScrollPosHeight
= 0;
6098 INT nScrollPosWidth
= 0;
6099 INT nHorzAdjust
= 0;
6100 INT nVertAdjust
= 0;
6103 RECT rcItem
, rcTemp
;
6105 rcItem
.left
= LVIR_BOUNDS
;
6106 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
6108 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
6110 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
6112 /* scroll left/right, but in LV_VIEW_DETAILS mode */
6113 if (infoPtr
->uView
== LV_VIEW_LIST
)
6114 nScrollPosWidth
= infoPtr
->nItemWidth
;
6115 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
6116 nScrollPosWidth
= 1;
6118 if (rcItem
.left
< infoPtr
->rcList
.left
)
6121 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
6126 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
6130 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
6132 /* scroll up/down, but not in LVS_LIST mode */
6133 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
6134 nScrollPosHeight
= infoPtr
->nItemHeight
;
6135 else if ((infoPtr
->uView
== LV_VIEW_ICON
) || (infoPtr
->uView
== LV_VIEW_SMALLICON
))
6136 nScrollPosHeight
= 1;
6138 if (rcItem
.top
< infoPtr
->rcList
.top
)
6141 if (infoPtr
->uView
!= LV_VIEW_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
6146 if (infoPtr
->uView
!= LV_VIEW_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
6150 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
6152 if (nScrollPosWidth
)
6154 INT diff
= nHorzDiff
/ nScrollPosWidth
;
6155 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
6156 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
);
6159 if (nScrollPosHeight
)
6161 INT diff
= nVertDiff
/ nScrollPosHeight
;
6162 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
6163 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
);
6171 * Searches for an item with specific characteristics.
6174 * [I] hwnd : window handle
6175 * [I] nStart : base item index
6176 * [I] lpFindInfo : item information to look for
6179 * SUCCESS : index of item
6182 static INT
LISTVIEW_FindItemW(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
6183 const LVFINDINFOW
*lpFindInfo
)
6185 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6186 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
6187 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
6188 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
6189 POINT Position
, Destination
;
6192 /* Search in virtual listviews should be done by application, not by
6193 listview control, so we just send LVN_ODFINDITEMW and return the result */
6194 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
6198 nmlv
.iStart
= nStart
;
6199 nmlv
.lvfi
= *lpFindInfo
;
6200 return notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
6203 if (!lpFindInfo
|| nItem
< 0) return -1;
6206 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
) ||
6207 lpFindInfo
->flags
& LVFI_SUBSTRING
)
6209 lvItem
.mask
|= LVIF_TEXT
;
6210 lvItem
.pszText
= szDispText
;
6211 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6214 if (lpFindInfo
->flags
& LVFI_WRAP
)
6217 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
6218 (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
))
6223 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6224 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
6225 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
6226 switch(lpFindInfo
->vkDirection
)
6228 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
6229 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
6230 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
6231 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
6232 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
6233 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
6234 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
6236 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
6237 Destination
.x
= rcArea
.right
;
6238 Destination
.y
= rcArea
.bottom
;
6240 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
6244 else Destination
.x
= Destination
.y
= 0;
6246 /* if LVFI_PARAM is specified, all other flags are ignored */
6247 if (lpFindInfo
->flags
& LVFI_PARAM
)
6249 lvItem
.mask
|= LVIF_PARAM
;
6251 lvItem
.mask
&= ~LVIF_TEXT
;
6255 for (; nItem
< nLast
; nItem
++)
6257 lvItem
.iItem
= nItem
;
6258 lvItem
.iSubItem
= 0;
6259 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
6261 if (lvItem
.mask
& LVIF_PARAM
)
6263 if (lpFindInfo
->lParam
== lvItem
.lParam
)
6269 if (lvItem
.mask
& LVIF_TEXT
)
6271 if (lpFindInfo
->flags
& (LVFI_PARTIAL
| LVFI_SUBSTRING
))
6273 WCHAR
*p
= strstrW(lvItem
.pszText
, lpFindInfo
->psz
);
6274 if (!p
|| p
!= lvItem
.pszText
) continue;
6278 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
6282 if (!bNearest
) return nItem
;
6284 /* This is very inefficient. To do a good job here,
6285 * we need a sorted array of (x,y) item positions */
6286 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
6288 /* compute the distance^2 to the destination */
6289 xdist
= Destination
.x
- Position
.x
;
6290 ydist
= Destination
.y
- Position
.y
;
6291 dist
= xdist
* xdist
+ ydist
* ydist
;
6293 /* remember the distance, and item if it's closer */
6297 nNearestItem
= nItem
;
6304 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
6309 return nNearestItem
;
6314 * Searches for an item with specific characteristics.
6317 * [I] hwnd : window handle
6318 * [I] nStart : base item index
6319 * [I] lpFindInfo : item information to look for
6322 * SUCCESS : index of item
6325 static INT
LISTVIEW_FindItemA(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
6326 const LVFINDINFOA
*lpFindInfo
)
6328 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
) ||
6329 lpFindInfo
->flags
& LVFI_SUBSTRING
;
6334 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
6335 if (hasText
) fiw
.psz
= strW
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
6336 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
6337 textfreeT(strW
, FALSE
);
6343 * Retrieves column attributes.
6346 * [I] infoPtr : valid pointer to the listview structure
6347 * [I] nColumn : column index
6348 * [IO] lpColumn : column information
6349 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
6350 * otherwise it is in fact a LPLVCOLUMNA
6356 static BOOL
LISTVIEW_GetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
6358 COLUMN_INFO
*lpColumnInfo
;
6361 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6362 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
6364 /* initialize memory */
6365 ZeroMemory(&hdi
, sizeof(hdi
));
6367 if (lpColumn
->mask
& LVCF_TEXT
)
6369 hdi
.mask
|= HDI_TEXT
;
6370 hdi
.pszText
= lpColumn
->pszText
;
6371 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
6374 if (lpColumn
->mask
& LVCF_IMAGE
)
6375 hdi
.mask
|= HDI_IMAGE
;
6377 if (lpColumn
->mask
& LVCF_ORDER
)
6378 hdi
.mask
|= HDI_ORDER
;
6380 if (lpColumn
->mask
& LVCF_SUBITEM
)
6381 hdi
.mask
|= HDI_LPARAM
;
6383 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
6385 if (lpColumn
->mask
& LVCF_FMT
)
6386 lpColumn
->fmt
= lpColumnInfo
->fmt
;
6388 if (lpColumn
->mask
& LVCF_WIDTH
)
6389 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
6391 if (lpColumn
->mask
& LVCF_IMAGE
)
6392 lpColumn
->iImage
= hdi
.iImage
;
6394 if (lpColumn
->mask
& LVCF_ORDER
)
6395 lpColumn
->iOrder
= hdi
.iOrder
;
6397 if (lpColumn
->mask
& LVCF_SUBITEM
)
6398 lpColumn
->iSubItem
= hdi
.lParam
;
6400 if (lpColumn
->mask
& LVCF_MINWIDTH
)
6401 lpColumn
->cxMin
= lpColumnInfo
->cxMin
;
6407 static BOOL
LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
6409 TRACE("iCount=%d, lpiArray=%p\n", iCount
, lpiArray
);
6414 return SendMessageW(infoPtr
->hwndHeader
, HDM_GETORDERARRAY
, iCount
, (LPARAM
)lpiArray
);
6419 * Retrieves the column width.
6422 * [I] infoPtr : valid pointer to the listview structure
6423 * [I] int : column index
6426 * SUCCESS : column width
6429 static INT
LISTVIEW_GetColumnWidth(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
6431 INT nColumnWidth
= 0;
6434 TRACE("nColumn=%d\n", nColumn
);
6436 /* we have a 'column' in LIST and REPORT mode only */
6437 switch(infoPtr
->uView
)
6440 nColumnWidth
= infoPtr
->nItemWidth
;
6442 case LV_VIEW_DETAILS
:
6443 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDN_ITEMCHANGED.
6444 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
6445 * HDN_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
6447 * TODO: should we do the same in LVM_GETCOLUMN?
6449 hdItem
.mask
= HDI_WIDTH
;
6450 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdItem
))
6452 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr
->hwndSelf
, nColumn
);
6455 nColumnWidth
= hdItem
.cxy
;
6459 TRACE("nColumnWidth=%d\n", nColumnWidth
);
6460 return nColumnWidth
;
6465 * In list or report display mode, retrieves the number of items that can fit
6466 * vertically in the visible area. In icon or small icon display mode,
6467 * retrieves the total number of visible items.
6470 * [I] infoPtr : valid pointer to the listview structure
6473 * Number of fully visible items.
6475 static INT
LISTVIEW_GetCountPerPage(const LISTVIEW_INFO
*infoPtr
)
6477 switch (infoPtr
->uView
)
6480 case LV_VIEW_SMALLICON
:
6481 return infoPtr
->nItemCount
;
6482 case LV_VIEW_DETAILS
:
6483 return LISTVIEW_GetCountPerColumn(infoPtr
);
6485 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
6493 * Retrieves an image list handle.
6496 * [I] infoPtr : valid pointer to the listview structure
6497 * [I] nImageList : image list identifier
6500 * SUCCESS : image list handle
6503 static HIMAGELIST
LISTVIEW_GetImageList(const LISTVIEW_INFO
*infoPtr
, INT nImageList
)
6507 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
6508 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
6509 case LVSIL_STATE
: return infoPtr
->himlState
;
6510 case LVSIL_GROUPHEADER
:
6511 FIXME("LVSIL_GROUPHEADER not supported\n");
6514 WARN("got unknown imagelist index - %d\n", nImageList
);
6519 /* LISTVIEW_GetISearchString */
6523 * Retrieves item attributes.
6526 * [I] hwnd : window handle
6527 * [IO] lpLVItem : item info
6528 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6529 * if FALSE, then lpLVItem is a LPLVITEMA.
6532 * This is the internal 'GetItem' interface -- it tries to
6533 * be smart and avoid text copies, if possible, by modifying
6534 * lpLVItem->pszText to point to the text string. Please note
6535 * that this is not always possible (e.g. OWNERDATA), so on
6536 * entry you *must* supply valid values for pszText, and cchTextMax.
6537 * The only difference to the documented interface is that upon
6538 * return, you should use *only* the lpLVItem->pszText, rather than
6539 * the buffer pointer you provided on input. Most code already does
6540 * that, so it's not a problem.
6541 * For the two cases when the text must be copied (that is,
6542 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
6548 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
6550 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
6551 NMLVDISPINFOW dispInfo
;
6557 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6559 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
6562 if (lpLVItem
->mask
== 0) return TRUE
;
6563 TRACE("mask=%x\n", lpLVItem
->mask
);
6565 /* make a local copy */
6566 isubitem
= lpLVItem
->iSubItem
;
6568 /* a quick optimization if all we're asked is the focus state
6569 * these queries are worth optimising since they are common,
6570 * and can be answered in constant time, without the heavy accesses */
6571 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
6572 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
6574 lpLVItem
->state
= 0;
6575 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
6576 lpLVItem
->state
|= LVIS_FOCUSED
;
6580 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
6582 /* if the app stores all the data, handle it separately */
6583 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
6585 dispInfo
.item
.state
= 0;
6587 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
6588 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) ||
6589 ((lpLVItem
->mask
& LVIF_STATE
) && (infoPtr
->uCallbackMask
& lpLVItem
->stateMask
)))
6591 UINT mask
= lpLVItem
->mask
;
6593 /* NOTE: copy only fields which we _know_ are initialized, some apps
6594 * depend on the uninitialized fields being 0 */
6595 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
6596 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
6597 dispInfo
.item
.iSubItem
= isubitem
;
6598 if (lpLVItem
->mask
& LVIF_TEXT
)
6600 if (lpLVItem
->mask
& LVIF_NORECOMPUTE
)
6602 dispInfo
.item
.mask
&= ~(LVIF_TEXT
| LVIF_NORECOMPUTE
);
6605 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
6606 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
6609 if (lpLVItem
->mask
& LVIF_STATE
)
6610 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
6611 /* could be zeroed on LVIF_NORECOMPUTE case */
6612 if (dispInfo
.item
.mask
)
6614 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
6615 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
6616 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6618 /* full size structure expected - _WIN32IE >= 0x560 */
6619 *lpLVItem
= dispInfo
.item
;
6621 else if (lpLVItem
->mask
& LVIF_INDENT
)
6623 /* indent member expected - _WIN32IE >= 0x300 */
6624 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
6628 /* minimal structure expected */
6629 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
6631 lpLVItem
->mask
= mask
;
6632 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
6636 /* make sure lParam is zeroed out */
6637 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
6639 /* callback marked pointer required here */
6640 if ((lpLVItem
->mask
& LVIF_TEXT
) && (lpLVItem
->mask
& LVIF_NORECOMPUTE
))
6641 lpLVItem
->pszText
= LPSTR_TEXTCALLBACKW
;
6643 /* we store only a little state, so if we're not asked, we're done */
6644 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
6646 /* if focus is handled by us, report it */
6647 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
6649 lpLVItem
->state
&= ~LVIS_FOCUSED
;
6650 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
6651 lpLVItem
->state
|= LVIS_FOCUSED
;
6654 /* and do the same for selection, if we handle it */
6655 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
6657 lpLVItem
->state
&= ~LVIS_SELECTED
;
6658 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
6659 lpLVItem
->state
|= LVIS_SELECTED
;
6665 /* find the item and subitem structures before we proceed */
6666 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
6667 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
6672 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
6673 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
6676 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
6681 pItemHdr
= &lpItem
->hdr
;
6683 /* Do we need to query the state from the app? */
6684 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
6686 dispInfo
.item
.mask
|= LVIF_STATE
;
6687 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
6690 /* Do we need to enquire about the image? */
6691 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
6692 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
6694 dispInfo
.item
.mask
|= LVIF_IMAGE
;
6695 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
6698 /* Only items support indentation */
6699 if ((lpLVItem
->mask
& LVIF_INDENT
) && lpItem
->iIndent
== I_INDENTCALLBACK
&&
6702 dispInfo
.item
.mask
|= LVIF_INDENT
;
6703 dispInfo
.item
.iIndent
= I_INDENTCALLBACK
;
6706 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
6707 if ((lpLVItem
->mask
& LVIF_TEXT
) && !(lpLVItem
->mask
& LVIF_NORECOMPUTE
) &&
6708 !is_text(pItemHdr
->pszText
))
6710 dispInfo
.item
.mask
|= LVIF_TEXT
;
6711 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
6712 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
6713 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
6714 *dispInfo
.item
.pszText
= '\0';
6717 /* If we don't have all the requested info, query the application */
6718 if (dispInfo
.item
.mask
)
6720 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
6721 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
6722 dispInfo
.item
.lParam
= lpItem
->lParam
;
6723 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
6724 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
6727 /* we should not store values for subitems */
6728 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
6730 /* Now, handle the iImage field */
6731 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
6733 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
6734 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
6735 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
6737 else if (lpLVItem
->mask
& LVIF_IMAGE
)
6739 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
6740 lpLVItem
->iImage
= pItemHdr
->iImage
;
6742 lpLVItem
->iImage
= 0;
6745 /* The pszText field */
6746 if (dispInfo
.item
.mask
& LVIF_TEXT
)
6748 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
6749 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
6751 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
6753 else if (lpLVItem
->mask
& LVIF_TEXT
)
6755 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
6756 if (isW
|| !is_text(pItemHdr
->pszText
)) lpLVItem
->pszText
= pItemHdr
->pszText
;
6757 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
6760 /* Next is the lParam field */
6761 if (dispInfo
.item
.mask
& LVIF_PARAM
)
6763 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
6764 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
6765 lpItem
->lParam
= dispInfo
.item
.lParam
;
6767 else if (lpLVItem
->mask
& LVIF_PARAM
)
6768 lpLVItem
->lParam
= lpItem
->lParam
;
6770 /* if this is a subitem, we're done */
6771 if (isubitem
) return TRUE
;
6773 /* ... the state field (this one is different due to uCallbackmask) */
6774 if (lpLVItem
->mask
& LVIF_STATE
)
6776 lpLVItem
->state
= lpItem
->state
& lpLVItem
->stateMask
;
6777 if (dispInfo
.item
.mask
& LVIF_STATE
)
6779 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
6780 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
6782 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
6784 lpLVItem
->state
&= ~LVIS_FOCUSED
;
6785 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
6786 lpLVItem
->state
|= LVIS_FOCUSED
;
6788 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
6790 lpLVItem
->state
&= ~LVIS_SELECTED
;
6791 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
6792 lpLVItem
->state
|= LVIS_SELECTED
;
6796 /* and last, but not least, the indent field */
6797 if (dispInfo
.item
.mask
& LVIF_INDENT
)
6799 lpLVItem
->iIndent
= dispInfo
.item
.iIndent
;
6800 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && lpItem
->iIndent
== I_INDENTCALLBACK
)
6801 lpItem
->iIndent
= dispInfo
.item
.iIndent
;
6803 else if (lpLVItem
->mask
& LVIF_INDENT
)
6805 lpLVItem
->iIndent
= lpItem
->iIndent
;
6813 * Retrieves item attributes.
6816 * [I] hwnd : window handle
6817 * [IO] lpLVItem : item info
6818 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6819 * if FALSE, then lpLVItem is a LPLVITEMA.
6822 * This is the external 'GetItem' interface -- it properly copies
6823 * the text in the provided buffer.
6829 static BOOL
LISTVIEW_GetItemExtT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
6834 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
6837 pszText
= lpLVItem
->pszText
;
6838 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
6839 if (bResult
&& (lpLVItem
->mask
& LVIF_TEXT
) && lpLVItem
->pszText
!= pszText
)
6841 if (lpLVItem
->pszText
!= LPSTR_TEXTCALLBACKW
)
6842 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
6844 pszText
= LPSTR_TEXTCALLBACKW
;
6846 lpLVItem
->pszText
= pszText
;
6854 * Retrieves the position (upper-left) of the listview control item.
6855 * Note that for LVS_ICON style, the upper-left is that of the icon
6856 * and not the bounding box.
6859 * [I] infoPtr : valid pointer to the listview structure
6860 * [I] nItem : item index
6861 * [O] lpptPosition : coordinate information
6867 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
6871 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
6873 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
6875 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6876 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
6878 if (infoPtr
->uView
== LV_VIEW_ICON
)
6880 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
6881 lpptPosition
->y
+= ICON_TOP_PADDING
;
6883 lpptPosition
->x
+= Origin
.x
;
6884 lpptPosition
->y
+= Origin
.y
;
6886 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition
));
6893 * Retrieves the bounding rectangle for a listview control item.
6896 * [I] infoPtr : valid pointer to the listview structure
6897 * [I] nItem : item index
6898 * [IO] lprc : bounding rectangle coordinates
6899 * lprc->left specifies the portion of the item for which the bounding
6900 * rectangle will be retrieved.
6902 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
6903 * including the icon and label.
6906 * * Experiment shows that native control returns:
6907 * * width = min (48, length of text line)
6908 * * .left = position.x - (width - iconsize.cx)/2
6909 * * .right = .left + width
6910 * * height = #lines of text * ntmHeight + icon height + 8
6911 * * .top = position.y - 2
6912 * * .bottom = .top + height
6913 * * separation between items .y = itemSpacing.cy - height
6914 * * .x = itemSpacing.cx - width
6915 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
6918 * * Experiment shows that native control returns:
6919 * * width = iconSize.cx + 16
6920 * * .left = position.x - (width - iconsize.cx)/2
6921 * * .right = .left + width
6922 * * height = iconSize.cy + 4
6923 * * .top = position.y - 2
6924 * * .bottom = .top + height
6925 * * separation between items .y = itemSpacing.cy - height
6926 * * .x = itemSpacing.cx - width
6927 * LVIR_LABEL Returns the bounding rectangle of the item text.
6930 * * Experiment shows that native control returns:
6931 * * width = text length
6932 * * .left = position.x - width/2
6933 * * .right = .left + width
6934 * * height = ntmH * linecount + 2
6935 * * .top = position.y + iconSize.cy + 6
6936 * * .bottom = .top + height
6937 * * separation between items .y = itemSpacing.cy - height
6938 * * .x = itemSpacing.cx - width
6939 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
6940 * rectangles, but excludes columns in report view.
6947 * Note that the bounding rectangle of the label in the LVS_ICON view depends
6948 * upon whether the window has the focus currently and on whether the item
6949 * is the one with the focus. Ensure that the control's record of which
6950 * item has the focus agrees with the items' records.
6952 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
6954 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6955 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
6956 POINT Position
, Origin
;
6960 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
6962 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
6964 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6965 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
6967 /* Be smart and try to figure out the minimum we have to do */
6968 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
6969 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
6970 if (infoPtr
->uView
== LV_VIEW_ICON
&& lprc
->left
!= LVIR_ICON
&&
6971 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
6972 oversizedBox
= TRUE
;
6974 /* get what we need from the item before hand, so we make
6975 * only one request. This can speed up things, if data
6976 * is stored on the app side */
6978 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
6979 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
6980 lvItem
.iItem
= nItem
;
6981 lvItem
.iSubItem
= 0;
6982 lvItem
.pszText
= szDispText
;
6983 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6984 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
6985 /* we got the state already up, simulate it here, to avoid a reget */
6986 if (infoPtr
->uView
== LV_VIEW_ICON
&& (lprc
->left
!= LVIR_ICON
))
6988 lvItem
.mask
|= LVIF_STATE
;
6989 lvItem
.stateMask
= LVIS_FOCUSED
;
6990 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
6993 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
6994 lprc
->left
= LVIR_BOUNDS
;
7000 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
7004 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, NULL
, lprc
);
7008 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
7011 case LVIR_SELECTBOUNDS
:
7012 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, lprc
, NULL
, NULL
, NULL
);
7016 WARN("Unknown value: %d\n", lprc
->left
);
7020 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7022 if (mode
!= LVIR_BOUNDS
)
7023 OffsetRect(lprc
, Origin
.x
+ LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
,
7024 Position
.y
+ Origin
.y
);
7026 OffsetRect(lprc
, Origin
.x
, Position
.y
+ Origin
.y
);
7029 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
7031 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc
));
7038 * Retrieves the spacing between listview control items.
7041 * [I] infoPtr : valid pointer to the listview structure
7042 * [IO] lprc : rectangle to receive the output
7043 * on input, lprc->top = nSubItem
7044 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
7046 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
7047 * not only those of the first column.
7048 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
7054 static BOOL
LISTVIEW_GetSubItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
7056 POINT Position
, Origin
;
7060 if (!lprc
) return FALSE
;
7062 nColumn
= lprc
->top
;
7064 TRACE("(nItem=%d, nSubItem=%d, type=%d)\n", nItem
, lprc
->top
, lprc
->left
);
7065 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
7067 return LISTVIEW_GetItemRect(infoPtr
, nItem
, lprc
);
7069 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) return FALSE
;
7071 /* special case for header items */
7074 if (lprc
->left
!= LVIR_BOUNDS
)
7076 FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc
->left
);
7080 if (infoPtr
->hwndHeader
)
7081 return SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMRECT
, lprc
->top
, (LPARAM
)lprc
);
7084 memset(lprc
, 0, sizeof(RECT
));
7089 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &Position
)) return FALSE
;
7090 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7092 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
7095 lvItem
.iItem
= nItem
;
7096 lvItem
.iSubItem
= nColumn
;
7101 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
7106 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
7110 ERR("Unknown bounds=%d\n", lprc
->left
);
7114 OffsetRect(lprc
, Origin
.x
, Position
.y
);
7115 TRACE("return rect %s\n", wine_dbgstr_rect(lprc
));
7122 * Retrieves the spacing between listview control items.
7125 * [I] infoPtr : valid pointer to the listview structure
7126 * [I] bSmall : flag for small or large icon
7129 * Horizontal + vertical spacing
7131 static LONG
LISTVIEW_GetItemSpacing(const LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
7137 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
7141 if (infoPtr
->uView
== LV_VIEW_ICON
)
7142 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
7144 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
7151 * Retrieves the state of a listview control item.
7154 * [I] infoPtr : valid pointer to the listview structure
7155 * [I] nItem : item index
7156 * [I] uMask : state mask
7159 * State specified by the mask.
7161 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
7165 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
7167 lvItem
.iItem
= nItem
;
7168 lvItem
.iSubItem
= 0;
7169 lvItem
.mask
= LVIF_STATE
;
7170 lvItem
.stateMask
= uMask
;
7171 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
7173 return lvItem
.state
& uMask
;
7178 * Retrieves the text of a listview control item or subitem.
7181 * [I] hwnd : window handle
7182 * [I] nItem : item index
7183 * [IO] lpLVItem : item information
7184 * [I] isW : TRUE if lpLVItem is Unicode
7187 * SUCCESS : string length
7190 static INT
LISTVIEW_GetItemTextT(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
7192 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
7194 lpLVItem
->mask
= LVIF_TEXT
;
7195 lpLVItem
->iItem
= nItem
;
7196 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
7198 return textlenT(lpLVItem
->pszText
, isW
);
7203 * Searches for an item based on properties + relationships.
7206 * [I] infoPtr : valid pointer to the listview structure
7207 * [I] nItem : item index
7208 * [I] uFlags : relationship flag
7211 * SUCCESS : item index
7214 static INT
LISTVIEW_GetNextItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
7217 LVFINDINFOW lvFindInfo
;
7218 INT nCountPerColumn
;
7222 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
7223 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
7225 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
7227 if (uFlags
& LVNI_CUT
)
7230 if (uFlags
& LVNI_DROPHILITED
)
7231 uMask
|= LVIS_DROPHILITED
;
7233 if (uFlags
& LVNI_FOCUSED
)
7234 uMask
|= LVIS_FOCUSED
;
7236 if (uFlags
& LVNI_SELECTED
)
7237 uMask
|= LVIS_SELECTED
;
7239 /* if we're asked for the focused item, that's only one,
7240 * so it's worth optimizing */
7241 if (uFlags
& LVNI_FOCUSED
)
7243 if ((LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) != uMask
) return -1;
7244 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
7247 if (uFlags
& LVNI_ABOVE
)
7249 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
7254 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7260 /* Special case for autoarrange - move 'til the top of a list */
7261 if (is_autoarrange(infoPtr
))
7263 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7264 while (nItem
- nCountPerRow
>= 0)
7266 nItem
-= nCountPerRow
;
7267 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7272 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7273 lvFindInfo
.vkDirection
= VK_UP
;
7274 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7275 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7277 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7282 else if (uFlags
& LVNI_BELOW
)
7284 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
7286 while (nItem
< infoPtr
->nItemCount
)
7289 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7295 /* Special case for autoarrange - move 'til the bottom of a list */
7296 if (is_autoarrange(infoPtr
))
7298 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7299 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
7301 nItem
+= nCountPerRow
;
7302 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7307 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7308 lvFindInfo
.vkDirection
= VK_DOWN
;
7309 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7310 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7312 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7317 else if (uFlags
& LVNI_TOLEFT
)
7319 if (infoPtr
->uView
== LV_VIEW_LIST
)
7321 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
7322 while (nItem
- nCountPerColumn
>= 0)
7324 nItem
-= nCountPerColumn
;
7325 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7329 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
7331 /* Special case for autoarrange - move 'til the beginning of a row */
7332 if (is_autoarrange(infoPtr
))
7334 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7335 while (nItem
% nCountPerRow
> 0)
7338 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7343 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7344 lvFindInfo
.vkDirection
= VK_LEFT
;
7345 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7346 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7348 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7353 else if (uFlags
& LVNI_TORIGHT
)
7355 if (infoPtr
->uView
== LV_VIEW_LIST
)
7357 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
7358 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
7360 nItem
+= nCountPerColumn
;
7361 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7365 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
7367 /* Special case for autoarrange - move 'til the end of a row */
7368 if (is_autoarrange(infoPtr
))
7370 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7371 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
7374 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7379 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7380 lvFindInfo
.vkDirection
= VK_RIGHT
;
7381 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7382 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7384 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7393 /* search by index */
7394 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
7396 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
7404 /* LISTVIEW_GetNumberOfWorkAreas */
7408 * Retrieves the origin coordinates when in icon or small icon display mode.
7411 * [I] infoPtr : valid pointer to the listview structure
7412 * [O] lpptOrigin : coordinate information
7417 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
7419 INT nHorzPos
= 0, nVertPos
= 0;
7420 SCROLLINFO scrollInfo
;
7422 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7423 scrollInfo
.fMask
= SIF_POS
;
7425 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
7426 nHorzPos
= scrollInfo
.nPos
;
7427 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
7428 nVertPos
= scrollInfo
.nPos
;
7430 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
7432 lpptOrigin
->x
= infoPtr
->rcList
.left
;
7433 lpptOrigin
->y
= infoPtr
->rcList
.top
;
7434 if (infoPtr
->uView
== LV_VIEW_LIST
)
7435 nHorzPos
*= infoPtr
->nItemWidth
;
7436 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7437 nVertPos
*= infoPtr
->nItemHeight
;
7439 lpptOrigin
->x
-= nHorzPos
;
7440 lpptOrigin
->y
-= nVertPos
;
7442 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin
));
7447 * Retrieves the width of a string.
7450 * [I] hwnd : window handle
7451 * [I] lpszText : text string to process
7452 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
7455 * SUCCESS : string width (in pixels)
7458 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
7463 if (is_text(lpszText
))
7465 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
7466 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
7467 HFONT hOldFont
= SelectObject(hdc
, hFont
);
7470 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
7472 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
7473 SelectObject(hdc
, hOldFont
);
7474 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7476 return stringSize
.cx
;
7481 * Determines which listview item is located at the specified position.
7484 * [I] infoPtr : valid pointer to the listview structure
7485 * [IO] lpht : hit test information
7486 * [I] subitem : fill out iSubItem.
7487 * [I] select : return the index only if the hit selects the item
7490 * (mm 20001022): We must not allow iSubItem to be touched, for
7491 * an app might pass only a structure with space up to iItem!
7492 * (MS Office 97 does that for instance in the file open dialog)
7495 * SUCCESS : item index
7498 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
7500 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
7501 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
7502 POINT Origin
, Position
, opt
;
7507 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht
->pt
), subitem
, select
);
7511 if (subitem
) lpht
->iSubItem
= 0;
7513 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7515 /* set whole list relation flags */
7516 if (subitem
&& infoPtr
->uView
== LV_VIEW_DETAILS
)
7518 /* LVM_SUBITEMHITTEST checks left bound of possible client area */
7519 if (infoPtr
->rcList
.left
> lpht
->pt
.x
&& Origin
.x
< lpht
->pt
.x
)
7520 lpht
->flags
|= LVHT_TOLEFT
;
7522 if (lpht
->pt
.y
< infoPtr
->rcList
.top
&& lpht
->pt
.y
>= 0)
7523 opt
.y
= lpht
->pt
.y
+ infoPtr
->rcList
.top
;
7527 if (infoPtr
->rcList
.bottom
< opt
.y
)
7528 lpht
->flags
|= LVHT_BELOW
;
7532 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
7533 lpht
->flags
|= LVHT_TOLEFT
;
7534 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
7535 lpht
->flags
|= LVHT_TORIGHT
;
7537 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
7538 lpht
->flags
|= LVHT_ABOVE
;
7539 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
7540 lpht
->flags
|= LVHT_BELOW
;
7543 /* even if item is invalid try to find subitem */
7544 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& subitem
)
7549 opt
.x
= lpht
->pt
.x
- Origin
.x
;
7551 lpht
->iSubItem
= -1;
7552 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
7554 pRect
= &LISTVIEW_GetColumnInfo(infoPtr
, j
)->rcHeader
;
7556 if ((opt
.x
>= pRect
->left
) && (opt
.x
< pRect
->right
))
7562 TRACE("lpht->iSubItem=%d\n", lpht
->iSubItem
);
7564 /* if we're outside horizontal columns bounds there's nothing to test further */
7565 if (lpht
->iSubItem
== -1)
7568 lpht
->flags
= LVHT_NOWHERE
;
7573 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
7574 if (lpht
->flags
) return -1;
7576 lpht
->flags
|= LVHT_NOWHERE
;
7578 /* first deal with the large items */
7579 rcSearch
.left
= lpht
->pt
.x
;
7580 rcSearch
.top
= lpht
->pt
.y
;
7581 rcSearch
.right
= rcSearch
.left
+ 1;
7582 rcSearch
.bottom
= rcSearch
.top
+ 1;
7584 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
7585 iterator_next(&i
); /* go to first item in the sequence */
7587 iterator_destroy(&i
);
7589 TRACE("lpht->iItem=%d\n", iItem
);
7590 if (iItem
== -1) return -1;
7592 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
7593 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
7594 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
7595 if (infoPtr
->uView
== LV_VIEW_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
7596 lvItem
.iItem
= iItem
;
7597 lvItem
.iSubItem
= subitem
? lpht
->iSubItem
: 0;
7598 lvItem
.pszText
= szDispText
;
7599 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7600 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
7601 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
7603 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
7604 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
7605 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
7607 if (lpht
->pt
.y
< infoPtr
->rcList
.top
&& lpht
->pt
.y
>= 0)
7608 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
+ infoPtr
->rcList
.top
;
7610 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
7612 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7615 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
7616 opt
.x
= lpht
->pt
.x
- Origin
.x
;
7620 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
7621 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
7623 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds
));
7624 if (!PtInRect(&rcBounds
, opt
)) return -1;
7626 if (PtInRect(&rcIcon
, opt
))
7627 lpht
->flags
|= LVHT_ONITEMICON
;
7628 else if (PtInRect(&rcLabel
, opt
))
7629 lpht
->flags
|= LVHT_ONITEMLABEL
;
7630 else if (infoPtr
->himlState
&& PtInRect(&rcState
, opt
))
7631 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
7632 /* special case for LVS_EX_FULLROWSELECT */
7633 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
&&
7634 !(lpht
->flags
& LVHT_ONITEM
))
7636 lpht
->flags
= LVHT_ONITEM
| LVHT_ABOVE
;
7638 if (lpht
->flags
& LVHT_ONITEM
)
7639 lpht
->flags
&= ~LVHT_NOWHERE
;
7640 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
7642 if (select
&& !(infoPtr
->uView
== LV_VIEW_DETAILS
&&
7643 ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) ||
7644 (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))))
7646 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7648 /* get main item bounds */
7649 lvItem
.iSubItem
= 0;
7650 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
7651 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
7652 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
7654 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
7656 return lpht
->iItem
= iItem
;
7661 * Inserts a new item in the listview control.
7664 * [I] infoPtr : valid pointer to the listview structure
7665 * [I] lpLVItem : item information
7666 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
7669 * SUCCESS : new item index
7672 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
7679 BOOL is_sorted
, has_changed
;
7681 HWND hwndSelf
= infoPtr
->hwndSelf
;
7683 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
7685 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
7687 /* make sure it's an item, and not a subitem; cannot insert a subitem */
7688 if (!lpLVItem
|| lpLVItem
->iSubItem
) return -1;
7690 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
7692 if (!(lpItem
= Alloc(sizeof(ITEM_INFO
)))) return -1;
7694 /* insert item in listview control data structure */
7695 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
7696 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
7698 /* link with id struct */
7699 if (!(lpID
= Alloc(sizeof(ITEM_ID
)))) goto fail
;
7701 lpID
->item
= hdpaSubItems
;
7702 lpID
->id
= get_next_itemid(infoPtr
);
7703 if ( DPA_InsertPtr(infoPtr
->hdpaItemIds
, infoPtr
->nItemCount
, lpID
) == -1) goto fail
;
7705 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
7706 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
7708 if (lpLVItem
->iItem
< 0 && !is_sorted
) return -1;
7710 /* calculate new item index */
7717 while (i
< infoPtr
->nItemCount
)
7719 hItem
= DPA_GetPtr( infoPtr
->hdpaItems
, i
);
7720 item_s
= DPA_GetPtr(hItem
, 0);
7722 cmpv
= textcmpWT(item_s
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
7723 if (infoPtr
->dwStyle
& LVS_SORTDESCENDING
) cmpv
*= -1;
7725 if (cmpv
>= 0) break;
7731 nItem
= min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
7733 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
7734 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
7735 if (nItem
== -1) goto fail
;
7736 infoPtr
->nItemCount
++;
7738 /* shift indices first so they don't get tangled */
7739 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
7741 /* set the item attributes */
7742 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
7744 /* full size structure expected - _WIN32IE >= 0x560 */
7747 else if (lpLVItem
->mask
& LVIF_INDENT
)
7749 /* indent member expected - _WIN32IE >= 0x300 */
7750 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
7754 /* minimal structure expected */
7755 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
7758 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
7760 item
.mask
|= LVIF_STATE
;
7761 item
.stateMask
|= LVIS_STATEIMAGEMASK
;
7762 item
.state
&= ~LVIS_STATEIMAGEMASK
;
7763 item
.state
|= INDEXTOSTATEIMAGEMASK(1);
7765 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
7767 /* make room for the position, if we are in the right mode */
7768 if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
7770 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
7772 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
7774 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
7779 /* send LVN_INSERTITEM notification */
7780 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
7782 nmlv
.lParam
= lpItem
->lParam
;
7783 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
7784 if (!IsWindow(hwndSelf
))
7787 /* align items (set position of each item) */
7788 if (infoPtr
->uView
== LV_VIEW_SMALLICON
|| infoPtr
->uView
== LV_VIEW_ICON
)
7792 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
7793 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
7795 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
7797 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
7800 /* now is the invalidation fun */
7801 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
7805 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
7806 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
7807 infoPtr
->nItemCount
--;
7809 DPA_DeletePtr(hdpaSubItems
, 0);
7810 DPA_Destroy (hdpaSubItems
);
7817 * Checks item visibility.
7820 * [I] infoPtr : valid pointer to the listview structure
7821 * [I] nFirst : item index to check for
7824 * Item visible : TRUE
7825 * Item invisible or failure : FALSE
7827 static BOOL
LISTVIEW_IsItemVisible(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
7829 POINT Origin
, Position
;
7834 TRACE("nItem=%d\n", nItem
);
7836 if (nItem
< 0 || nItem
>= DPA_GetPtrCount(infoPtr
->hdpaItems
)) return FALSE
;
7838 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7839 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
7840 rcItem
.left
= Position
.x
+ Origin
.x
;
7841 rcItem
.top
= Position
.y
+ Origin
.y
;
7842 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
7843 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
7845 hdc
= GetDC(infoPtr
->hwndSelf
);
7846 if (!hdc
) return FALSE
;
7847 ret
= RectVisible(hdc
, &rcItem
);
7848 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7855 * Redraws a range of items.
7858 * [I] infoPtr : valid pointer to the listview structure
7859 * [I] nFirst : first item
7860 * [I] nLast : last item
7866 static BOOL
LISTVIEW_RedrawItems(const LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
7870 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
7871 max(nFirst
, nLast
) >= infoPtr
->nItemCount
)
7874 for (i
= nFirst
; i
<= nLast
; i
++)
7875 LISTVIEW_InvalidateItem(infoPtr
, i
);
7882 * Scroll the content of a listview.
7885 * [I] infoPtr : valid pointer to the listview structure
7886 * [I] dx : horizontal scroll amount in pixels
7887 * [I] dy : vertical scroll amount in pixels
7894 * If the control is in report view (LV_VIEW_DETAILS) the control can
7895 * be scrolled only in line increments. "dy" will be rounded to the
7896 * nearest number of pixels that are a whole line. Ex: if line height
7897 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
7898 * is passed, then the scroll will be 0. (per MSDN 7/2002)
7900 * For: (per experimentation with native control and CSpy ListView)
7901 * LV_VIEW_ICON scrolling in any direction is allowed
7902 * LV_VIEW_SMALLICON scrolling in any direction is allowed
7903 * LV_VIEW_LIST dx=1 = 1 column (horizontal only)
7904 * but will only scroll 1 column per message
7905 * no matter what the value.
7906 * dy must be 0 or FALSE returned.
7907 * LV_VIEW_DETAILS dx=1 = 1 pixel
7911 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
7913 switch(infoPtr
->uView
) {
7914 case LV_VIEW_DETAILS
:
7915 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
7916 dy
/= infoPtr
->nItemHeight
;
7919 if (dy
!= 0) return FALSE
;
7925 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
);
7926 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
);
7933 * Sets the background color.
7936 * [I] infoPtr : valid pointer to the listview structure
7937 * [I] color : background color
7943 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF color
)
7945 TRACE("(color=%x)\n", color
);
7947 infoPtr
->bDefaultBkColor
= FALSE
;
7948 if(infoPtr
->clrBk
!= color
) {
7949 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
7950 infoPtr
->clrBk
= color
;
7951 if (color
== CLR_NONE
)
7952 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
7955 infoPtr
->hBkBrush
= CreateSolidBrush(color
);
7956 infoPtr
->dwLvExStyle
&= ~LVS_EX_TRANSPARENTBKGND
;
7963 /* LISTVIEW_SetBkImage */
7965 /*** Helper for {Insert,Set}ColumnT *only* */
7966 static void column_fill_hditem(const LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
,
7967 const LVCOLUMNW
*lpColumn
, BOOL isW
)
7969 if (lpColumn
->mask
& LVCF_FMT
)
7971 /* format member is valid */
7972 lphdi
->mask
|= HDI_FORMAT
;
7974 /* set text alignment (leftmost column must be left-aligned) */
7975 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
7976 lphdi
->fmt
|= HDF_LEFT
;
7977 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
7978 lphdi
->fmt
|= HDF_RIGHT
;
7979 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
7980 lphdi
->fmt
|= HDF_CENTER
;
7982 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
7983 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
7985 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
7987 lphdi
->fmt
|= HDF_IMAGE
;
7988 lphdi
->iImage
= I_IMAGECALLBACK
;
7991 if (lpColumn
->fmt
& LVCFMT_FIXED_WIDTH
)
7992 lphdi
->fmt
|= HDF_FIXEDWIDTH
;
7995 if (lpColumn
->mask
& LVCF_WIDTH
)
7997 lphdi
->mask
|= HDI_WIDTH
;
7998 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
8000 /* make it fill the remainder of the controls width */
8004 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
8006 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
8007 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
8010 /* retrieve the layout of the header */
8011 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
8012 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, wine_dbgstr_rect(&rcHeader
));
8014 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
8017 lphdi
->cxy
= lpColumn
->cx
;
8020 if (lpColumn
->mask
& LVCF_TEXT
)
8022 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
8023 lphdi
->fmt
|= HDF_STRING
;
8024 lphdi
->pszText
= lpColumn
->pszText
;
8025 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
8028 if (lpColumn
->mask
& LVCF_IMAGE
)
8030 lphdi
->mask
|= HDI_IMAGE
;
8031 lphdi
->iImage
= lpColumn
->iImage
;
8034 if (lpColumn
->mask
& LVCF_ORDER
)
8036 lphdi
->mask
|= HDI_ORDER
;
8037 lphdi
->iOrder
= lpColumn
->iOrder
;
8044 * Inserts a new column.
8047 * [I] infoPtr : valid pointer to the listview structure
8048 * [I] nColumn : column index
8049 * [I] lpColumn : column information
8050 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
8053 * SUCCESS : new column index
8056 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
8057 const LVCOLUMNW
*lpColumn
, BOOL isW
)
8059 COLUMN_INFO
*lpColumnInfo
;
8063 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
8065 if (!lpColumn
|| nColumn
< 0) return -1;
8066 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
8068 ZeroMemory(&hdi
, sizeof(HDITEMW
));
8069 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
8072 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
8073 * (can be seen in SPY) otherwise column never gets added.
8075 if (!(lpColumn
->mask
& LVCF_WIDTH
)) {
8076 hdi
.mask
|= HDI_WIDTH
;
8081 * when the iSubItem is available Windows copies it to the header lParam. It seems
8082 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
8084 if (lpColumn
->mask
& LVCF_SUBITEM
)
8086 hdi
.mask
|= HDI_LPARAM
;
8087 hdi
.lParam
= lpColumn
->iSubItem
;
8090 /* create header if not present */
8091 LISTVIEW_CreateHeader(infoPtr
);
8092 if (!(LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
) &&
8093 (infoPtr
->uView
== LV_VIEW_DETAILS
) && (WS_VISIBLE
& infoPtr
->dwStyle
))
8095 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
8098 /* insert item in header control */
8099 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
8100 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
8101 nColumn
, (LPARAM
)&hdi
);
8102 if (nNewColumn
== -1) return -1;
8103 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
8105 /* create our own column info */
8106 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
8107 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
8109 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
8110 if (lpColumn
->mask
& LVCF_MINWIDTH
) lpColumnInfo
->cxMin
= lpColumn
->cxMin
;
8111 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMRECT
, nNewColumn
, (LPARAM
)&lpColumnInfo
->rcHeader
))
8114 /* now we have to actually adjust the data */
8115 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
8117 SUBITEM_INFO
*lpSubItem
;
8123 item
.iSubItem
= nNewColumn
;
8124 item
.mask
= LVIF_TEXT
| LVIF_IMAGE
;
8125 item
.iImage
= I_IMAGECALLBACK
;
8126 item
.pszText
= LPSTR_TEXTCALLBACKW
;
8128 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
8130 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
8131 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
8133 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
8134 if (lpSubItem
->iSubItem
>= nNewColumn
)
8135 lpSubItem
->iSubItem
++;
8138 /* add new subitem for each item */
8140 set_sub_item(infoPtr
, &item
, isW
, &changed
);
8144 /* make space for the new column */
8145 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
8146 LISTVIEW_UpdateItemSize(infoPtr
);
8151 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
8154 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
8162 * Sets the attributes of a header item.
8165 * [I] infoPtr : valid pointer to the listview structure
8166 * [I] nColumn : column index
8167 * [I] lpColumn : column attributes
8168 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
8174 static BOOL
LISTVIEW_SetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
,
8175 const LVCOLUMNW
*lpColumn
, BOOL isW
)
8177 HDITEMW hdi
, hdiget
;
8180 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
8182 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
8184 ZeroMemory(&hdi
, sizeof(HDITEMW
));
8185 if (lpColumn
->mask
& LVCF_FMT
)
8187 hdi
.mask
|= HDI_FORMAT
;
8188 hdiget
.mask
= HDI_FORMAT
;
8189 if (SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdiget
))
8190 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
8192 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
8194 /* set header item attributes */
8195 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, nColumn
, (LPARAM
)&hdi
);
8196 if (!bResult
) return FALSE
;
8198 if (lpColumn
->mask
& LVCF_FMT
)
8200 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
8201 INT oldFmt
= lpColumnInfo
->fmt
;
8203 lpColumnInfo
->fmt
= lpColumn
->fmt
;
8204 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
8206 if (infoPtr
->uView
== LV_VIEW_DETAILS
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
8210 if (lpColumn
->mask
& LVCF_MINWIDTH
)
8211 LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->cxMin
= lpColumn
->cxMin
;
8218 * Sets the column order array
8221 * [I] infoPtr : valid pointer to the listview structure
8222 * [I] iCount : number of elements in column order array
8223 * [I] lpiArray : pointer to column order array
8229 static BOOL
LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
8231 TRACE("iCount %d lpiArray %p\n", iCount
, lpiArray
);
8233 if (!lpiArray
|| !IsWindow(infoPtr
->hwndHeader
)) return FALSE
;
8235 infoPtr
->colRectsDirty
= TRUE
;
8237 return SendMessageW(infoPtr
->hwndHeader
, HDM_SETORDERARRAY
, iCount
, (LPARAM
)lpiArray
);
8242 * Sets the width of a column
8245 * [I] infoPtr : valid pointer to the listview structure
8246 * [I] nColumn : column index
8247 * [I] cx : column width
8253 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
8255 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
8259 TRACE("(nColumn=%d, cx=%d\n", nColumn
, cx
);
8261 /* set column width only if in report or list mode */
8262 if (infoPtr
->uView
!= LV_VIEW_DETAILS
&& infoPtr
->uView
!= LV_VIEW_LIST
) return FALSE
;
8264 /* take care of invalid cx values */
8265 if(infoPtr
->uView
== LV_VIEW_DETAILS
&& cx
< -2) cx
= LVSCW_AUTOSIZE
;
8266 else if (infoPtr
->uView
== LV_VIEW_LIST
&& cx
< 1) return FALSE
;
8268 /* resize all columns if in LV_VIEW_LIST mode */
8269 if(infoPtr
->uView
== LV_VIEW_LIST
)
8271 infoPtr
->nItemWidth
= cx
;
8272 LISTVIEW_InvalidateList(infoPtr
);
8276 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
8278 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
8283 lvItem
.mask
= LVIF_TEXT
;
8285 lvItem
.iSubItem
= nColumn
;
8286 lvItem
.pszText
= szDispText
;
8287 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
8288 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
8290 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
8291 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
8292 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
8294 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
8295 max_cx
+= infoPtr
->iconSize
.cx
;
8296 max_cx
+= TRAILING_LABEL_PADDING
;
8299 /* autosize based on listview items width */
8300 if(cx
== LVSCW_AUTOSIZE
)
8302 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
8304 /* if iCol is the last column make it fill the remainder of the controls width */
8305 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
8310 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
8311 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
8313 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
8317 /* Despite what the MS docs say, if this is not the last
8318 column, then MS resizes the column to the width of the
8319 largest text string in the column, including headers
8320 and items. This is different from LVSCW_AUTOSIZE in that
8321 LVSCW_AUTOSIZE ignores the header string length. */
8324 /* retrieve header text */
8325 hdi
.mask
= HDI_TEXT
;
8326 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
8327 hdi
.pszText
= szDispText
;
8328 if (SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdi
))
8330 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
8331 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
8334 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
8335 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
8336 /* FIXME: Take into account the header image, if one is present */
8337 SelectObject(hdc
, old_font
);
8338 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
8340 cx
= max (cx
, max_cx
);
8344 if (cx
< 0) return FALSE
;
8346 /* call header to update the column change */
8347 hdi
.mask
= HDI_WIDTH
;
8348 hdi
.cxy
= max(cx
, LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->cxMin
);
8349 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
8350 return SendMessageW(infoPtr
->hwndHeader
, HDM_SETITEMW
, nColumn
, (LPARAM
)&hdi
);
8354 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
8357 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO
*infoPtr
)
8360 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
8362 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
8363 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
8366 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
8367 ILC_COLOR
| ILC_MASK
, 2, 2);
8368 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
8369 hdc
= CreateCompatibleDC(hdc_wnd
);
8370 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
8371 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
8372 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
8374 rc
.left
= rc
.top
= 0;
8375 rc
.right
= GetSystemMetrics(SM_CXSMICON
);
8376 rc
.bottom
= GetSystemMetrics(SM_CYSMICON
);
8378 hbm_orig
= SelectObject(hdc
, hbm_mask
);
8379 FillRect(hdc
, &rc
, hbr_white
);
8380 InflateRect(&rc
, -2, -2);
8381 FillRect(hdc
, &rc
, hbr_black
);
8383 SelectObject(hdc
, hbm_im
);
8384 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
8385 SelectObject(hdc
, hbm_orig
);
8386 ImageList_Add(himl
, hbm_im
, hbm_mask
);
8388 SelectObject(hdc
, hbm_im
);
8389 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
8390 SelectObject(hdc
, hbm_orig
);
8391 ImageList_Add(himl
, hbm_im
, hbm_mask
);
8393 DeleteObject(hbm_mask
);
8394 DeleteObject(hbm_im
);
8402 * Sets the extended listview style.
8405 * [I] infoPtr : valid pointer to the listview structure
8407 * [I] dwStyle : style
8410 * SUCCESS : previous style
8413 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD mask
, DWORD ex_style
)
8415 DWORD old_ex_style
= infoPtr
->dwLvExStyle
;
8417 TRACE("mask=0x%08x, ex_style=0x%08x\n", mask
, ex_style
);
8421 infoPtr
->dwLvExStyle
= (old_ex_style
& ~mask
) | (ex_style
& mask
);
8423 infoPtr
->dwLvExStyle
= ex_style
;
8425 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_CHECKBOXES
)
8427 HIMAGELIST himl
= 0;
8428 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
8431 item
.mask
= LVIF_STATE
;
8432 item
.stateMask
= LVIS_STATEIMAGEMASK
;
8433 item
.state
= INDEXTOSTATEIMAGEMASK(1);
8434 LISTVIEW_SetItemState(infoPtr
, -1, &item
);
8436 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
8437 if(!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
8438 ImageList_Destroy(infoPtr
->himlState
);
8440 himl
= LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
8441 /* checkbox list replaces previous custom list or... */
8442 if(((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) &&
8443 !(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
)) ||
8444 /* ...previous was checkbox list */
8445 (old_ex_style
& LVS_EX_CHECKBOXES
))
8446 ImageList_Destroy(himl
);
8449 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_HEADERDRAGDROP
)
8453 /* if not already created */
8454 LISTVIEW_CreateHeader(infoPtr
);
8456 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
8457 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
)
8458 style
|= HDS_DRAGDROP
;
8460 style
&= ~HDS_DRAGDROP
;
8461 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
);
8464 /* GRIDLINES adds decoration at top so changes sizes */
8465 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_GRIDLINES
)
8467 LISTVIEW_CreateHeader(infoPtr
);
8468 LISTVIEW_UpdateSize(infoPtr
);
8471 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_FULLROWSELECT
)
8473 LISTVIEW_CreateHeader(infoPtr
);
8476 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_TRANSPARENTBKGND
)
8478 if (infoPtr
->dwLvExStyle
& LVS_EX_TRANSPARENTBKGND
)
8479 LISTVIEW_SetBkColor(infoPtr
, CLR_NONE
);
8482 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_HEADERINALLVIEWS
)
8484 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERINALLVIEWS
)
8485 LISTVIEW_CreateHeader(infoPtr
);
8487 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
8488 LISTVIEW_UpdateSize(infoPtr
);
8489 LISTVIEW_UpdateScroll(infoPtr
);
8492 LISTVIEW_InvalidateList(infoPtr
);
8493 return old_ex_style
;
8498 * Sets the new hot cursor used during hot tracking and hover selection.
8501 * [I] infoPtr : valid pointer to the listview structure
8502 * [I] hCursor : the new hot cursor handle
8505 * Returns the previous hot cursor
8507 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
8509 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
8511 infoPtr
->hHotCursor
= hCursor
;
8519 * Sets the hot item index.
8522 * [I] infoPtr : valid pointer to the listview structure
8523 * [I] iIndex : index
8526 * SUCCESS : previous hot item index
8527 * FAILURE : -1 (no hot item)
8529 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
8531 INT iOldIndex
= infoPtr
->nHotItem
;
8533 infoPtr
->nHotItem
= iIndex
;
8541 * Sets the amount of time the cursor must hover over an item before it is selected.
8544 * [I] infoPtr : valid pointer to the listview structure
8545 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
8548 * Returns the previous hover time
8550 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
8552 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
8554 infoPtr
->dwHoverTime
= dwHoverTime
;
8556 return oldHoverTime
;
8561 * Sets spacing for icons of LVS_ICON style.
8564 * [I] infoPtr : valid pointer to the listview structure
8565 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
8566 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
8569 * MAKELONG(oldcx, oldcy)
8571 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
8573 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
8575 TRACE("requested=(%d,%d)\n", cx
, cy
);
8577 /* this is supported only for LVS_ICON style */
8578 if (infoPtr
->uView
!= LV_VIEW_ICON
) return oldspacing
;
8580 /* set to defaults, if instructed to */
8581 if (cx
== -1) cx
= GetSystemMetrics(SM_CXICONSPACING
);
8582 if (cy
== -1) cy
= GetSystemMetrics(SM_CYICONSPACING
);
8584 /* if 0 then compute width
8585 * FIXME: computed cx and cy is not matching native behaviour */
8587 cx
= GetSystemMetrics(SM_CXICONSPACING
);
8588 if (infoPtr
->iconSize
.cx
+ ICON_LR_PADDING
> cx
)
8589 cx
= infoPtr
->iconSize
.cx
+ ICON_LR_PADDING
;
8592 /* if 0 then compute height */
8594 cy
= infoPtr
->iconSize
.cy
+ 2 * infoPtr
->ntmHeight
+
8595 ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_PADDING
;
8598 infoPtr
->iconSpacing
.cx
= cx
;
8599 infoPtr
->iconSpacing
.cy
= cy
;
8601 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
8602 LOWORD(oldspacing
), HIWORD(oldspacing
), cx
, cy
,
8603 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
8604 infoPtr
->ntmHeight
);
8606 /* these depend on the iconSpacing */
8607 LISTVIEW_UpdateItemSize(infoPtr
);
8612 static inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL small
)
8616 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
8623 size
->cx
= GetSystemMetrics(small
? SM_CXSMICON
: SM_CXICON
);
8624 size
->cy
= GetSystemMetrics(small
? SM_CYSMICON
: SM_CYICON
);
8633 * [I] infoPtr : valid pointer to the listview structure
8634 * [I] nType : image list type
8635 * [I] himl : image list handle
8638 * SUCCESS : old image list
8641 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
8643 INT oldHeight
= infoPtr
->nItemHeight
;
8644 HIMAGELIST himlOld
= 0;
8646 TRACE("(nType=%d, himl=%p\n", nType
, himl
);
8651 himlOld
= infoPtr
->himlNormal
;
8652 infoPtr
->himlNormal
= himl
;
8653 if (infoPtr
->uView
== LV_VIEW_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
8654 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
8658 himlOld
= infoPtr
->himlSmall
;
8659 infoPtr
->himlSmall
= himl
;
8660 if (infoPtr
->uView
!= LV_VIEW_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
8664 himlOld
= infoPtr
->himlState
;
8665 infoPtr
->himlState
= himl
;
8666 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
8667 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
8671 ERR("Unknown icon type=%d\n", nType
);
8675 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
8676 if (infoPtr
->nItemHeight
!= oldHeight
)
8677 LISTVIEW_UpdateScroll(infoPtr
);
8684 * Preallocates memory (does *not* set the actual count of items !)
8687 * [I] infoPtr : valid pointer to the listview structure
8688 * [I] nItems : item count (projected number of items to allocate)
8689 * [I] dwFlags : update flags
8695 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
8697 TRACE("(nItems=%d, dwFlags=%x)\n", nItems
, dwFlags
);
8699 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
8701 INT nOldCount
= infoPtr
->nItemCount
;
8703 if (nItems
< nOldCount
)
8705 RANGE range
= { nItems
, nOldCount
};
8706 ranges_del(infoPtr
->selectionRanges
, range
);
8707 if (infoPtr
->nFocusedItem
>= nItems
)
8709 LISTVIEW_SetItemFocus(infoPtr
, -1);
8710 SetRectEmpty(&infoPtr
->rcFocus
);
8714 infoPtr
->nItemCount
= nItems
;
8715 LISTVIEW_UpdateScroll(infoPtr
);
8717 /* the flags are valid only in ownerdata report and list modes */
8718 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
) dwFlags
= 0;
8720 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
8721 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
8723 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
8724 LISTVIEW_InvalidateList(infoPtr
);
8731 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
8732 nFrom
= min(nOldCount
, nItems
);
8733 nTo
= max(nOldCount
, nItems
);
8735 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
8738 rcErase
.top
= nFrom
* infoPtr
->nItemHeight
;
8739 rcErase
.right
= infoPtr
->nItemWidth
;
8740 rcErase
.bottom
= nTo
* infoPtr
->nItemHeight
;
8741 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
8742 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
8743 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
8745 else /* LV_VIEW_LIST */
8747 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
8749 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
8750 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
8751 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
8752 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
8753 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
8754 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
8755 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
8757 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
8759 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
8760 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
8761 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
8762 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
8763 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
8769 /* According to MSDN for non-LVS_OWNERDATA this is just
8770 * a performance issue. The control allocates its internal
8771 * data structures for the number of items specified. It
8772 * cuts down on the number of memory allocations. Therefore
8773 * we will just issue a WARN here
8775 WARN("for non-ownerdata performance option not implemented.\n");
8783 * Sets the position of an item.
8786 * [I] infoPtr : valid pointer to the listview structure
8787 * [I] nItem : item index
8788 * [I] pt : coordinate
8794 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*pt
)
8798 TRACE("(nItem=%d, pt=%s\n", nItem
, wine_dbgstr_point(pt
));
8800 if (!pt
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
8801 !(infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)) return FALSE
;
8804 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
8806 /* This point value seems to be an undocumented feature.
8807 * The best guess is that it means either at the origin,
8808 * or at true beginning of the list. I will assume the origin. */
8809 if ((Pt
.x
== -1) && (Pt
.y
== -1))
8812 if (infoPtr
->uView
== LV_VIEW_ICON
)
8814 Pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
8815 Pt
.y
-= ICON_TOP_PADDING
;
8820 infoPtr
->bAutoarrange
= FALSE
;
8822 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &Pt
, FALSE
);
8827 * Sets the state of one or many items.
8830 * [I] infoPtr : valid pointer to the listview structure
8831 * [I] nItem : item index
8832 * [I] item : item or subitem info
8838 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*item
)
8843 if (!item
) return FALSE
;
8845 lvItem
.iItem
= nItem
;
8846 lvItem
.iSubItem
= 0;
8847 lvItem
.mask
= LVIF_STATE
;
8848 lvItem
.state
= item
->state
;
8849 lvItem
.stateMask
= item
->stateMask
;
8850 TRACE("item=%s\n", debuglvitem_t(&lvItem
, TRUE
));
8857 /* select all isn't allowed in LVS_SINGLESEL */
8858 if ((lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) && (infoPtr
->dwStyle
& LVS_SINGLESEL
))
8861 /* focus all isn't allowed */
8862 if (lvItem
.state
& lvItem
.stateMask
& LVIS_FOCUSED
) return FALSE
;
8864 notify
= infoPtr
->bDoChangeNotify
;
8865 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
8867 infoPtr
->bDoChangeNotify
= FALSE
;
8868 if (!(lvItem
.state
& LVIS_SELECTED
) && LISTVIEW_GetSelectedCount(infoPtr
))
8869 oldstate
|= LVIS_SELECTED
;
8870 if (infoPtr
->nFocusedItem
!= -1) oldstate
|= LVIS_FOCUSED
;
8873 /* apply to all items */
8874 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
8875 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) ret
= FALSE
;
8877 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
8881 infoPtr
->bDoChangeNotify
= notify
;
8885 nmlv
.uNewState
= lvItem
.state
& lvItem
.stateMask
;
8886 nmlv
.uOldState
= oldstate
& lvItem
.stateMask
;
8887 nmlv
.uChanged
= LVIF_STATE
;
8888 nmlv
.ptAction
.x
= nmlv
.ptAction
.y
= 0;
8891 notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
8895 ret
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
8902 * Sets the text of an item or subitem.
8905 * [I] hwnd : window handle
8906 * [I] nItem : item index
8907 * [I] lpLVItem : item or subitem info
8908 * [I] isW : TRUE if input is Unicode
8914 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
8918 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
8919 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
8921 lvItem
.iItem
= nItem
;
8922 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
8923 lvItem
.mask
= LVIF_TEXT
;
8924 lvItem
.pszText
= lpLVItem
->pszText
;
8925 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
8927 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
8929 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
8934 * Set item index that marks the start of a multiple selection.
8937 * [I] infoPtr : valid pointer to the listview structure
8938 * [I] nIndex : index
8941 * Index number or -1 if there is no selection mark.
8943 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
8945 INT nOldIndex
= infoPtr
->nSelectionMark
;
8947 TRACE("(nIndex=%d)\n", nIndex
);
8949 infoPtr
->nSelectionMark
= nIndex
;
8956 * Sets the text background color.
8959 * [I] infoPtr : valid pointer to the listview structure
8960 * [I] color : text background color
8966 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF color
)
8968 TRACE("(color=%x)\n", color
);
8970 infoPtr
->clrTextBk
= color
;
8976 * Sets the text foreground color.
8979 * [I] infoPtr : valid pointer to the listview structure
8980 * [I] color : text color
8986 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF color
)
8988 TRACE("(color=%x)\n", color
);
8990 infoPtr
->clrText
= color
;
8996 * Sets new ToolTip window to ListView control.
8999 * [I] infoPtr : valid pointer to the listview structure
9000 * [I] hwndNewToolTip : handle to new ToolTip
9005 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
9007 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
9008 infoPtr
->hwndToolTip
= hwndNewToolTip
;
9009 return hwndOldToolTip
;
9014 * sets the Unicode character format flag for the control
9016 * [I] infoPtr :valid pointer to the listview structure
9017 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
9020 * Old Unicode Format
9022 static BOOL
LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO
*infoPtr
, BOOL unicode
)
9024 SHORT rc
= infoPtr
->notifyFormat
;
9025 infoPtr
->notifyFormat
= (unicode
) ? NFR_UNICODE
: NFR_ANSI
;
9026 return rc
== NFR_UNICODE
;
9031 * sets the control view mode
9033 * [I] infoPtr :valid pointer to the listview structure
9034 * [I] nView :new view mode value
9040 static INT
LISTVIEW_SetView(LISTVIEW_INFO
*infoPtr
, DWORD nView
)
9042 SIZE oldIconSize
= infoPtr
->iconSize
;
9045 if (infoPtr
->uView
== nView
) return 1;
9047 if ((INT
)nView
< 0 || nView
> LV_VIEW_MAX
) return -1;
9048 if (nView
== LV_VIEW_TILE
)
9050 FIXME("View LV_VIEW_TILE unimplemented\n");
9054 infoPtr
->uView
= nView
;
9056 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9057 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
9059 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
9060 SetRectEmpty(&infoPtr
->rcFocus
);
9062 himl
= (nView
== LV_VIEW_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
9063 set_icon_size(&infoPtr
->iconSize
, himl
, nView
!= LV_VIEW_ICON
);
9068 if ((infoPtr
->iconSize
.cx
!= oldIconSize
.cx
) || (infoPtr
->iconSize
.cy
!= oldIconSize
.cy
))
9070 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9071 oldIconSize
.cx
, oldIconSize
.cy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
9072 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
9074 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9076 case LV_VIEW_SMALLICON
:
9077 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9079 case LV_VIEW_DETAILS
:
9084 LISTVIEW_CreateHeader( infoPtr
);
9086 hl
.prc
= &infoPtr
->rcList
;
9088 SendMessageW(infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9089 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9090 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9097 LISTVIEW_UpdateItemSize(infoPtr
);
9098 LISTVIEW_UpdateSize(infoPtr
);
9099 LISTVIEW_UpdateScroll(infoPtr
);
9100 LISTVIEW_InvalidateList(infoPtr
);
9102 TRACE("nView=%d\n", nView
);
9107 /* LISTVIEW_SetWorkAreas */
9111 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
9114 * [I] first : pointer to first ITEM_INFO to compare
9115 * [I] second : pointer to second ITEM_INFO to compare
9116 * [I] lParam : HWND of control
9119 * if first comes before second : negative
9120 * if first comes after second : positive
9121 * if first and second are equivalent : zero
9123 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
9125 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
9126 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
9127 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
9129 /* Forward the call to the client defined callback */
9130 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
9135 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
9138 * [I] first : pointer to first ITEM_INFO to compare
9139 * [I] second : pointer to second ITEM_INFO to compare
9140 * [I] lParam : HWND of control
9143 * if first comes before second : negative
9144 * if first comes after second : positive
9145 * if first and second are equivalent : zero
9147 static INT WINAPI
LISTVIEW_CallBackCompareEx(LPVOID first
, LPVOID second
, LPARAM lParam
)
9149 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
9150 INT first_idx
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, first
);
9151 INT second_idx
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, second
);
9153 /* Forward the call to the client defined callback */
9154 return (infoPtr
->pfnCompare
)( first_idx
, second_idx
, infoPtr
->lParamSort
);
9159 * Sorts the listview items.
9162 * [I] infoPtr : valid pointer to the listview structure
9163 * [I] pfnCompare : application-defined value
9164 * [I] lParamSort : pointer to comparison callback
9165 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
9171 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
,
9172 LPARAM lParamSort
, BOOL IsEx
)
9176 LPVOID selectionMarkItem
= NULL
;
9177 LPVOID focusedItem
= NULL
;
9180 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
9182 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
9184 if (!pfnCompare
) return FALSE
;
9185 if (!infoPtr
->hdpaItems
) return FALSE
;
9187 /* if there are 0 or 1 items, there is no need to sort */
9188 if (infoPtr
->nItemCount
< 2) return TRUE
;
9190 /* clear selection */
9191 ranges_clear(infoPtr
->selectionRanges
);
9193 /* save selection mark and focused item */
9194 if (infoPtr
->nSelectionMark
>= 0)
9195 selectionMarkItem
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
);
9196 if (infoPtr
->nFocusedItem
>= 0)
9197 focusedItem
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
9199 infoPtr
->pfnCompare
= pfnCompare
;
9200 infoPtr
->lParamSort
= lParamSort
;
9202 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompareEx
, (LPARAM
)infoPtr
);
9204 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
9206 /* restore selection ranges */
9207 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
9209 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
9210 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
9212 if (lpItem
->state
& LVIS_SELECTED
)
9213 ranges_additem(infoPtr
->selectionRanges
, i
);
9215 /* restore selection mark and focused item */
9216 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
9217 infoPtr
->nFocusedItem
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, focusedItem
);
9219 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
9221 /* refresh the display */
9222 LISTVIEW_InvalidateList(infoPtr
);
9228 * Update theme handle after a theme change.
9231 * [I] infoPtr : valid pointer to the listview structure
9235 * FAILURE : something else
9237 static LRESULT
LISTVIEW_ThemeChanged(const LISTVIEW_INFO
*infoPtr
)
9239 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
9240 CloseThemeData(theme
);
9241 OpenThemeData(infoPtr
->hwndSelf
, themeClass
);
9247 * Updates an items or rearranges the listview control.
9250 * [I] infoPtr : valid pointer to the listview structure
9251 * [I] nItem : item index
9257 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
9259 TRACE("(nItem=%d)\n", nItem
);
9261 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
9263 /* rearrange with default alignment style */
9264 if (is_autoarrange(infoPtr
))
9265 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9267 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
9274 * Draw the track line at the place defined in the infoPtr structure.
9275 * The line is drawn with a XOR pen so drawing the line for the second time
9276 * in the same place erases the line.
9279 * [I] infoPtr : valid pointer to the listview structure
9285 static BOOL
LISTVIEW_DrawTrackLine(const LISTVIEW_INFO
*infoPtr
)
9289 if (infoPtr
->xTrackLine
== -1)
9292 if (!(hdc
= GetDC(infoPtr
->hwndSelf
)))
9294 PatBlt( hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.top
,
9295 1, infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
, DSTINVERT
);
9296 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
9302 * Called when an edit control should be displayed. This function is called after
9303 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
9306 * [I] hwnd : Handle to the listview
9307 * [I] uMsg : WM_TIMER (ignored)
9308 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
9309 * [I] dwTimer : The elapsed time (ignored)
9314 static VOID CALLBACK
LISTVIEW_DelayedEditItem(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
9316 DELAYED_ITEM_EDIT
*editItem
= (DELAYED_ITEM_EDIT
*)idEvent
;
9317 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9319 KillTimer(hwnd
, idEvent
);
9320 editItem
->fEnabled
= FALSE
;
9321 /* check if the item is still selected */
9322 if (infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, editItem
->iItem
, LVIS_SELECTED
))
9323 LISTVIEW_EditLabelT(infoPtr
, editItem
->iItem
, TRUE
);
9328 * Creates the listview control - the WM_NCCREATE phase.
9331 * [I] hwnd : window handle
9332 * [I] lpcs : the create parameters
9338 static LRESULT
LISTVIEW_NCCreate(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
9340 LISTVIEW_INFO
*infoPtr
;
9343 TRACE("(lpcs=%p)\n", lpcs
);
9345 /* initialize info pointer */
9346 infoPtr
= Alloc(sizeof(LISTVIEW_INFO
));
9347 if (!infoPtr
) return FALSE
;
9349 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
9351 infoPtr
->hwndSelf
= hwnd
;
9352 infoPtr
->dwStyle
= lpcs
->style
; /* Note: may be changed in WM_CREATE */
9353 map_style_view(infoPtr
);
9354 /* determine the type of structures to use */
9355 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
9356 /* infoPtr->notifyFormat will be filled in WM_CREATE */
9358 /* initialize color information */
9359 infoPtr
->clrBk
= CLR_NONE
;
9360 infoPtr
->clrText
= CLR_DEFAULT
;
9361 infoPtr
->clrTextBk
= CLR_DEFAULT
;
9362 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
9363 infoPtr
->bDefaultBkColor
= TRUE
;
9365 /* set default values */
9366 infoPtr
->nFocusedItem
= -1;
9367 infoPtr
->nSelectionMark
= -1;
9368 infoPtr
->nHotItem
= -1;
9369 infoPtr
->bRedraw
= TRUE
;
9370 infoPtr
->bNoItemMetrics
= TRUE
;
9371 infoPtr
->bDoChangeNotify
= TRUE
;
9372 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
9373 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
9374 infoPtr
->nEditLabelItem
= -1;
9375 infoPtr
->nLButtonDownItem
= -1;
9376 infoPtr
->dwHoverTime
= HOVER_DEFAULT
; /* default system hover time */
9377 infoPtr
->nMeasureItemHeight
= 0;
9378 infoPtr
->xTrackLine
= -1; /* no track line */
9379 infoPtr
->itemEdit
.fEnabled
= FALSE
;
9380 infoPtr
->iVersion
= COMCTL32_VERSION
;
9381 infoPtr
->colRectsDirty
= FALSE
;
9383 /* get default font (icon title) */
9384 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
9385 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
9386 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
9387 LISTVIEW_SaveTextMetrics(infoPtr
);
9389 /* allocate memory for the data structure */
9390 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
9391 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
9392 if (!(infoPtr
->hdpaItemIds
= DPA_Create(10))) goto fail
;
9393 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
9394 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
9395 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
9399 DestroyWindow(infoPtr
->hwndHeader
);
9400 ranges_destroy(infoPtr
->selectionRanges
);
9401 DPA_Destroy(infoPtr
->hdpaItems
);
9402 DPA_Destroy(infoPtr
->hdpaItemIds
);
9403 DPA_Destroy(infoPtr
->hdpaPosX
);
9404 DPA_Destroy(infoPtr
->hdpaPosY
);
9405 DPA_Destroy(infoPtr
->hdpaColumns
);
9412 * Creates the listview control - the WM_CREATE phase. Most of the data is
9413 * already set up in LISTVIEW_NCCreate
9416 * [I] hwnd : window handle
9417 * [I] lpcs : the create parameters
9423 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
9425 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9427 TRACE("(lpcs=%p, style=0x%08x)\n", lpcs
, lpcs
->style
);
9429 infoPtr
->dwStyle
= lpcs
->style
;
9430 map_style_view(infoPtr
);
9432 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
9433 (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
9434 /* on error defaulting to ANSI notifications */
9435 if (infoPtr
->notifyFormat
== 0) infoPtr
->notifyFormat
= NFR_ANSI
;
9436 TRACE("notify format=%d\n", infoPtr
->notifyFormat
);
9438 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && (lpcs
->style
& WS_VISIBLE
))
9440 if (LISTVIEW_CreateHeader(infoPtr
) < 0) return -1;
9443 infoPtr
->hwndHeader
= 0;
9445 /* init item size to avoid division by 0 */
9446 LISTVIEW_UpdateItemSize (infoPtr
);
9447 LISTVIEW_UpdateSize (infoPtr
);
9449 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
9451 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
) && (WS_VISIBLE
& lpcs
->style
))
9453 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
9455 LISTVIEW_UpdateScroll(infoPtr
);
9456 /* send WM_MEASUREITEM notification */
9457 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) notify_measureitem(infoPtr
);
9460 OpenThemeData(hwnd
, themeClass
);
9462 /* initialize the icon sizes */
9463 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, infoPtr
->uView
!= LV_VIEW_ICON
);
9464 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
9470 * Destroys the listview control.
9473 * [I] infoPtr : valid pointer to the listview structure
9479 static LRESULT
LISTVIEW_Destroy(LISTVIEW_INFO
*infoPtr
)
9481 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
9482 CloseThemeData(theme
);
9484 /* delete all items */
9485 LISTVIEW_DeleteAllItems(infoPtr
, TRUE
);
9492 * Enables the listview control.
9495 * [I] infoPtr : valid pointer to the listview structure
9496 * [I] bEnable : specifies whether to enable or disable the window
9502 static BOOL
LISTVIEW_Enable(const LISTVIEW_INFO
*infoPtr
)
9504 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
9505 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
9511 * Erases the background of the listview control.
9514 * [I] infoPtr : valid pointer to the listview structure
9515 * [I] hdc : device context handle
9521 static inline BOOL
LISTVIEW_EraseBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
9525 TRACE("(hdc=%p)\n", hdc
);
9527 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
9529 if (infoPtr
->clrBk
== CLR_NONE
)
9531 if (infoPtr
->dwLvExStyle
& LVS_EX_TRANSPARENTBKGND
)
9532 return SendMessageW(infoPtr
->hwndNotify
, WM_PRINTCLIENT
,
9533 (WPARAM
)hdc
, PRF_ERASEBKGND
);
9535 return SendMessageW(infoPtr
->hwndNotify
, WM_ERASEBKGND
, (WPARAM
)hdc
, 0);
9538 /* for double buffered controls we need to do this during refresh */
9539 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) return FALSE
;
9541 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
9547 * Helper function for LISTVIEW_[HV]Scroll *only*.
9548 * Performs vertical/horizontal scrolling by a give amount.
9551 * [I] infoPtr : valid pointer to the listview structure
9552 * [I] dx : amount of horizontal scroll
9553 * [I] dy : amount of vertical scroll
9555 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
9557 /* now we can scroll the list */
9558 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
9559 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
9560 /* if we have focus, adjust rect */
9561 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
9562 UpdateWindow(infoPtr
->hwndSelf
);
9567 * Performs vertical scrolling.
9570 * [I] infoPtr : valid pointer to the listview structure
9571 * [I] nScrollCode : scroll code
9572 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9573 * [I] hScrollWnd : scrollbar control window handle
9579 * SB_LINEUP/SB_LINEDOWN:
9580 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
9581 * for LVS_REPORT is 1 line
9582 * for LVS_LIST cannot occur
9585 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
9588 INT nOldScrollPos
, nNewScrollPos
;
9589 SCROLLINFO scrollInfo
;
9592 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
9593 debugscrollcode(nScrollCode
), nScrollDiff
);
9595 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9597 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
9598 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
9600 is_an_icon
= ((infoPtr
->uView
== LV_VIEW_ICON
) || (infoPtr
->uView
== LV_VIEW_SMALLICON
));
9602 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
9604 nOldScrollPos
= scrollInfo
.nPos
;
9605 switch (nScrollCode
)
9611 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
9615 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
9619 nScrollDiff
= -scrollInfo
.nPage
;
9623 nScrollDiff
= scrollInfo
.nPage
;
9626 case SB_THUMBPOSITION
:
9628 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
9635 /* quit right away if pos isn't changing */
9636 if (nScrollDiff
== 0) return 0;
9638 /* calculate new position, and handle overflows */
9639 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
9640 if (nScrollDiff
> 0) {
9641 if (nNewScrollPos
< nOldScrollPos
||
9642 nNewScrollPos
> scrollInfo
.nMax
)
9643 nNewScrollPos
= scrollInfo
.nMax
;
9645 if (nNewScrollPos
> nOldScrollPos
||
9646 nNewScrollPos
< scrollInfo
.nMin
)
9647 nNewScrollPos
= scrollInfo
.nMin
;
9650 /* set the new position, and reread in case it changed */
9651 scrollInfo
.fMask
= SIF_POS
;
9652 scrollInfo
.nPos
= nNewScrollPos
;
9653 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
9655 /* carry on only if it really changed */
9656 if (nNewScrollPos
== nOldScrollPos
) return 0;
9658 /* now adjust to client coordinates */
9659 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
9660 if (infoPtr
->uView
== LV_VIEW_DETAILS
) nScrollDiff
*= infoPtr
->nItemHeight
;
9662 /* and scroll the window */
9663 scroll_list(infoPtr
, 0, nScrollDiff
);
9670 * Performs horizontal scrolling.
9673 * [I] infoPtr : valid pointer to the listview structure
9674 * [I] nScrollCode : scroll code
9675 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9676 * [I] hScrollWnd : scrollbar control window handle
9682 * SB_LINELEFT/SB_LINERIGHT:
9683 * for LVS_ICON, LVS_SMALLICON 1 pixel
9684 * for LVS_REPORT is 1 pixel
9685 * for LVS_LIST is 1 column --> which is a 1 because the
9686 * scroll is based on columns not pixels
9689 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
9692 INT nOldScrollPos
, nNewScrollPos
;
9693 SCROLLINFO scrollInfo
;
9696 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
9697 debugscrollcode(nScrollCode
), nScrollDiff
);
9699 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9701 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
9702 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
9704 is_an_icon
= ((infoPtr
->uView
== LV_VIEW_ICON
) || (infoPtr
->uView
== LV_VIEW_SMALLICON
));
9706 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
9708 nOldScrollPos
= scrollInfo
.nPos
;
9710 switch (nScrollCode
)
9716 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
9720 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
9724 nScrollDiff
= -scrollInfo
.nPage
;
9728 nScrollDiff
= scrollInfo
.nPage
;
9731 case SB_THUMBPOSITION
:
9733 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
9740 /* quit right away if pos isn't changing */
9741 if (nScrollDiff
== 0) return 0;
9743 /* calculate new position, and handle overflows */
9744 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
9745 if (nScrollDiff
> 0) {
9746 if (nNewScrollPos
< nOldScrollPos
||
9747 nNewScrollPos
> scrollInfo
.nMax
)
9748 nNewScrollPos
= scrollInfo
.nMax
;
9750 if (nNewScrollPos
> nOldScrollPos
||
9751 nNewScrollPos
< scrollInfo
.nMin
)
9752 nNewScrollPos
= scrollInfo
.nMin
;
9755 /* set the new position, and reread in case it changed */
9756 scrollInfo
.fMask
= SIF_POS
;
9757 scrollInfo
.nPos
= nNewScrollPos
;
9758 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
9760 /* carry on only if it really changed */
9761 if (nNewScrollPos
== nOldScrollPos
) return 0;
9763 if (infoPtr
->hwndHeader
) LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
9765 /* now adjust to client coordinates */
9766 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
9767 if (infoPtr
->uView
== LV_VIEW_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
9769 /* and scroll the window */
9770 scroll_list(infoPtr
, nScrollDiff
, 0);
9775 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
9777 INT gcWheelDelta
= 0;
9778 INT pulScrollLines
= 3;
9780 TRACE("(wheelDelta=%d)\n", wheelDelta
);
9782 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
9783 gcWheelDelta
-= wheelDelta
;
9785 switch(infoPtr
->uView
)
9788 case LV_VIEW_SMALLICON
:
9790 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
9791 * should be fixed in the future.
9793 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (gcWheelDelta
< 0) ?
9794 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
);
9797 case LV_VIEW_DETAILS
:
9798 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
9800 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
9801 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
9802 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, cLineScroll
);
9807 LISTVIEW_HScroll(infoPtr
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0);
9818 * [I] infoPtr : valid pointer to the listview structure
9819 * [I] nVirtualKey : virtual key
9820 * [I] lKeyData : key data
9825 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
9827 HWND hwndSelf
= infoPtr
->hwndSelf
;
9829 NMLVKEYDOWN nmKeyDown
;
9831 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey
, lKeyData
);
9833 /* send LVN_KEYDOWN notification */
9834 nmKeyDown
.wVKey
= nVirtualKey
;
9835 nmKeyDown
.flags
= 0;
9836 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
9837 if (!IsWindow(hwndSelf
))
9840 switch (nVirtualKey
)
9843 nItem
= infoPtr
->nFocusedItem
;
9844 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
9845 toggle_checkbox_state(infoPtr
, infoPtr
->nFocusedItem
);
9849 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
9851 if (!notify(infoPtr
, NM_RETURN
)) return 0;
9852 if (!notify(infoPtr
, LVN_ITEMACTIVATE
)) return 0;
9857 if (infoPtr
->nItemCount
> 0)
9862 if (infoPtr
->nItemCount
> 0)
9863 nItem
= infoPtr
->nItemCount
- 1;
9867 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
9871 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
9875 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
9879 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
9883 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
9885 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
9886 if (infoPtr
->nFocusedItem
== topidx
)
9887 nItem
= topidx
- LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
9892 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
9893 * LISTVIEW_GetCountPerRow(infoPtr
);
9894 if(nItem
< 0) nItem
= 0;
9898 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
9900 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
9901 INT cnt
= LISTVIEW_GetCountPerColumn(infoPtr
);
9902 if (infoPtr
->nFocusedItem
== topidx
+ cnt
- 1)
9903 nItem
= infoPtr
->nFocusedItem
+ cnt
- 1;
9905 nItem
= topidx
+ cnt
- 1;
9908 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
9909 * LISTVIEW_GetCountPerRow(infoPtr
);
9910 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
9914 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
|| nVirtualKey
== VK_SPACE
))
9915 LISTVIEW_KeySelection(infoPtr
, nItem
, nVirtualKey
== VK_SPACE
);
9925 * [I] infoPtr : valid pointer to the listview structure
9930 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
9934 /* if we did not have the focus, there's nothing to do */
9935 if (!infoPtr
->bFocus
) return 0;
9937 /* send NM_KILLFOCUS notification */
9938 if (!notify(infoPtr
, NM_KILLFOCUS
)) return 0;
9940 /* if we have a focus rectangle, get rid of it */
9941 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
9943 /* if have a marquee selection, stop it */
9944 if (infoPtr
->bMarqueeSelect
)
9946 /* Remove the marquee rectangle and release our mouse capture */
9947 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeRect
);
9950 SetRect(&infoPtr
->marqueeRect
, 0, 0, 0, 0);
9952 infoPtr
->bMarqueeSelect
= FALSE
;
9953 infoPtr
->bScrolling
= FALSE
;
9954 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
);
9957 /* set window focus flag */
9958 infoPtr
->bFocus
= FALSE
;
9960 /* invalidate the selected items before resetting focus flag */
9961 LISTVIEW_InvalidateSelectedItems(infoPtr
);
9968 * Processes double click messages (left mouse button).
9971 * [I] infoPtr : valid pointer to the listview structure
9972 * [I] wKey : key flag
9973 * [I] x,y : mouse coordinate
9978 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9980 LVHITTESTINFO htInfo
;
9982 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey
, x
, y
);
9984 /* Cancel the item edition if any */
9985 if (infoPtr
->itemEdit
.fEnabled
)
9987 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
)&infoPtr
->itemEdit
);
9988 infoPtr
->itemEdit
.fEnabled
= FALSE
;
9991 /* send NM_RELEASEDCAPTURE notification */
9992 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9997 /* send NM_DBLCLK notification */
9998 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
9999 if (!notify_click(infoPtr
, NM_DBLCLK
, &htInfo
)) return 0;
10001 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
10002 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
10009 * Processes mouse down messages (left mouse button).
10012 * infoPtr [I ] valid pointer to the listview structure
10013 * wKey [I ] key flag
10014 * x,y [I ] mouse coordinate
10019 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10021 LVHITTESTINFO lvHitTestInfo
;
10022 static BOOL bGroupSelect
= TRUE
;
10023 POINT pt
= { x
, y
};
10026 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey
, x
, y
);
10028 /* send NM_RELEASEDCAPTURE notification */
10029 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10031 /* set left button down flag and record the click position */
10032 infoPtr
->bLButtonDown
= TRUE
;
10033 infoPtr
->ptClickPos
= pt
;
10034 infoPtr
->bDragging
= FALSE
;
10035 infoPtr
->bMarqueeSelect
= FALSE
;
10036 infoPtr
->bScrolling
= FALSE
;
10038 lvHitTestInfo
.pt
.x
= x
;
10039 lvHitTestInfo
.pt
.y
= y
;
10041 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
10042 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt
), nItem
);
10043 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
10045 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
10047 toggle_checkbox_state(infoPtr
, nItem
);
10051 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
10053 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
10054 infoPtr
->nEditLabelItem
= nItem
;
10056 LISTVIEW_SetSelection(infoPtr
, nItem
);
10060 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
10064 if (!LISTVIEW_AddGroupSelection(infoPtr
, nItem
)) return 0;
10065 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
10066 infoPtr
->nSelectionMark
= nItem
;
10072 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
10073 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
10075 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
10076 infoPtr
->nSelectionMark
= nItem
;
10079 else if (wKey
& MK_CONTROL
)
10083 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
10085 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
10086 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
10087 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
10088 infoPtr
->nSelectionMark
= nItem
;
10090 else if (wKey
& MK_SHIFT
)
10092 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
10096 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
10098 infoPtr
->nEditLabelItem
= nItem
;
10099 infoPtr
->nLButtonDownItem
= nItem
;
10101 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
10104 /* set selection (clears other pre-existing selections) */
10105 LISTVIEW_SetSelection(infoPtr
, nItem
);
10109 if (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
)
10110 if(lvHitTestInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&lvHitTestInfo
);
10114 if (!infoPtr
->bFocus
)
10115 SetFocus(infoPtr
->hwndSelf
);
10117 /* remove all selections */
10118 if (!(wKey
& MK_CONTROL
) && !(wKey
& MK_SHIFT
))
10119 LISTVIEW_DeselectAll(infoPtr
);
10128 * Processes mouse up messages (left mouse button).
10131 * infoPtr [I ] valid pointer to the listview structure
10132 * wKey [I ] key flag
10133 * x,y [I ] mouse coordinate
10138 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10140 LVHITTESTINFO lvHitTestInfo
;
10142 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey
, x
, y
);
10144 if (!infoPtr
->bLButtonDown
) return 0;
10146 lvHitTestInfo
.pt
.x
= x
;
10147 lvHitTestInfo
.pt
.y
= y
;
10149 /* send NM_CLICK notification */
10150 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
10151 if (!notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
)) return 0;
10153 /* set left button flag */
10154 infoPtr
->bLButtonDown
= FALSE
;
10156 /* set a single selection, reset others */
10157 if(lvHitTestInfo
.iItem
== infoPtr
->nLButtonDownItem
&& lvHitTestInfo
.iItem
!= -1)
10158 LISTVIEW_SetSelection(infoPtr
, infoPtr
->nLButtonDownItem
);
10159 infoPtr
->nLButtonDownItem
= -1;
10161 if (infoPtr
->bDragging
|| infoPtr
->bMarqueeSelect
)
10163 /* Remove the marquee rectangle and release our mouse capture */
10164 if (infoPtr
->bMarqueeSelect
)
10166 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeDrawRect
);
10170 SetRect(&infoPtr
->marqueeRect
, 0, 0, 0, 0);
10171 SetRect(&infoPtr
->marqueeDrawRect
, 0, 0, 0, 0);
10173 infoPtr
->bDragging
= FALSE
;
10174 infoPtr
->bMarqueeSelect
= FALSE
;
10175 infoPtr
->bScrolling
= FALSE
;
10177 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
);
10181 /* if we clicked on a selected item, edit the label */
10182 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
10184 /* we want to make sure the user doesn't want to do a double click. So we will
10185 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
10187 infoPtr
->itemEdit
.fEnabled
= TRUE
;
10188 infoPtr
->itemEdit
.iItem
= lvHitTestInfo
.iItem
;
10189 SetTimer(infoPtr
->hwndSelf
,
10190 (UINT_PTR
)&infoPtr
->itemEdit
,
10191 GetDoubleClickTime(),
10192 LISTVIEW_DelayedEditItem
);
10195 if (!infoPtr
->bFocus
)
10196 SetFocus(infoPtr
->hwndSelf
);
10203 * Destroys the listview control (called after WM_DESTROY).
10206 * [I] infoPtr : valid pointer to the listview structure
10211 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
10217 /* destroy data structure */
10218 DPA_Destroy(infoPtr
->hdpaItems
);
10219 DPA_Destroy(infoPtr
->hdpaItemIds
);
10220 DPA_Destroy(infoPtr
->hdpaPosX
);
10221 DPA_Destroy(infoPtr
->hdpaPosY
);
10223 for (i
= 0; i
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); i
++)
10224 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, i
));
10225 DPA_Destroy(infoPtr
->hdpaColumns
);
10226 ranges_destroy(infoPtr
->selectionRanges
);
10228 /* destroy image lists */
10229 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
10231 ImageList_Destroy(infoPtr
->himlNormal
);
10232 ImageList_Destroy(infoPtr
->himlSmall
);
10233 ImageList_Destroy(infoPtr
->himlState
);
10236 /* destroy font, bkgnd brush */
10237 infoPtr
->hFont
= 0;
10238 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
10239 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
10241 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
10243 /* free listview info pointer*/
10251 * Handles notifications.
10254 * [I] infoPtr : valid pointer to the listview structure
10255 * [I] lpnmhdr : notification information
10260 static LRESULT
LISTVIEW_Notify(LISTVIEW_INFO
*infoPtr
, const NMHDR
*lpnmhdr
)
10262 const NMHEADERW
*lpnmh
;
10264 TRACE("(lpnmhdr=%p)\n", lpnmhdr
);
10266 if (!lpnmhdr
|| lpnmhdr
->hwndFrom
!= infoPtr
->hwndHeader
) return 0;
10268 /* remember: HDN_LAST < HDN_FIRST */
10269 if (lpnmhdr
->code
> HDN_FIRST
|| lpnmhdr
->code
< HDN_LAST
) return 0;
10270 lpnmh
= (const NMHEADERW
*)lpnmhdr
;
10272 if (lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
10274 switch (lpnmhdr
->code
)
10279 COLUMN_INFO
*lpColumnInfo
;
10283 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
10286 /* remove the old line (if any) */
10287 LISTVIEW_DrawTrackLine(infoPtr
);
10289 /* compute & draw the new line */
10290 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
10291 x
= lpColumnInfo
->rcHeader
.left
+ lpnmh
->pitem
->cxy
;
10292 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
10293 infoPtr
->xTrackLine
= x
+ ptOrigin
.x
;
10294 LISTVIEW_DrawTrackLine(infoPtr
);
10298 case HDN_ENDTRACKA
:
10299 case HDN_ENDTRACKW
:
10300 /* remove the track line (if any) */
10301 LISTVIEW_DrawTrackLine(infoPtr
);
10302 infoPtr
->xTrackLine
= -1;
10305 case HDN_BEGINDRAG
:
10306 notify_forward_header(infoPtr
, lpnmh
);
10307 return (infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
) == 0;
10310 infoPtr
->colRectsDirty
= TRUE
;
10311 LISTVIEW_InvalidateList(infoPtr
);
10312 notify_forward_header(infoPtr
, lpnmh
);
10315 case HDN_ITEMCHANGEDW
:
10316 case HDN_ITEMCHANGEDA
:
10318 COLUMN_INFO
*lpColumnInfo
;
10322 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
10324 hdi
.mask
= HDI_WIDTH
;
10325 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, lpnmh
->iItem
, (LPARAM
)&hdi
)) return 0;
10329 cxy
= lpnmh
->pitem
->cxy
;
10331 /* determine how much we change since the last know position */
10332 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
10333 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
10336 lpColumnInfo
->rcHeader
.right
+= dx
;
10338 hdi
.mask
= HDI_ORDER
;
10339 SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, lpnmh
->iItem
, (LPARAM
)&hdi
);
10341 /* not the rightmost one */
10342 if (hdi
.iOrder
+ 1 < DPA_GetPtrCount(infoPtr
->hdpaColumns
))
10344 INT nIndex
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
10345 hdi
.iOrder
+ 1, 0);
10346 LISTVIEW_ScrollColumns(infoPtr
, nIndex
, dx
);
10350 /* only needs to update the scrolls */
10351 infoPtr
->nItemWidth
+= dx
;
10352 LISTVIEW_UpdateScroll(infoPtr
);
10354 LISTVIEW_UpdateItemSize(infoPtr
);
10355 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& is_redrawing(infoPtr
))
10358 RECT rcCol
= lpColumnInfo
->rcHeader
;
10360 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
10361 OffsetRect(&rcCol
, ptOrigin
.x
, 0);
10363 rcCol
.top
= infoPtr
->rcList
.top
;
10364 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
10366 /* resizing left-aligned columns leaves most of the left side untouched */
10367 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
10369 INT nMaxDirty
= infoPtr
->nEllipsisWidth
+ infoPtr
->ntmMaxCharWidth
;
10372 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- nMaxDirty
);
10375 /* when shrinking the last column clear the now unused field */
10376 if (hdi
.iOrder
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
10382 /* deal with right from rightmost column area */
10383 right
.left
= rcCol
.right
;
10384 right
.top
= rcCol
.top
;
10385 right
.bottom
= rcCol
.bottom
;
10386 right
.right
= infoPtr
->rcList
.right
;
10388 LISTVIEW_InvalidateRect(infoPtr
, &right
);
10391 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
10397 case HDN_ITEMCLICKW
:
10398 case HDN_ITEMCLICKA
:
10400 /* Handle sorting by Header Column */
10403 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
10405 nmlv
.iSubItem
= lpnmh
->iItem
;
10406 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
10407 notify_forward_header(infoPtr
, lpnmh
);
10411 case HDN_DIVIDERDBLCLICKW
:
10412 case HDN_DIVIDERDBLCLICKA
:
10413 /* FIXME: for LVS_EX_HEADERINALLVIEWS and not LV_VIEW_DETAILS
10414 we should use LVSCW_AUTOSIZE_USEHEADER, helper rework or
10415 split needed for that */
10416 LISTVIEW_SetColumnWidth(infoPtr
, lpnmh
->iItem
, LVSCW_AUTOSIZE
);
10417 notify_forward_header(infoPtr
, lpnmh
);
10426 * Paint non-client area of control.
10429 * [I] infoPtr : valid pointer to the listview structureof the sender
10430 * [I] region : update region
10433 * TRUE - frame was painted
10434 * FALSE - call default window proc
10436 static BOOL
LISTVIEW_NCPaint(const LISTVIEW_INFO
*infoPtr
, HRGN region
)
10438 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
10442 int cxEdge
= GetSystemMetrics (SM_CXEDGE
),
10443 cyEdge
= GetSystemMetrics (SM_CYEDGE
);
10446 return DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)region
, 0);
10448 GetWindowRect(infoPtr
->hwndSelf
, &r
);
10450 cliprgn
= CreateRectRgn (r
.left
+ cxEdge
, r
.top
+ cyEdge
,
10451 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
10452 if (region
!= (HRGN
)1)
10453 CombineRgn (cliprgn
, cliprgn
, region
, RGN_AND
);
10454 OffsetRect(&r
, -r
.left
, -r
.top
);
10456 dc
= GetDCEx(infoPtr
->hwndSelf
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
10457 OffsetRect(&r
, -r
.left
, -r
.top
);
10459 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
10460 DrawThemeParentBackground(infoPtr
->hwndSelf
, dc
, &r
);
10461 DrawThemeBackground (theme
, dc
, 0, 0, &r
, 0);
10462 ReleaseDC(infoPtr
->hwndSelf
, dc
);
10464 /* Call default proc to get the scrollbars etc. painted */
10465 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)cliprgn
, 0);
10472 * Determines the type of structure to use.
10475 * [I] infoPtr : valid pointer to the listview structureof the sender
10476 * [I] hwndFrom : listview window handle
10477 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
10482 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
10484 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
10486 if (nCommand
== NF_REQUERY
)
10487 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
10489 return infoPtr
->notifyFormat
;
10494 * Paints/Repaints the listview control. Internal use.
10497 * [I] infoPtr : valid pointer to the listview structure
10498 * [I] hdc : device context handle
10503 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
10505 TRACE("(hdc=%p)\n", hdc
);
10507 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
10509 infoPtr
->bNoItemMetrics
= FALSE
;
10510 LISTVIEW_UpdateItemSize(infoPtr
);
10511 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)
10512 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
10513 LISTVIEW_UpdateScroll(infoPtr
);
10516 if (infoPtr
->hwndHeader
) UpdateWindow(infoPtr
->hwndHeader
);
10519 LISTVIEW_Refresh(infoPtr
, hdc
, NULL
);
10524 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
10525 if (!hdc
) return 1;
10526 LISTVIEW_Refresh(infoPtr
, hdc
, ps
.fErase
? &ps
.rcPaint
: NULL
);
10527 EndPaint(infoPtr
->hwndSelf
, &ps
);
10535 * Paints/Repaints the listview control, WM_PAINT handler.
10538 * [I] infoPtr : valid pointer to the listview structure
10539 * [I] hdc : device context handle
10544 static inline LRESULT
LISTVIEW_WMPaint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
10546 TRACE("(hdc=%p)\n", hdc
);
10548 if (!is_redrawing(infoPtr
))
10549 return DefWindowProcW (infoPtr
->hwndSelf
, WM_PAINT
, (WPARAM
)hdc
, 0);
10551 return LISTVIEW_Paint(infoPtr
, hdc
);
10556 * Paints/Repaints the listview control.
10559 * [I] infoPtr : valid pointer to the listview structure
10560 * [I] hdc : device context handle
10561 * [I] options : drawing options
10566 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
10568 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc
, options
);
10570 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
10573 if (options
& PRF_ERASEBKGND
)
10574 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
10576 if (options
& PRF_CLIENT
)
10577 LISTVIEW_Paint(infoPtr
, hdc
);
10585 * Processes double click messages (right mouse button).
10588 * [I] infoPtr : valid pointer to the listview structure
10589 * [I] wKey : key flag
10590 * [I] x,y : mouse coordinate
10595 static LRESULT
LISTVIEW_RButtonDblClk(const LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10597 LVHITTESTINFO lvHitTestInfo
;
10599 TRACE("(key=%hu,X=%u,Y=%u)\n", wKey
, x
, y
);
10601 /* send NM_RELEASEDCAPTURE notification */
10602 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10604 /* send NM_RDBLCLK notification */
10605 lvHitTestInfo
.pt
.x
= x
;
10606 lvHitTestInfo
.pt
.y
= y
;
10607 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
10608 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
10615 * Processes mouse down messages (right mouse button).
10618 * [I] infoPtr : valid pointer to the listview structure
10619 * [I] wKey : key flag
10620 * [I] x,y : mouse coordinate
10625 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10627 LVHITTESTINFO lvHitTestInfo
;
10630 TRACE("(key=%hu,X=%u,Y=%u)\n", wKey
, x
, y
);
10632 /* send NM_RELEASEDCAPTURE notification */
10633 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10635 /* make sure the listview control window has the focus */
10636 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
10638 /* set right button down flag */
10639 infoPtr
->bRButtonDown
= TRUE
;
10641 /* determine the index of the selected item */
10642 lvHitTestInfo
.pt
.x
= x
;
10643 lvHitTestInfo
.pt
.y
= y
;
10644 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
10646 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
10648 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
10649 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
10650 !LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
10651 LISTVIEW_SetSelection(infoPtr
, nItem
);
10655 LISTVIEW_DeselectAll(infoPtr
);
10663 * Processes mouse up messages (right mouse button).
10666 * [I] infoPtr : valid pointer to the listview structure
10667 * [I] wKey : key flag
10668 * [I] x,y : mouse coordinate
10673 static LRESULT
LISTVIEW_RButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10675 LVHITTESTINFO lvHitTestInfo
;
10678 TRACE("(key=%hu,X=%u,Y=%u)\n", wKey
, x
, y
);
10680 if (!infoPtr
->bRButtonDown
) return 0;
10682 /* set button flag */
10683 infoPtr
->bRButtonDown
= FALSE
;
10685 /* Send NM_RCLICK notification */
10686 lvHitTestInfo
.pt
.x
= x
;
10687 lvHitTestInfo
.pt
.y
= y
;
10688 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
10689 if (!notify_click(infoPtr
, NM_RCLICK
, &lvHitTestInfo
)) return 0;
10691 /* Change to screen coordinate for WM_CONTEXTMENU */
10692 pt
= lvHitTestInfo
.pt
;
10693 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
10695 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
10696 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
10697 (WPARAM
)infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
10708 * [I] infoPtr : valid pointer to the listview structure
10709 * [I] hwnd : window handle of window containing the cursor
10710 * [I] nHittest : hit-test code
10711 * [I] wMouseMsg : ideintifier of the mouse message
10714 * TRUE if cursor is set
10717 static BOOL
LISTVIEW_SetCursor(const LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
10719 LVHITTESTINFO lvHitTestInfo
;
10721 if (!LISTVIEW_IsHotTracking(infoPtr
)) goto forward
;
10723 if (!infoPtr
->hHotCursor
) goto forward
;
10725 GetCursorPos(&lvHitTestInfo
.pt
);
10726 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) goto forward
;
10728 SetCursor(infoPtr
->hHotCursor
);
10734 return DefWindowProcW(infoPtr
->hwndSelf
, WM_SETCURSOR
, wParam
, lParam
);
10742 * [I] infoPtr : valid pointer to the listview structure
10743 * [I] hwndLoseFocus : handle of previously focused window
10748 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
10750 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
10752 /* if we have the focus already, there's nothing to do */
10753 if (infoPtr
->bFocus
) return 0;
10755 /* send NM_SETFOCUS notification */
10756 if (!notify(infoPtr
, NM_SETFOCUS
)) return 0;
10758 /* set window focus flag */
10759 infoPtr
->bFocus
= TRUE
;
10761 /* put the focus rect back on */
10762 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
10764 /* redraw all visible selected items */
10765 LISTVIEW_InvalidateSelectedItems(infoPtr
);
10775 * [I] infoPtr : valid pointer to the listview structure
10776 * [I] fRedraw : font handle
10777 * [I] fRedraw : redraw flag
10782 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
10784 HFONT oldFont
= infoPtr
->hFont
;
10786 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
10788 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
10789 if (infoPtr
->hFont
== oldFont
) return 0;
10791 LISTVIEW_SaveTextMetrics(infoPtr
);
10793 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
10795 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
10796 LISTVIEW_UpdateSize(infoPtr
);
10797 LISTVIEW_UpdateScroll(infoPtr
);
10800 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
10807 * Message handling for WM_SETREDRAW.
10808 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
10811 * [I] infoPtr : valid pointer to the listview structure
10812 * [I] bRedraw: state of redraw flag
10815 * DefWinProc return value
10817 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
10819 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr
->bRedraw
, bRedraw
);
10821 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
10822 if ((infoPtr
->bRedraw
&& bRedraw
) || (!infoPtr
->bRedraw
&& !bRedraw
)) return 0;
10824 infoPtr
->bRedraw
= bRedraw
;
10826 if(!bRedraw
) return 0;
10828 if (is_autoarrange(infoPtr
))
10829 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
10830 LISTVIEW_UpdateScroll(infoPtr
);
10832 /* despite what the WM_SETREDRAW docs says, apps expect us
10833 * to invalidate the listview here... stupid! */
10834 LISTVIEW_InvalidateList(infoPtr
);
10841 * Resizes the listview control. This function processes WM_SIZE
10842 * messages. At this time, the width and height are not used.
10845 * [I] infoPtr : valid pointer to the listview structure
10846 * [I] Width : new width
10847 * [I] Height : new height
10852 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
10854 RECT rcOld
= infoPtr
->rcList
;
10856 TRACE("(width=%d, height=%d)\n", Width
, Height
);
10858 LISTVIEW_UpdateSize(infoPtr
);
10859 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
10861 /* do not bother with display related stuff if we're not redrawing */
10862 if (!is_redrawing(infoPtr
)) return 0;
10864 if (is_autoarrange(infoPtr
))
10865 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
10867 LISTVIEW_UpdateScroll(infoPtr
);
10869 /* refresh all only for lists whose height changed significantly */
10870 if ((infoPtr
->uView
== LV_VIEW_LIST
) &&
10871 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
10872 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
10873 LISTVIEW_InvalidateList(infoPtr
);
10880 * Sets the size information.
10883 * [I] infoPtr : valid pointer to the listview structure
10888 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
10890 TRACE("uView=%d, rcList(old)=%s\n", infoPtr
->uView
, wine_dbgstr_rect(&infoPtr
->rcList
));
10892 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
10894 if (infoPtr
->uView
== LV_VIEW_LIST
)
10896 /* Apparently the "LIST" style is supposed to have the same
10897 * number of items in a column even if there is no scroll bar.
10898 * Since if a scroll bar already exists then the bottom is already
10899 * reduced, only reduce if the scroll bar does not currently exist.
10900 * The "2" is there to mimic the native control. I think it may be
10901 * related to either padding or edges. (GLA 7/2002)
10903 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_HSCROLL
))
10904 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
10905 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
10908 /* if control created invisible header isn't created */
10909 if (infoPtr
->hwndHeader
)
10914 hl
.prc
= &infoPtr
->rcList
;
10916 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
10917 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp
.flags
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
10919 if (LISTVIEW_IsHeaderEnabled(infoPtr
))
10920 wp
.flags
|= SWP_SHOWWINDOW
;
10923 wp
.flags
|= SWP_HIDEWINDOW
;
10927 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
10928 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
10930 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
10932 /* extra padding for grid */
10933 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
10934 infoPtr
->rcList
.top
+= 2;
10936 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr
->rcList
));
10941 * Processes WM_STYLECHANGED messages.
10944 * [I] infoPtr : valid pointer to the listview structure
10945 * [I] wStyleType : window style type (normal or extended)
10946 * [I] lpss : window style information
10951 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
10952 const STYLESTRUCT
*lpss
)
10954 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
10955 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
10958 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
10959 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
10961 if (wStyleType
!= GWL_STYLE
) return 0;
10963 infoPtr
->dwStyle
= lpss
->styleNew
;
10964 map_style_view(infoPtr
);
10966 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
10967 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
10968 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
10970 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
10971 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
10972 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
10974 if (uNewView
!= uOldView
)
10976 SIZE oldIconSize
= infoPtr
->iconSize
;
10979 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
10980 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
10982 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
10983 SetRectEmpty(&infoPtr
->rcFocus
);
10985 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
10986 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
10988 if (uNewView
== LVS_ICON
)
10990 if ((infoPtr
->iconSize
.cx
!= oldIconSize
.cx
) || (infoPtr
->iconSize
.cy
!= oldIconSize
.cy
))
10992 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
10993 oldIconSize
.cx
, oldIconSize
.cy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
10994 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
10997 else if (uNewView
== LVS_REPORT
)
11002 LISTVIEW_CreateHeader( infoPtr
);
11004 hl
.prc
= &infoPtr
->rcList
;
11006 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
11007 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
11008 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
11009 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
11012 LISTVIEW_UpdateItemSize(infoPtr
);
11015 if (uNewView
== LVS_REPORT
|| infoPtr
->dwLvExStyle
& LVS_EX_HEADERINALLVIEWS
)
11017 if ((lpss
->styleOld
^ lpss
->styleNew
) & LVS_NOCOLUMNHEADER
)
11019 if (lpss
->styleNew
& LVS_NOCOLUMNHEADER
)
11021 /* Turn off the header control */
11022 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
11023 TRACE("Hide header control, was 0x%08x\n", style
);
11024 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
| HDS_HIDDEN
);
11026 /* Turn on the header control */
11027 if ((style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
)) & HDS_HIDDEN
)
11029 TRACE("Show header control, was 0x%08x\n", style
);
11030 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, (style
& ~HDS_HIDDEN
) | WS_VISIBLE
);
11036 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
11037 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
11038 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
11040 /* update the size of the client area */
11041 LISTVIEW_UpdateSize(infoPtr
);
11043 /* add scrollbars if needed */
11044 LISTVIEW_UpdateScroll(infoPtr
);
11046 /* invalidate client area + erase background */
11047 LISTVIEW_InvalidateList(infoPtr
);
11054 * Processes WM_STYLECHANGING messages.
11057 * [I] wStyleType : window style type (normal or extended)
11058 * [I0] lpss : window style information
11063 static INT
LISTVIEW_StyleChanging(WPARAM wStyleType
,
11066 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
11067 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
11069 /* don't forward LVS_OWNERDATA only if not already set to */
11070 if ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_OWNERDATA
)
11072 if (lpss
->styleOld
& LVS_OWNERDATA
)
11073 lpss
->styleNew
|= LVS_OWNERDATA
;
11075 lpss
->styleNew
&= ~LVS_OWNERDATA
;
11083 * Processes WM_SHOWWINDOW messages.
11086 * [I] infoPtr : valid pointer to the listview structure
11087 * [I] bShown : window is being shown (FALSE when hidden)
11088 * [I] iStatus : window show status
11093 static LRESULT
LISTVIEW_ShowWindow(LISTVIEW_INFO
*infoPtr
, WPARAM bShown
, LPARAM iStatus
)
11095 /* header delayed creation */
11096 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && bShown
)
11098 LISTVIEW_CreateHeader(infoPtr
);
11100 if (!(LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
))
11101 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
11104 return DefWindowProcW(infoPtr
->hwndSelf
, WM_SHOWWINDOW
, bShown
, iStatus
);
11109 * Processes CCM_GETVERSION messages.
11112 * [I] infoPtr : valid pointer to the listview structure
11117 static inline LRESULT
LISTVIEW_GetVersion(const LISTVIEW_INFO
*infoPtr
)
11119 return infoPtr
->iVersion
;
11124 * Processes CCM_SETVERSION messages.
11127 * [I] infoPtr : valid pointer to the listview structure
11128 * [I] iVersion : version to be set
11131 * -1 when requested version is greater than DLL version;
11132 * previous version otherwise
11134 static LRESULT
LISTVIEW_SetVersion(LISTVIEW_INFO
*infoPtr
, DWORD iVersion
)
11136 INT iOldVersion
= infoPtr
->iVersion
;
11138 if (iVersion
> COMCTL32_VERSION
)
11141 infoPtr
->iVersion
= iVersion
;
11143 TRACE("new version %d\n", iVersion
);
11145 return iOldVersion
;
11150 * Window procedure of the listview control.
11153 static LRESULT WINAPI
11154 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
11156 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
11158 TRACE("(hwnd=%p uMsg=%x wParam=%lx lParam=%lx)\n", hwnd
, uMsg
, wParam
, lParam
);
11160 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
11161 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11165 case LVM_APPROXIMATEVIEWRECT
:
11166 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
11167 LOWORD(lParam
), HIWORD(lParam
));
11169 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
11171 case LVM_CANCELEDITLABEL
:
11172 return LISTVIEW_CancelEditLabel(infoPtr
);
11174 case LVM_CREATEDRAGIMAGE
:
11175 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
11177 case LVM_DELETEALLITEMS
:
11178 return LISTVIEW_DeleteAllItems(infoPtr
, FALSE
);
11180 case LVM_DELETECOLUMN
:
11181 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
11183 case LVM_DELETEITEM
:
11184 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
11186 case LVM_EDITLABELA
:
11187 case LVM_EDITLABELW
:
11188 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
,
11189 uMsg
== LVM_EDITLABELW
);
11190 /* case LVM_ENABLEGROUPVIEW: */
11192 case LVM_ENSUREVISIBLE
:
11193 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
11195 case LVM_FINDITEMW
:
11196 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
11198 case LVM_FINDITEMA
:
11199 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
11201 case LVM_GETBKCOLOR
:
11202 return infoPtr
->clrBk
;
11204 /* case LVM_GETBKIMAGE: */
11206 case LVM_GETCALLBACKMASK
:
11207 return infoPtr
->uCallbackMask
;
11209 case LVM_GETCOLUMNA
:
11210 case LVM_GETCOLUMNW
:
11211 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
,
11212 uMsg
== LVM_GETCOLUMNW
);
11214 case LVM_GETCOLUMNORDERARRAY
:
11215 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
11217 case LVM_GETCOLUMNWIDTH
:
11218 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
11220 case LVM_GETCOUNTPERPAGE
:
11221 return LISTVIEW_GetCountPerPage(infoPtr
);
11223 case LVM_GETEDITCONTROL
:
11224 return (LRESULT
)infoPtr
->hwndEdit
;
11226 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
11227 return infoPtr
->dwLvExStyle
;
11229 /* case LVM_GETGROUPINFO: */
11231 /* case LVM_GETGROUPMETRICS: */
11233 case LVM_GETHEADER
:
11234 return (LRESULT
)infoPtr
->hwndHeader
;
11236 case LVM_GETHOTCURSOR
:
11237 return (LRESULT
)infoPtr
->hHotCursor
;
11239 case LVM_GETHOTITEM
:
11240 return infoPtr
->nHotItem
;
11242 case LVM_GETHOVERTIME
:
11243 return infoPtr
->dwHoverTime
;
11245 case LVM_GETIMAGELIST
:
11246 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
11248 /* case LVM_GETINSERTMARK: */
11250 /* case LVM_GETINSERTMARKCOLOR: */
11252 /* case LVM_GETINSERTMARKRECT: */
11254 case LVM_GETISEARCHSTRINGA
:
11255 case LVM_GETISEARCHSTRINGW
:
11256 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
11261 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, uMsg
== LVM_GETITEMW
);
11263 case LVM_GETITEMCOUNT
:
11264 return infoPtr
->nItemCount
;
11266 case LVM_GETITEMPOSITION
:
11267 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
11269 case LVM_GETITEMRECT
:
11270 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
11272 case LVM_GETITEMSPACING
:
11273 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
11275 case LVM_GETITEMSTATE
:
11276 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
11278 case LVM_GETITEMTEXTA
:
11279 case LVM_GETITEMTEXTW
:
11280 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
,
11281 uMsg
== LVM_GETITEMTEXTW
);
11283 case LVM_GETNEXTITEM
:
11284 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
11286 case LVM_GETNUMBEROFWORKAREAS
:
11287 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
11290 case LVM_GETORIGIN
:
11291 if (!lParam
) return FALSE
;
11292 if (infoPtr
->uView
== LV_VIEW_DETAILS
||
11293 infoPtr
->uView
== LV_VIEW_LIST
) return FALSE
;
11294 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
11297 /* case LVM_GETOUTLINECOLOR: */
11299 /* case LVM_GETSELECTEDCOLUMN: */
11301 case LVM_GETSELECTEDCOUNT
:
11302 return LISTVIEW_GetSelectedCount(infoPtr
);
11304 case LVM_GETSELECTIONMARK
:
11305 return infoPtr
->nSelectionMark
;
11307 case LVM_GETSTRINGWIDTHA
:
11308 case LVM_GETSTRINGWIDTHW
:
11309 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
,
11310 uMsg
== LVM_GETSTRINGWIDTHW
);
11312 case LVM_GETSUBITEMRECT
:
11313 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
11315 case LVM_GETTEXTBKCOLOR
:
11316 return infoPtr
->clrTextBk
;
11318 case LVM_GETTEXTCOLOR
:
11319 return infoPtr
->clrText
;
11321 /* case LVM_GETTILEINFO: */
11323 /* case LVM_GETTILEVIEWINFO: */
11325 case LVM_GETTOOLTIPS
:
11326 if( !infoPtr
->hwndToolTip
)
11327 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
11328 return (LRESULT
)infoPtr
->hwndToolTip
;
11330 case LVM_GETTOPINDEX
:
11331 return LISTVIEW_GetTopIndex(infoPtr
);
11333 case LVM_GETUNICODEFORMAT
:
11334 return (infoPtr
->notifyFormat
== NFR_UNICODE
);
11337 return infoPtr
->uView
;
11339 case LVM_GETVIEWRECT
:
11340 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
11342 case LVM_GETWORKAREAS
:
11343 FIXME("LVM_GETWORKAREAS: unimplemented\n");
11346 /* case LVM_HASGROUP: */
11349 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, TRUE
);
11351 case LVM_INSERTCOLUMNA
:
11352 case LVM_INSERTCOLUMNW
:
11353 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
,
11354 uMsg
== LVM_INSERTCOLUMNW
);
11356 /* case LVM_INSERTGROUP: */
11358 /* case LVM_INSERTGROUPSORTED: */
11360 case LVM_INSERTITEMA
:
11361 case LVM_INSERTITEMW
:
11362 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, uMsg
== LVM_INSERTITEMW
);
11364 /* case LVM_INSERTMARKHITTEST: */
11366 /* case LVM_ISGROUPVIEWENABLED: */
11368 case LVM_ISITEMVISIBLE
:
11369 return LISTVIEW_IsItemVisible(infoPtr
, (INT
)wParam
);
11371 case LVM_MAPIDTOINDEX
:
11372 return LISTVIEW_MapIdToIndex(infoPtr
, (UINT
)wParam
);
11374 case LVM_MAPINDEXTOID
:
11375 return LISTVIEW_MapIndexToId(infoPtr
, (INT
)wParam
);
11377 /* case LVM_MOVEGROUP: */
11379 /* case LVM_MOVEITEMTOGROUP: */
11381 case LVM_REDRAWITEMS
:
11382 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
11384 /* case LVM_REMOVEALLGROUPS: */
11386 /* case LVM_REMOVEGROUP: */
11389 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
11391 case LVM_SETBKCOLOR
:
11392 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
11394 /* case LVM_SETBKIMAGE: */
11396 case LVM_SETCALLBACKMASK
:
11397 infoPtr
->uCallbackMask
= (UINT
)wParam
;
11400 case LVM_SETCOLUMNA
:
11401 case LVM_SETCOLUMNW
:
11402 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
,
11403 uMsg
== LVM_SETCOLUMNW
);
11405 case LVM_SETCOLUMNORDERARRAY
:
11406 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
11408 case LVM_SETCOLUMNWIDTH
:
11409 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
11411 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
11412 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
11414 /* case LVM_SETGROUPINFO: */
11416 /* case LVM_SETGROUPMETRICS: */
11418 case LVM_SETHOTCURSOR
:
11419 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
11421 case LVM_SETHOTITEM
:
11422 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
11424 case LVM_SETHOVERTIME
:
11425 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)lParam
);
11427 case LVM_SETICONSPACING
:
11428 return LISTVIEW_SetIconSpacing(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
11430 case LVM_SETIMAGELIST
:
11431 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
11433 /* case LVM_SETINFOTIP: */
11435 /* case LVM_SETINSERTMARK: */
11437 /* case LVM_SETINSERTMARKCOLOR: */
11442 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
11443 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, (uMsg
== LVM_SETITEMW
));
11446 case LVM_SETITEMCOUNT
:
11447 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
11449 case LVM_SETITEMPOSITION
:
11452 pt
.x
= (short)LOWORD(lParam
);
11453 pt
.y
= (short)HIWORD(lParam
);
11454 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, &pt
);
11457 case LVM_SETITEMPOSITION32
:
11458 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, (POINT
*)lParam
);
11460 case LVM_SETITEMSTATE
:
11461 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
11463 case LVM_SETITEMTEXTA
:
11464 case LVM_SETITEMTEXTW
:
11465 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
,
11466 uMsg
== LVM_SETITEMTEXTW
);
11468 /* case LVM_SETOUTLINECOLOR: */
11470 /* case LVM_SETSELECTEDCOLUMN: */
11472 case LVM_SETSELECTIONMARK
:
11473 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
11475 case LVM_SETTEXTBKCOLOR
:
11476 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
11478 case LVM_SETTEXTCOLOR
:
11479 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
11481 /* case LVM_SETTILEINFO: */
11483 /* case LVM_SETTILEVIEWINFO: */
11485 /* case LVM_SETTILEWIDTH: */
11487 case LVM_SETTOOLTIPS
:
11488 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
11490 case LVM_SETUNICODEFORMAT
:
11491 return LISTVIEW_SetUnicodeFormat(infoPtr
, wParam
);
11494 return LISTVIEW_SetView(infoPtr
, wParam
);
11496 /* case LVM_SETWORKAREAS: */
11498 /* case LVM_SORTGROUPS: */
11500 case LVM_SORTITEMS
:
11501 case LVM_SORTITEMSEX
:
11502 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, wParam
,
11503 uMsg
== LVM_SORTITEMSEX
);
11504 case LVM_SUBITEMHITTEST
:
11505 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
11508 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
11510 case CCM_GETVERSION
:
11511 return LISTVIEW_GetVersion(infoPtr
);
11513 case CCM_SETVERSION
:
11514 return LISTVIEW_SetVersion(infoPtr
, wParam
);
11517 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
11520 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
11523 return LISTVIEW_NCCreate(hwnd
, (LPCREATESTRUCTW
)lParam
);
11526 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
11529 return LISTVIEW_Destroy(infoPtr
);
11532 return LISTVIEW_Enable(infoPtr
);
11534 case WM_ERASEBKGND
:
11535 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
11537 case WM_GETDLGCODE
:
11538 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
11541 return (LRESULT
)infoPtr
->hFont
;
11544 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0);
11547 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
11550 return LISTVIEW_KillFocus(infoPtr
);
11552 case WM_LBUTTONDBLCLK
:
11553 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11555 case WM_LBUTTONDOWN
:
11556 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11559 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11562 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11564 case WM_MOUSEHOVER
:
11565 return LISTVIEW_MouseHover(infoPtr
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11568 return LISTVIEW_NCDestroy(infoPtr
);
11571 return LISTVIEW_NCPaint(infoPtr
, (HRGN
)wParam
);
11574 return LISTVIEW_Notify(infoPtr
, (LPNMHDR
)lParam
);
11576 case WM_NOTIFYFORMAT
:
11577 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
11579 case WM_PRINTCLIENT
:
11580 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
11583 return LISTVIEW_WMPaint(infoPtr
, (HDC
)wParam
);
11585 case WM_RBUTTONDBLCLK
:
11586 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11588 case WM_RBUTTONDOWN
:
11589 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11592 return LISTVIEW_RButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11595 return LISTVIEW_SetCursor(infoPtr
, wParam
, lParam
);
11598 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
11601 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
11604 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
11606 case WM_SHOWWINDOW
:
11607 return LISTVIEW_ShowWindow(infoPtr
, wParam
, lParam
);
11609 case WM_STYLECHANGED
:
11610 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
11612 case WM_STYLECHANGING
:
11613 return LISTVIEW_StyleChanging(wParam
, (LPSTYLESTRUCT
)lParam
);
11615 case WM_SYSCOLORCHANGE
:
11616 COMCTL32_RefreshSysColors();
11617 if (infoPtr
->bDefaultBkColor
)
11619 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
11620 infoPtr
->bDefaultBkColor
= TRUE
;
11621 LISTVIEW_InvalidateList(infoPtr
);
11625 /* case WM_TIMER: */
11626 case WM_THEMECHANGED
:
11627 return LISTVIEW_ThemeChanged(infoPtr
);
11630 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0);
11632 case WM_MOUSEWHEEL
:
11633 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
11634 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11635 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
11637 case WM_WINDOWPOSCHANGED
:
11638 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
11640 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
11641 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
11643 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
11645 if (notify_measureitem(infoPtr
)) LISTVIEW_InvalidateList(infoPtr
);
11647 LISTVIEW_Size(infoPtr
, ((WINDOWPOS
*)lParam
)->cx
, ((WINDOWPOS
*)lParam
)->cy
);
11649 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11651 /* case WM_WININICHANGE: */
11654 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
) && !COMCTL32_IsReflectedMessage(uMsg
))
11655 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg
, wParam
, lParam
);
11657 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11664 * Registers the window class.
11672 void LISTVIEW_Register(void)
11674 WNDCLASSW wndClass
;
11676 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
11677 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
11678 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
11679 wndClass
.cbClsExtra
= 0;
11680 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
11681 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
11682 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
11683 wndClass
.lpszClassName
= WC_LISTVIEWW
;
11684 RegisterClassW(&wndClass
);
11689 * Unregisters the window class.
11697 void LISTVIEW_Unregister(void)
11699 UnregisterClassW(WC_LISTVIEWW
, NULL
);
11704 * Handle any WM_COMMAND messages
11707 * [I] infoPtr : valid pointer to the listview structure
11708 * [I] wParam : the first message parameter
11709 * [I] lParam : the second message parameter
11714 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
11717 TRACE("(%p %x %x %lx)\n", infoPtr
, HIWORD(wParam
), LOWORD(wParam
), lParam
);
11719 if (!infoPtr
->hwndEdit
) return 0;
11721 switch (HIWORD(wParam
))
11726 * Adjust the edit window size
11728 WCHAR buffer
[1024];
11729 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
11730 HFONT hFont
, hOldFont
= 0;
11734 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
11735 GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
11736 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
11738 /* Select font to get the right dimension of the string */
11739 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
11742 hOldFont
= SelectObject(hdc
, hFont
);
11745 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
11747 TEXTMETRICW textMetric
;
11749 /* Add Extra spacing for the next character */
11750 GetTextMetricsW(hdc
, &textMetric
);
11751 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
11753 SetWindowPos(infoPtr
->hwndEdit
, NULL
, 0, 0, sz
.cx
,
11754 rect
.bottom
- rect
.top
, SWP_DRAWFRAME
| SWP_NOMOVE
| SWP_NOZORDER
);
11757 SelectObject(hdc
, hOldFont
);
11759 ReleaseDC(infoPtr
->hwndEdit
, hdc
);
11765 LISTVIEW_CancelEditLabel(infoPtr
);
11770 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);