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-2015 Nikolay Sivov
10 * Copyright 2009 Owen Rudge for CodeWeavers
11 * Copyright 2012-2013 Daniel Jelinski
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 * Default Message Processing
30 * -- WM_CREATE: create the icon and small icon image lists at this point only if
31 * the LVS_SHAREIMAGELISTS style is not specified.
32 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
33 * or small icon and the LVS_AUTOARRANGE style is specified.
38 * -- Hot item handling, mouse hovering
39 * -- Workareas support
44 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
45 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
46 * -- LVA_SNAPTOGRID not implemented
47 * -- LISTVIEW_ApproximateViewRect partially implemented
48 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
51 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
52 * linear in the number of items in the list, and this is
53 * unacceptable for large lists.
54 * -- if list is sorted by item text LISTVIEW_InsertItemT could use
55 * binary search to calculate item index (e.g. DPA_Search()).
56 * This requires sorted state to be reliably tracked in item modifiers.
57 * -- we should keep an ordered array of coordinates in iconic mode.
58 * This would allow framing items (iterator_frameditems),
59 * and finding the nearest item (LVFI_NEARESTXY) a lot more efficiently.
66 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
71 * -- LVS_NOSCROLL (see Q137520)
75 * -- LVS_EX_BORDERSELECT
79 * -- LVS_EX_MULTIWORKAREAS
81 * -- LVS_EX_SIMPLESELECT
82 * -- LVS_EX_TWOCLICKACTIVATE
83 * -- LVS_EX_UNDERLINECOLD
84 * -- LVS_EX_UNDERLINEHOT
87 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
93 * -- LVM_ENABLEGROUPVIEW
94 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
95 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
96 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
97 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
98 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
99 * -- LVM_GETINSERTMARKRECT
100 * -- LVM_GETNUMBEROFWORKAREAS
101 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
102 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
103 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
104 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
105 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
106 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
107 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
108 * -- LVM_INSERTGROUPSORTED
109 * -- LVM_INSERTMARKHITTEST
110 * -- LVM_ISGROUPVIEWENABLED
112 * -- LVM_MOVEITEMTOGROUP
114 * -- LVM_SETTILEWIDTH
118 * -- ListView_GetHoverTime, ListView_SetHoverTime
119 * -- ListView_GetISearchString
120 * -- ListView_GetNumberOfWorkAreas
121 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
128 #include "wine/port.h"
143 #include "commctrl.h"
144 #include "comctl32.h"
147 #include "wine/debug.h"
148 #include "wine/unicode.h"
150 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
152 typedef struct tagCOLUMN_INFO
154 RECT rcHeader
; /* tracks the header's rectangle */
155 INT fmt
; /* same as LVCOLUMN.fmt */
159 typedef struct tagITEMHDR
163 } ITEMHDR
, *LPITEMHDR
;
165 typedef struct tagSUBITEM_INFO
171 typedef struct tagITEM_ID ITEM_ID
;
173 typedef struct tagITEM_INFO
184 UINT id
; /* item id */
185 HDPA item
; /* link to item data */
188 typedef struct tagRANGE
194 typedef struct tagRANGES
199 typedef struct tagITERATOR
208 typedef struct tagDELAYED_ITEM_EDIT
214 typedef struct tagLISTVIEW_INFO
218 RECT rcList
; /* This rectangle is really the window
219 * client rectangle possibly reduced by the
220 * horizontal scroll bar and/or header - see
221 * LISTVIEW_UpdateSize. This rectangle offset
222 * by the LISTVIEW_GetOrigin value is in
223 * client coordinates */
225 /* notification window */
228 BOOL bDoChangeNotify
; /* send change notification messages? */
235 INT nItemCount
; /* the number of items in the list */
236 HDPA hdpaItems
; /* array ITEM_INFO pointers */
237 HDPA hdpaItemIds
; /* array of ITEM_ID pointers */
238 HDPA hdpaPosX
; /* maintains the (X, Y) coordinates of the */
239 HDPA hdpaPosY
; /* items in LVS_ICON, and LVS_SMALLICON modes */
240 RANGES selectionRanges
;
241 INT nSelectionMark
; /* item to start next multiselection from */
245 HDPA hdpaColumns
; /* array of COLUMN_INFO pointers */
246 BOOL colRectsDirty
; /* trigger column rectangles requery from header */
249 BOOL bNoItemMetrics
; /* flags if item metrics are not yet computed */
254 PFNLVCOMPARE pfnCompare
; /* sorting callback pointer */
258 DWORD dwStyle
; /* the cached window GWL_STYLE */
259 DWORD dwLvExStyle
; /* extended listview style */
260 DWORD uView
; /* current view available through LVM_[G,S]ETVIEW */
266 DELAYED_ITEM_EDIT itemEdit
; /* Pointer to this structure will be the timer ID */
269 HIMAGELIST himlNormal
;
270 HIMAGELIST himlSmall
;
271 HIMAGELIST himlState
;
276 POINT currIconPos
; /* this is the position next icon will be placed */
280 INT xTrackLine
; /* The x coefficient of the track line or -1 if none */
282 /* marquee selection */
283 BOOL bMarqueeSelect
; /* marquee selection/highlight underway */
285 RECT marqueeRect
; /* absolute coordinates of marquee selection */
286 RECT marqueeDrawRect
; /* relative coordinates for drawing marquee */
287 POINT marqueeOrigin
; /* absolute coordinates of marquee click origin */
290 BOOL bFocus
; /* control has focus */
292 RECT rcFocus
; /* focus bounds */
303 INT ntmHeight
; /* Some cached metrics of the font used */
304 INT ntmMaxCharWidth
; /* by the listview to draw items */
307 /* mouse operation */
310 POINT ptClickPos
; /* point where the user clicked */
311 INT nLButtonDownItem
; /* tracks item to reset multiselection on WM_LBUTTONUP */
316 /* keyboard operation */
317 DWORD lastKeyPressTimestamp
;
319 INT nSearchParamLength
;
320 WCHAR szSearchParam
[ MAX_PATH
];
323 BOOL bIsDrawing
; /* Drawing in progress */
324 INT nMeasureItemHeight
; /* WM_MEASUREITEM result */
325 BOOL redraw
; /* WM_SETREDRAW switch */
328 DWORD iVersion
; /* CCM_[G,S]ETVERSION */
334 /* How many we debug buffer to allocate */
335 #define DEBUG_BUFFERS 20
336 /* The size of a single debug buffer */
337 #define DEBUG_BUFFER_SIZE 256
339 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
340 #define SB_INTERNAL -1
342 /* maximum size of a label */
343 #define DISP_TEXT_SIZE 260
345 /* padding for items in list and small icon display modes */
346 #define WIDTH_PADDING 12
348 /* padding for items in list, report and small icon display modes */
349 #define HEIGHT_PADDING 1
351 /* offset of items in report display mode */
352 #define REPORT_MARGINX 2
354 /* padding for icon in large icon display mode
355 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
356 * that HITTEST will see.
357 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
358 * ICON_TOP_PADDING - sum of the two above.
359 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
360 * LABEL_HOR_PADDING - between text and sides of box
361 * LABEL_VERT_PADDING - between bottom of text and end of box
363 * ICON_LR_PADDING - additional width above icon size.
364 * ICON_LR_HALF - half of the above value
366 #define ICON_TOP_PADDING_NOTHITABLE 2
367 #define ICON_TOP_PADDING_HITABLE 2
368 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
369 #define ICON_BOTTOM_PADDING 4
370 #define LABEL_HOR_PADDING 5
371 #define LABEL_VERT_PADDING 7
372 #define ICON_LR_PADDING 16
373 #define ICON_LR_HALF (ICON_LR_PADDING/2)
375 /* default label width for items in list and small icon display modes */
376 #define DEFAULT_LABEL_WIDTH 40
377 /* maximum select rectangle width for empty text item in LV_VIEW_DETAILS */
378 #define MAX_EMPTYTEXT_SELECT_WIDTH 80
380 /* default column width for items in list display mode */
381 #define DEFAULT_COLUMN_WIDTH 128
383 /* Size of "line" scroll for V & H scrolls */
384 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
386 /* Padding between image and label */
387 #define IMAGE_PADDING 2
389 /* Padding behind the label */
390 #define TRAILING_LABEL_PADDING 12
391 #define TRAILING_HEADER_PADDING 11
393 /* Border for the icon caption */
394 #define CAPTION_BORDER 2
396 /* Standard DrawText flags */
397 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
398 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
399 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
401 /* Image index from state */
402 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
404 /* The time in milliseconds to reset the search in the list */
405 #define KEY_DELAY 450
407 /* Dump the LISTVIEW_INFO structure to the debug channel */
408 #define LISTVIEW_DUMP(iP) do { \
409 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
410 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
411 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
412 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
413 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
414 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
415 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
416 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
417 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
418 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
421 static const WCHAR themeClass
[] = {'L','i','s','t','V','i','e','w',0};
424 * forward declarations
426 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
427 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*, INT
, LPRECT
);
428 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*, INT
, LPPOINT
);
429 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*, INT
, LPPOINT
);
430 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*, INT
, LPRECT
);
431 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*, LPPOINT
);
432 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*, LPRECT
);
433 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
434 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*, WPARAM
, LPARAM
);
435 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
436 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
, BOOL
);
437 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*, INT
, UINT
);
438 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, const LVITEMW
*);
439 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
);
440 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
);
441 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
442 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*, INT
, HIMAGELIST
);
443 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*, LPLVHITTESTINFO
, BOOL
, BOOL
);
444 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*, BOOL
, BOOL
);
445 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*, INT
, INT
);
447 /******** Text handling functions *************************************/
449 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
450 * text string. The string may be ANSI or Unicode, in which case
451 * the boolean isW tells us the type of the string.
453 * The name of the function tell what type of strings it expects:
454 * W: Unicode, T: ANSI/Unicode - function of isW
457 static inline BOOL
is_text(LPCWSTR text
)
459 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
462 static inline int textlenT(LPCWSTR text
, BOOL isW
)
464 return !is_text(text
) ? 0 :
465 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
468 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
471 if (isSrcW
) lstrcpynW(dest
, src
, max
);
472 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
474 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
475 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
478 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
480 LPWSTR wstr
= (LPWSTR
)text
;
482 if (!isW
&& is_text(text
))
484 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
485 wstr
= Alloc(len
* sizeof(WCHAR
));
486 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
488 TRACE(" wstr=%s\n", text
== LPSTR_TEXTCALLBACKW
? "(callback)" : debugstr_w(wstr
));
492 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
494 if (!isW
&& is_text(wstr
)) Free (wstr
);
498 * dest is a pointer to a Unicode string
499 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
501 static BOOL
textsetptrT(LPWSTR
*dest
, LPCWSTR src
, BOOL isW
)
505 if (src
== LPSTR_TEXTCALLBACKW
)
507 if (is_text(*dest
)) Free(*dest
);
508 *dest
= LPSTR_TEXTCALLBACKW
;
512 LPWSTR pszText
= textdupTtoW(src
, isW
);
513 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
514 bResult
= Str_SetPtrW(dest
, pszText
);
515 textfreeT(pszText
, isW
);
521 * compares a Unicode to a Unicode/ANSI text string
523 static inline int textcmpWT(LPCWSTR aw
, LPCWSTR bt
, BOOL isW
)
525 if (!aw
) return bt
? -1 : 0;
527 if (aw
== LPSTR_TEXTCALLBACKW
)
528 return bt
== LPSTR_TEXTCALLBACKW
? 1 : -1;
529 if (bt
!= LPSTR_TEXTCALLBACKW
)
531 LPWSTR bw
= textdupTtoW(bt
, isW
);
532 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
540 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
542 n
= min(min(n
, lstrlenW(s1
)), lstrlenW(s2
));
543 return CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
) - CSTR_EQUAL
;
546 /******** Debugging functions *****************************************/
548 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
550 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
551 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
554 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
556 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
557 n
= min(textlenT(text
, isW
), n
);
558 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
561 static char* debug_getbuf(void)
563 static int index
= 0;
564 static char buffers
[DEBUG_BUFFERS
][DEBUG_BUFFER_SIZE
];
565 return buffers
[index
++ % DEBUG_BUFFERS
];
568 static inline const char* debugrange(const RANGE
*lprng
)
570 if (!lprng
) return "(null)";
571 return wine_dbg_sprintf("[%d, %d]", lprng
->lower
, lprng
->upper
);
574 static const char* debugscrollinfo(const SCROLLINFO
*pScrollInfo
)
576 char* buf
= debug_getbuf(), *text
= buf
;
577 int len
, size
= DEBUG_BUFFER_SIZE
;
579 if (pScrollInfo
== NULL
) return "(null)";
580 len
= snprintf(buf
, size
, "{cbSize=%u, ", pScrollInfo
->cbSize
);
581 if (len
== -1) goto end
;
582 buf
+= len
; size
-= len
;
583 if (pScrollInfo
->fMask
& SIF_RANGE
)
584 len
= snprintf(buf
, size
, "nMin=%d, nMax=%d, ", pScrollInfo
->nMin
, pScrollInfo
->nMax
);
586 if (len
== -1) goto end
;
587 buf
+= len
; size
-= len
;
588 if (pScrollInfo
->fMask
& SIF_PAGE
)
589 len
= snprintf(buf
, size
, "nPage=%u, ", pScrollInfo
->nPage
);
591 if (len
== -1) goto end
;
592 buf
+= len
; size
-= len
;
593 if (pScrollInfo
->fMask
& SIF_POS
)
594 len
= snprintf(buf
, size
, "nPos=%d, ", pScrollInfo
->nPos
);
596 if (len
== -1) goto end
;
597 buf
+= len
; size
-= len
;
598 if (pScrollInfo
->fMask
& SIF_TRACKPOS
)
599 len
= snprintf(buf
, size
, "nTrackPos=%d, ", pScrollInfo
->nTrackPos
);
601 if (len
== -1) goto end
;
605 buf
= text
+ strlen(text
);
607 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
611 static const char* debugnmlistview(const NMLISTVIEW
*plvnm
)
613 if (!plvnm
) return "(null)";
614 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
615 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
616 plvnm
->iItem
, plvnm
->iSubItem
, plvnm
->uNewState
, plvnm
->uOldState
,
617 plvnm
->uChanged
, wine_dbgstr_point(&plvnm
->ptAction
), plvnm
->lParam
);
620 static const char* debuglvitem_t(const LVITEMW
*lpLVItem
, BOOL isW
)
622 char* buf
= debug_getbuf(), *text
= buf
;
623 int len
, size
= DEBUG_BUFFER_SIZE
;
625 if (lpLVItem
== NULL
) return "(null)";
626 len
= snprintf(buf
, size
, "{iItem=%d, iSubItem=%d, ", lpLVItem
->iItem
, lpLVItem
->iSubItem
);
627 if (len
== -1) goto end
;
628 buf
+= len
; size
-= len
;
629 if (lpLVItem
->mask
& LVIF_STATE
)
630 len
= snprintf(buf
, size
, "state=%x, stateMask=%x, ", lpLVItem
->state
, lpLVItem
->stateMask
);
632 if (len
== -1) goto end
;
633 buf
+= len
; size
-= len
;
634 if (lpLVItem
->mask
& LVIF_TEXT
)
635 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem
->pszText
, isW
, 80), lpLVItem
->cchTextMax
);
637 if (len
== -1) goto end
;
638 buf
+= len
; size
-= len
;
639 if (lpLVItem
->mask
& LVIF_IMAGE
)
640 len
= snprintf(buf
, size
, "iImage=%d, ", lpLVItem
->iImage
);
642 if (len
== -1) goto end
;
643 buf
+= len
; size
-= len
;
644 if (lpLVItem
->mask
& LVIF_PARAM
)
645 len
= snprintf(buf
, size
, "lParam=%lx, ", lpLVItem
->lParam
);
647 if (len
== -1) goto end
;
648 buf
+= len
; size
-= len
;
649 if (lpLVItem
->mask
& LVIF_INDENT
)
650 len
= snprintf(buf
, size
, "iIndent=%d, ", lpLVItem
->iIndent
);
652 if (len
== -1) goto end
;
656 buf
= text
+ strlen(text
);
658 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
662 static const char* debuglvcolumn_t(const LVCOLUMNW
*lpColumn
, BOOL isW
)
664 char* buf
= debug_getbuf(), *text
= buf
;
665 int len
, size
= DEBUG_BUFFER_SIZE
;
667 if (lpColumn
== NULL
) return "(null)";
668 len
= snprintf(buf
, size
, "{");
669 if (len
== -1) goto end
;
670 buf
+= len
; size
-= len
;
671 if (lpColumn
->mask
& LVCF_SUBITEM
)
672 len
= snprintf(buf
, size
, "iSubItem=%d, ", lpColumn
->iSubItem
);
674 if (len
== -1) goto end
;
675 buf
+= len
; size
-= len
;
676 if (lpColumn
->mask
& LVCF_FMT
)
677 len
= snprintf(buf
, size
, "fmt=%x, ", lpColumn
->fmt
);
679 if (len
== -1) goto end
;
680 buf
+= len
; size
-= len
;
681 if (lpColumn
->mask
& LVCF_WIDTH
)
682 len
= snprintf(buf
, size
, "cx=%d, ", lpColumn
->cx
);
684 if (len
== -1) goto end
;
685 buf
+= len
; size
-= len
;
686 if (lpColumn
->mask
& LVCF_TEXT
)
687 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn
->pszText
, isW
, 80), lpColumn
->cchTextMax
);
689 if (len
== -1) goto end
;
690 buf
+= len
; size
-= len
;
691 if (lpColumn
->mask
& LVCF_IMAGE
)
692 len
= snprintf(buf
, size
, "iImage=%d, ", lpColumn
->iImage
);
694 if (len
== -1) goto end
;
695 buf
+= len
; size
-= len
;
696 if (lpColumn
->mask
& LVCF_ORDER
)
697 len
= snprintf(buf
, size
, "iOrder=%d, ", lpColumn
->iOrder
);
699 if (len
== -1) goto end
;
703 buf
= text
+ strlen(text
);
705 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
709 static const char* debuglvhittestinfo(const LVHITTESTINFO
*lpht
)
711 if (!lpht
) return "(null)";
713 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
714 wine_dbgstr_point(&lpht
->pt
), lpht
->flags
, lpht
->iItem
, lpht
->iSubItem
);
717 /* Return the corresponding text for a given scroll value */
718 static inline LPCSTR
debugscrollcode(int nScrollCode
)
722 case SB_LINELEFT
: return "SB_LINELEFT";
723 case SB_LINERIGHT
: return "SB_LINERIGHT";
724 case SB_PAGELEFT
: return "SB_PAGELEFT";
725 case SB_PAGERIGHT
: return "SB_PAGERIGHT";
726 case SB_THUMBPOSITION
: return "SB_THUMBPOSITION";
727 case SB_THUMBTRACK
: return "SB_THUMBTRACK";
728 case SB_ENDSCROLL
: return "SB_ENDSCROLL";
729 case SB_INTERNAL
: return "SB_INTERNAL";
730 default: return "unknown";
735 /******** Notification functions ************************************/
737 static int get_ansi_notification(UINT unicodeNotificationCode
)
739 switch (unicodeNotificationCode
)
741 case LVN_BEGINLABELEDITA
:
742 case LVN_BEGINLABELEDITW
: return LVN_BEGINLABELEDITA
;
743 case LVN_ENDLABELEDITA
:
744 case LVN_ENDLABELEDITW
: return LVN_ENDLABELEDITA
;
745 case LVN_GETDISPINFOA
:
746 case LVN_GETDISPINFOW
: return LVN_GETDISPINFOA
;
747 case LVN_SETDISPINFOA
:
748 case LVN_SETDISPINFOW
: return LVN_SETDISPINFOA
;
749 case LVN_ODFINDITEMA
:
750 case LVN_ODFINDITEMW
: return LVN_ODFINDITEMA
;
751 case LVN_GETINFOTIPA
:
752 case LVN_GETINFOTIPW
: return LVN_GETINFOTIPA
;
753 /* header forwards */
755 case HDN_TRACKW
: return HDN_TRACKA
;
757 case HDN_ENDTRACKW
: return HDN_ENDTRACKA
;
758 case HDN_BEGINDRAG
: return HDN_BEGINDRAG
;
759 case HDN_ENDDRAG
: return HDN_ENDDRAG
;
760 case HDN_ITEMCHANGINGA
:
761 case HDN_ITEMCHANGINGW
: return HDN_ITEMCHANGINGA
;
762 case HDN_ITEMCHANGEDA
:
763 case HDN_ITEMCHANGEDW
: return HDN_ITEMCHANGEDA
;
765 case HDN_ITEMCLICKW
: return HDN_ITEMCLICKA
;
766 case HDN_DIVIDERDBLCLICKA
:
767 case HDN_DIVIDERDBLCLICKW
: return HDN_DIVIDERDBLCLICKA
;
770 FIXME("unknown notification %x\n", unicodeNotificationCode
);
771 return unicodeNotificationCode
;
774 /* forwards header notifications to listview parent */
775 static LRESULT
notify_forward_header(const LISTVIEW_INFO
*infoPtr
, NMHEADERW
*lpnmhW
)
777 LPCWSTR text
= NULL
, filter
= NULL
;
779 NMHEADERA
*lpnmh
= (NMHEADERA
*) lpnmhW
;
781 /* on unicode format exit earlier */
782 if (infoPtr
->notifyFormat
== NFR_UNICODE
)
783 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, lpnmh
->hdr
.idFrom
,
786 /* header always supplies unicode notifications,
787 all we have to do is to convert strings to ANSI */
790 /* convert item text */
791 if (lpnmh
->pitem
->mask
& HDI_TEXT
)
793 text
= (LPCWSTR
)lpnmh
->pitem
->pszText
;
794 lpnmh
->pitem
->pszText
= NULL
;
795 Str_SetPtrWtoA(&lpnmh
->pitem
->pszText
, text
);
797 /* convert filter text */
798 if ((lpnmh
->pitem
->mask
& HDI_FILTER
) && (lpnmh
->pitem
->type
== HDFT_ISSTRING
) &&
799 lpnmh
->pitem
->pvFilter
)
801 filter
= (LPCWSTR
)((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
;
802 ((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
= NULL
;
803 Str_SetPtrWtoA(&((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
, filter
);
806 lpnmh
->hdr
.code
= get_ansi_notification(lpnmh
->hdr
.code
);
808 ret
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, lpnmh
->hdr
.idFrom
,
814 Free(lpnmh
->pitem
->pszText
);
815 lpnmh
->pitem
->pszText
= (LPSTR
)text
;
819 Free(((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
);
820 ((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
= (LPSTR
)filter
;
826 static LRESULT
notify_hdr(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
830 TRACE("(code=%d)\n", code
);
832 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
833 pnmh
->idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
835 result
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, pnmh
->idFrom
, (LPARAM
)pnmh
);
837 TRACE(" <= %ld\n", result
);
842 static inline BOOL
notify(const LISTVIEW_INFO
*infoPtr
, INT code
)
845 HWND hwnd
= infoPtr
->hwndSelf
;
846 notify_hdr(infoPtr
, code
, &nmh
);
847 return IsWindow(hwnd
);
850 static inline void notify_itemactivate(const LISTVIEW_INFO
*infoPtr
, const LVHITTESTINFO
*htInfo
)
860 item
.mask
= LVIF_PARAM
|LVIF_STATE
;
861 item
.iItem
= htInfo
->iItem
;
863 item
.stateMask
= (UINT
)-1;
864 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) {
865 nmia
.lParam
= item
.lParam
;
866 nmia
.uOldState
= item
.state
;
867 nmia
.uNewState
= item
.state
| LVIS_ACTIVATING
;
868 nmia
.uChanged
= LVIF_STATE
;
871 nmia
.iItem
= htInfo
->iItem
;
872 nmia
.iSubItem
= htInfo
->iSubItem
;
873 nmia
.ptAction
= htInfo
->pt
;
875 if (GetKeyState(VK_SHIFT
) & 0x8000) nmia
.uKeyFlags
|= LVKF_SHIFT
;
876 if (GetKeyState(VK_CONTROL
) & 0x8000) nmia
.uKeyFlags
|= LVKF_CONTROL
;
877 if (GetKeyState(VK_MENU
) & 0x8000) nmia
.uKeyFlags
|= LVKF_ALT
;
879 notify_hdr(infoPtr
, LVN_ITEMACTIVATE
, (LPNMHDR
)&nmia
);
882 static inline LRESULT
notify_listview(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
884 TRACE("(code=%d, plvnm=%s)\n", code
, debugnmlistview(plvnm
));
885 return notify_hdr(infoPtr
, code
, (LPNMHDR
)plvnm
);
888 /* Handles NM_DBLCLK, NM_CLICK, NM_RDBLCLK, NM_RCLICK. Only NM_RCLICK return value is used. */
889 static BOOL
notify_click(const LISTVIEW_INFO
*infoPtr
, INT code
, const LVHITTESTINFO
*lvht
)
893 HWND hwnd
= infoPtr
->hwndSelf
;
896 TRACE("code=%d, lvht=%s\n", code
, debuglvhittestinfo(lvht
));
897 ZeroMemory(&nmia
, sizeof(nmia
));
898 nmia
.iItem
= lvht
->iItem
;
899 nmia
.iSubItem
= lvht
->iSubItem
;
900 nmia
.ptAction
= lvht
->pt
;
901 item
.mask
= LVIF_PARAM
;
902 item
.iItem
= lvht
->iItem
;
904 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmia
.lParam
= item
.lParam
;
905 ret
= notify_hdr(infoPtr
, code
, (NMHDR
*)&nmia
);
906 return IsWindow(hwnd
) && (code
== NM_RCLICK
? !ret
: TRUE
);
909 static BOOL
notify_deleteitem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
913 HWND hwnd
= infoPtr
->hwndSelf
;
915 ZeroMemory(&nmlv
, sizeof (NMLISTVIEW
));
917 item
.mask
= LVIF_PARAM
;
920 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
921 notify_listview(infoPtr
, LVN_DELETEITEM
, &nmlv
);
922 return IsWindow(hwnd
);
926 Send notification. depends on dispinfoW having same
927 structure as dispinfoA.
928 infoPtr : listview struct
929 code : *Unicode* notification code
930 pdi : dispinfo structure (can be unicode or ansi)
931 isW : TRUE if dispinfo is Unicode
933 static BOOL
notify_dispinfoT(const LISTVIEW_INFO
*infoPtr
, UINT code
, LPNMLVDISPINFOW pdi
, BOOL isW
)
935 INT length
= 0, ret_length
;
936 LPWSTR buffer
= NULL
, ret_text
;
937 BOOL return_ansi
= FALSE
;
938 BOOL return_unicode
= FALSE
;
941 if ((pdi
->item
.mask
& LVIF_TEXT
) && is_text(pdi
->item
.pszText
))
943 return_unicode
= ( isW
&& infoPtr
->notifyFormat
== NFR_ANSI
);
944 return_ansi
= (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
);
947 ret_length
= pdi
->item
.cchTextMax
;
948 ret_text
= pdi
->item
.pszText
;
950 if (return_unicode
|| return_ansi
)
952 if (code
!= LVN_GETDISPINFOW
)
954 length
= return_ansi
?
955 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
956 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
960 length
= pdi
->item
.cchTextMax
;
961 *pdi
->item
.pszText
= 0; /* make sure we don't process garbage */
964 buffer
= Alloc( (return_ansi
? sizeof(WCHAR
) : sizeof(CHAR
)) * length
);
965 if (!buffer
) return FALSE
;
968 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
971 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) buffer
,
974 pdi
->item
.pszText
= buffer
;
975 pdi
->item
.cchTextMax
= length
;
978 if (infoPtr
->notifyFormat
== NFR_ANSI
)
979 code
= get_ansi_notification(code
);
981 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi
->item
, infoPtr
->notifyFormat
!= NFR_ANSI
));
982 ret
= notify_hdr(infoPtr
, code
, &pdi
->hdr
);
983 TRACE(" resulting code=%d\n", pdi
->hdr
.code
);
985 if (return_ansi
|| return_unicode
)
987 if (return_ansi
&& (pdi
->hdr
.code
== LVN_GETDISPINFOA
))
989 strcpy((char*)ret_text
, (char*)pdi
->item
.pszText
);
991 else if (return_unicode
&& (pdi
->hdr
.code
== LVN_GETDISPINFOW
))
993 strcpyW(ret_text
, pdi
->item
.pszText
);
995 else if (return_ansi
) /* note : pointer can be changed by app ! */
997 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) ret_text
,
998 ret_length
, NULL
, NULL
);
1001 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
1002 ret_text
, ret_length
);
1004 pdi
->item
.pszText
= ret_text
; /* restores our buffer */
1005 pdi
->item
.cchTextMax
= ret_length
;
1011 /* if dispinfo holder changed notification code then convert */
1012 if (!isW
&& (pdi
->hdr
.code
== LVN_GETDISPINFOW
) && (pdi
->item
.mask
& LVIF_TEXT
))
1014 length
= WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
1016 buffer
= Alloc(length
* sizeof(CHAR
));
1017 if (!buffer
) return FALSE
;
1019 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) buffer
,
1020 ret_length
, NULL
, NULL
);
1022 strcpy((LPSTR
)pdi
->item
.pszText
, (LPSTR
)buffer
);
1029 static void customdraw_fill(NMLVCUSTOMDRAW
*lpnmlvcd
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
,
1030 const RECT
*rcBounds
, const LVITEMW
*lplvItem
)
1032 ZeroMemory(lpnmlvcd
, sizeof(NMLVCUSTOMDRAW
));
1033 lpnmlvcd
->nmcd
.hdc
= hdc
;
1034 lpnmlvcd
->nmcd
.rc
= *rcBounds
;
1035 lpnmlvcd
->clrTextBk
= infoPtr
->clrTextBk
;
1036 lpnmlvcd
->clrText
= infoPtr
->clrText
;
1037 if (!lplvItem
) return;
1038 lpnmlvcd
->nmcd
.dwItemSpec
= lplvItem
->iItem
+ 1;
1039 lpnmlvcd
->iSubItem
= lplvItem
->iSubItem
;
1040 if (lplvItem
->state
& LVIS_SELECTED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_SELECTED
;
1041 if (lplvItem
->state
& LVIS_FOCUSED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_FOCUS
;
1042 if (lplvItem
->iItem
== infoPtr
->nHotItem
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_HOT
;
1043 lpnmlvcd
->nmcd
.lItemlParam
= lplvItem
->lParam
;
1046 static inline DWORD
notify_customdraw (const LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, NMLVCUSTOMDRAW
*lpnmlvcd
)
1048 BOOL isForItem
= (lpnmlvcd
->nmcd
.dwItemSpec
!= 0);
1051 lpnmlvcd
->nmcd
.dwDrawStage
= dwDrawStage
;
1052 if (isForItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_ITEM
;
1053 if (lpnmlvcd
->iSubItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_SUBITEM
;
1054 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
--;
1055 result
= notify_hdr(infoPtr
, NM_CUSTOMDRAW
, &lpnmlvcd
->nmcd
.hdr
);
1056 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
++;
1060 static void prepaint_setup (const LISTVIEW_INFO
*infoPtr
, HDC hdc
, NMLVCUSTOMDRAW
*lpnmlvcd
, BOOL SubItem
)
1062 COLORREF backcolor
, textcolor
;
1064 /* apparently, for selected items, we have to override the returned values */
1067 if (lpnmlvcd
->nmcd
.uItemState
& CDIS_SELECTED
)
1069 if (infoPtr
->bFocus
)
1071 lpnmlvcd
->clrTextBk
= comctl32_color
.clrHighlight
;
1072 lpnmlvcd
->clrText
= comctl32_color
.clrHighlightText
;
1074 else if (infoPtr
->dwStyle
& LVS_SHOWSELALWAYS
)
1076 lpnmlvcd
->clrTextBk
= comctl32_color
.clr3dFace
;
1077 lpnmlvcd
->clrText
= comctl32_color
.clrBtnText
;
1082 backcolor
= lpnmlvcd
->clrTextBk
;
1083 textcolor
= lpnmlvcd
->clrText
;
1085 if (backcolor
== CLR_DEFAULT
)
1086 backcolor
= comctl32_color
.clrWindow
;
1087 if (textcolor
== CLR_DEFAULT
)
1088 textcolor
= comctl32_color
.clrWindowText
;
1090 /* Set the text attributes */
1091 if (backcolor
!= CLR_NONE
)
1093 SetBkMode(hdc
, OPAQUE
);
1094 SetBkColor(hdc
, backcolor
);
1097 SetBkMode(hdc
, TRANSPARENT
);
1098 SetTextColor(hdc
, textcolor
);
1101 static inline DWORD
notify_postpaint (const LISTVIEW_INFO
*infoPtr
, NMLVCUSTOMDRAW
*lpnmlvcd
)
1103 return notify_customdraw(infoPtr
, CDDS_POSTPAINT
, lpnmlvcd
);
1106 /* returns TRUE when repaint needed, FALSE otherwise */
1107 static BOOL
notify_measureitem(LISTVIEW_INFO
*infoPtr
)
1109 MEASUREITEMSTRUCT mis
;
1110 mis
.CtlType
= ODT_LISTVIEW
;
1111 mis
.CtlID
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1115 mis
.itemHeight
= infoPtr
->nItemHeight
;
1116 SendMessageW(infoPtr
->hwndNotify
, WM_MEASUREITEM
, mis
.CtlID
, (LPARAM
)&mis
);
1117 if (infoPtr
->nItemHeight
!= max(mis
.itemHeight
, 1))
1119 infoPtr
->nMeasureItemHeight
= infoPtr
->nItemHeight
= max(mis
.itemHeight
, 1);
1125 /******** Item iterator functions **********************************/
1127 static RANGES
ranges_create(int count
);
1128 static void ranges_destroy(RANGES ranges
);
1129 static BOOL
ranges_add(RANGES ranges
, RANGE range
);
1130 static BOOL
ranges_del(RANGES ranges
, RANGE range
);
1131 static void ranges_dump(RANGES ranges
);
1133 static inline BOOL
ranges_additem(RANGES ranges
, INT nItem
)
1135 RANGE range
= { nItem
, nItem
+ 1 };
1137 return ranges_add(ranges
, range
);
1140 static inline BOOL
ranges_delitem(RANGES ranges
, INT nItem
)
1142 RANGE range
= { nItem
, nItem
+ 1 };
1144 return ranges_del(ranges
, range
);
1148 * ITERATOR DOCUMENTATION
1150 * The iterator functions allow for easy, and convenient iteration
1151 * over items of interest in the list. Typically, you create an
1152 * iterator, use it, and destroy it, as such:
1155 * iterator_xxxitems(&i, ...);
1156 * while (iterator_{prev,next}(&i)
1158 * //code which uses i.nItem
1160 * iterator_destroy(&i);
1162 * where xxx is either: framed, or visible.
1163 * Note that it is important that the code destroys the iterator
1164 * after it's done with it, as the creation of the iterator may
1165 * allocate memory, which thus needs to be freed.
1167 * You can iterate both forwards, and backwards through the list,
1168 * by using iterator_next or iterator_prev respectively.
1170 * Lower numbered items are draw on top of higher number items in
1171 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1172 * items may overlap). So, to test items, you should use
1174 * which lists the items top to bottom (in Z-order).
1175 * For drawing items, you should use
1177 * which lists the items bottom to top (in Z-order).
1178 * If you keep iterating over the items after the end-of-items
1179 * marker (-1) is returned, the iterator will start from the
1180 * beginning. Typically, you don't need to test for -1,
1181 * because iterator_{next,prev} will return TRUE if more items
1182 * are to be iterated over, or FALSE otherwise.
1184 * Note: the iterator is defined to be bidirectional. That is,
1185 * any number of prev followed by any number of next, or
1186 * five versa, should leave the iterator at the same item:
1187 * prev * n, next * n = next * n, prev * n
1189 * The iterator has a notion of an out-of-order, special item,
1190 * which sits at the start of the list. This is used in
1191 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1192 * which needs to be first, as it may overlap other items.
1194 * The code is a bit messy because we have:
1195 * - a special item to deal with
1196 * - simple range, or composite range
1198 * If you find bugs, or want to add features, please make sure you
1199 * always check/modify *both* iterator_prev, and iterator_next.
1203 * This function iterates through the items in increasing order,
1204 * but prefixed by the special item, then -1. That is:
1205 * special, 1, 2, 3, ..., n, -1.
1206 * Each item is listed only once.
1208 static inline BOOL
iterator_next(ITERATOR
* i
)
1212 i
->nItem
= i
->nSpecial
;
1213 if (i
->nItem
!= -1) return TRUE
;
1215 if (i
->nItem
== i
->nSpecial
)
1217 if (i
->ranges
) i
->index
= 0;
1223 if (i
->nItem
== i
->nSpecial
) i
->nItem
++;
1224 if (i
->nItem
< i
->range
.upper
) return TRUE
;
1229 if (i
->index
< DPA_GetPtrCount(i
->ranges
->hdpa
))
1230 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, i
->index
++);
1233 else if (i
->nItem
>= i
->range
.upper
) goto end
;
1235 i
->nItem
= i
->range
.lower
;
1236 if (i
->nItem
>= 0) goto testitem
;
1243 * This function iterates through the items in decreasing order,
1244 * followed by the special item, then -1. That is:
1245 * n, n-1, ..., 3, 2, 1, special, -1.
1246 * Each item is listed only once.
1248 static inline BOOL
iterator_prev(ITERATOR
* i
)
1255 if (i
->ranges
) i
->index
= DPA_GetPtrCount(i
->ranges
->hdpa
);
1258 if (i
->nItem
== i
->nSpecial
)
1266 if (i
->nItem
== i
->nSpecial
) i
->nItem
--;
1267 if (i
->nItem
>= i
->range
.lower
) return TRUE
;
1273 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, --i
->index
);
1276 else if (!start
&& i
->nItem
< i
->range
.lower
) goto end
;
1278 i
->nItem
= i
->range
.upper
;
1279 if (i
->nItem
> 0) goto testitem
;
1281 return (i
->nItem
= i
->nSpecial
) != -1;
1284 static RANGE
iterator_range(const ITERATOR
*i
)
1288 if (!i
->ranges
) return i
->range
;
1290 if (DPA_GetPtrCount(i
->ranges
->hdpa
) > 0)
1292 range
.lower
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, 0)).lower
;
1293 range
.upper
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, DPA_GetPtrCount(i
->ranges
->hdpa
) - 1)).upper
;
1295 else range
.lower
= range
.upper
= 0;
1301 * Releases resources associated with this iterator.
1303 static inline void iterator_destroy(const ITERATOR
*i
)
1305 ranges_destroy(i
->ranges
);
1309 * Create an empty iterator.
1311 static inline BOOL
iterator_empty(ITERATOR
* i
)
1313 ZeroMemory(i
, sizeof(*i
));
1314 i
->nItem
= i
->nSpecial
= i
->range
.lower
= i
->range
.upper
= -1;
1319 * Create an iterator over a range.
1321 static inline BOOL
iterator_rangeitems(ITERATOR
* i
, RANGE range
)
1329 * Create an iterator over a bunch of ranges.
1330 * Please note that the iterator will take ownership of the ranges,
1331 * and will free them upon destruction.
1333 static inline BOOL
iterator_rangesitems(ITERATOR
* i
, RANGES ranges
)
1341 * Creates an iterator over the items which intersect frame.
1342 * Uses absolute coordinates rather than compensating for the current offset.
1344 static BOOL
iterator_frameditems_absolute(ITERATOR
* i
, const LISTVIEW_INFO
* infoPtr
, const RECT
*frame
)
1346 RECT rcItem
, rcTemp
;
1348 /* in case we fail, we want to return an empty iterator */
1349 if (!iterator_empty(i
)) return FALSE
;
1351 TRACE("(frame=%s)\n", wine_dbgstr_rect(frame
));
1353 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)
1357 if (infoPtr
->uView
== LV_VIEW_ICON
&& infoPtr
->nFocusedItem
!= -1)
1359 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcItem
);
1360 if (IntersectRect(&rcTemp
, &rcItem
, frame
))
1361 i
->nSpecial
= infoPtr
->nFocusedItem
;
1363 if (!(iterator_rangesitems(i
, ranges_create(50)))) return FALSE
;
1364 /* to do better here, we need to have PosX, and PosY sorted */
1365 TRACE("building icon ranges:\n");
1366 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
1368 rcItem
.left
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1369 rcItem
.top
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1370 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1371 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1372 if (IntersectRect(&rcTemp
, &rcItem
, frame
))
1373 ranges_additem(i
->ranges
, nItem
);
1377 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
1381 if (frame
->left
>= infoPtr
->nItemWidth
) return TRUE
;
1382 if (frame
->top
>= infoPtr
->nItemHeight
* infoPtr
->nItemCount
) return TRUE
;
1384 range
.lower
= max(frame
->top
/ infoPtr
->nItemHeight
, 0);
1385 range
.upper
= min((frame
->bottom
- 1) / infoPtr
->nItemHeight
, infoPtr
->nItemCount
- 1) + 1;
1386 if (range
.upper
<= range
.lower
) return TRUE
;
1387 if (!iterator_rangeitems(i
, range
)) return FALSE
;
1388 TRACE(" report=%s\n", debugrange(&i
->range
));
1392 INT nPerCol
= max((infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
, 1);
1393 INT nFirstRow
= max(frame
->top
/ infoPtr
->nItemHeight
, 0);
1394 INT nLastRow
= min((frame
->bottom
- 1) / infoPtr
->nItemHeight
, nPerCol
- 1);
1401 if (infoPtr
->nItemWidth
)
1403 nFirstCol
= max(frame
->left
/ infoPtr
->nItemWidth
, 0);
1404 nLastCol
= min((frame
->right
- 1) / infoPtr
->nItemWidth
, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1408 nFirstCol
= max(frame
->left
, 0);
1409 nLastCol
= min(frame
->right
- 1, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1412 lower
= nFirstCol
* nPerCol
+ nFirstRow
;
1414 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1415 nPerCol
, nFirstRow
, nLastRow
, nFirstCol
, nLastCol
, lower
);
1417 if (nLastCol
< nFirstCol
|| nLastRow
< nFirstRow
) return TRUE
;
1419 if (!(iterator_rangesitems(i
, ranges_create(nLastCol
- nFirstCol
+ 1)))) return FALSE
;
1420 TRACE("building list ranges:\n");
1421 for (nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++)
1423 item_range
.lower
= nCol
* nPerCol
+ nFirstRow
;
1424 if(item_range
.lower
>= infoPtr
->nItemCount
) break;
1425 item_range
.upper
= min(nCol
* nPerCol
+ nLastRow
+ 1, infoPtr
->nItemCount
);
1426 TRACE(" list=%s\n", debugrange(&item_range
));
1427 ranges_add(i
->ranges
, item_range
);
1435 * Creates an iterator over the items which intersect lprc.
1437 static BOOL
iterator_frameditems(ITERATOR
* i
, const LISTVIEW_INFO
* infoPtr
, const RECT
*lprc
)
1442 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc
));
1444 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1445 OffsetRect(&frame
, -Origin
.x
, -Origin
.y
);
1447 return iterator_frameditems_absolute(i
, infoPtr
, &frame
);
1451 * Creates an iterator over the items which intersect the visible region of hdc.
1453 static BOOL
iterator_visibleitems(ITERATOR
*i
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1455 POINT Origin
, Position
;
1456 RECT rcItem
, rcClip
;
1459 rgntype
= GetClipBox(hdc
, &rcClip
);
1460 if (rgntype
== NULLREGION
) return iterator_empty(i
);
1461 if (!iterator_frameditems(i
, infoPtr
, &rcClip
)) return FALSE
;
1462 if (rgntype
== SIMPLEREGION
) return TRUE
;
1464 /* first deal with the special item */
1465 if (i
->nSpecial
!= -1)
1467 LISTVIEW_GetItemBox(infoPtr
, i
->nSpecial
, &rcItem
);
1468 if (!RectVisible(hdc
, &rcItem
)) i
->nSpecial
= -1;
1471 /* if we can't deal with the region, we'll just go with the simple range */
1472 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1473 TRACE("building visible range:\n");
1474 if (!i
->ranges
&& i
->range
.lower
< i
->range
.upper
)
1476 if (!(i
->ranges
= ranges_create(50))) return TRUE
;
1477 if (!ranges_add(i
->ranges
, i
->range
))
1479 ranges_destroy(i
->ranges
);
1485 /* now delete the invisible items from the list */
1486 while(iterator_next(i
))
1488 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
1489 rcItem
.left
= (infoPtr
->uView
== LV_VIEW_DETAILS
) ? Origin
.x
: Position
.x
+ Origin
.x
;
1490 rcItem
.top
= Position
.y
+ Origin
.y
;
1491 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1492 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1493 if (!RectVisible(hdc
, &rcItem
))
1494 ranges_delitem(i
->ranges
, i
->nItem
);
1496 /* the iterator should restart on the next iterator_next */
1502 /* Remove common elements from two iterators */
1503 /* Passed iterators have to point on the first elements */
1504 static BOOL
iterator_remove_common_items(ITERATOR
*iter1
, ITERATOR
*iter2
)
1506 if(!iter1
->ranges
|| !iter2
->ranges
) {
1509 if(iter1
->ranges
|| iter2
->ranges
||
1510 (iter1
->range
.lower
<iter2
->range
.lower
&& iter1
->range
.upper
>iter2
->range
.upper
) ||
1511 (iter1
->range
.lower
>iter2
->range
.lower
&& iter1
->range
.upper
<iter2
->range
.upper
)) {
1512 ERR("result is not a one range iterator\n");
1516 if(iter1
->range
.lower
==-1 || iter2
->range
.lower
==-1)
1519 lower
= iter1
->range
.lower
;
1520 upper
= iter1
->range
.upper
;
1522 if(lower
< iter2
->range
.lower
)
1523 iter1
->range
.upper
= iter2
->range
.lower
;
1524 else if(upper
> iter2
->range
.upper
)
1525 iter1
->range
.lower
= iter2
->range
.upper
;
1527 iter1
->range
.lower
= iter1
->range
.upper
= -1;
1529 if(iter2
->range
.lower
< lower
)
1530 iter2
->range
.upper
= lower
;
1531 else if(iter2
->range
.upper
> upper
)
1532 iter2
->range
.lower
= upper
;
1534 iter2
->range
.lower
= iter2
->range
.upper
= -1;
1539 iterator_next(iter1
);
1540 iterator_next(iter2
);
1543 if(iter1
->nItem
==-1 || iter2
->nItem
==-1)
1546 if(iter1
->nItem
== iter2
->nItem
) {
1547 int delete = iter1
->nItem
;
1549 iterator_prev(iter1
);
1550 iterator_prev(iter2
);
1551 ranges_delitem(iter1
->ranges
, delete);
1552 ranges_delitem(iter2
->ranges
, delete);
1553 iterator_next(iter1
);
1554 iterator_next(iter2
);
1555 } else if(iter1
->nItem
> iter2
->nItem
)
1556 iterator_next(iter2
);
1558 iterator_next(iter1
);
1561 iter1
->nItem
= iter1
->range
.lower
= iter1
->range
.upper
= -1;
1562 iter2
->nItem
= iter2
->range
.lower
= iter2
->range
.upper
= -1;
1566 /******** Misc helper functions ************************************/
1568 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
1569 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
1571 if (isW
) return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
1572 else return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
1575 static inline BOOL
is_autoarrange(const LISTVIEW_INFO
*infoPtr
)
1577 return (infoPtr
->dwStyle
& LVS_AUTOARRANGE
) &&
1578 (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
);
1581 static void toggle_checkbox_state(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1583 DWORD state
= STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_STATEIMAGEMASK
));
1584 if(state
== 1 || state
== 2)
1588 lvitem
.state
= INDEXTOSTATEIMAGEMASK(state
);
1589 lvitem
.stateMask
= LVIS_STATEIMAGEMASK
;
1590 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvitem
);
1594 /* this should be called after window style got updated,
1595 it used to reset view state to match current window style */
1596 static inline void map_style_view(LISTVIEW_INFO
*infoPtr
)
1598 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
1601 infoPtr
->uView
= LV_VIEW_ICON
;
1604 infoPtr
->uView
= LV_VIEW_DETAILS
;
1607 infoPtr
->uView
= LV_VIEW_SMALLICON
;
1610 infoPtr
->uView
= LV_VIEW_LIST
;
1614 /* computes next item id value */
1615 static DWORD
get_next_itemid(const LISTVIEW_INFO
*infoPtr
)
1617 INT count
= DPA_GetPtrCount(infoPtr
->hdpaItemIds
);
1621 ITEM_ID
*lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, count
- 1);
1622 return lpID
->id
+ 1;
1627 /******** Internal API functions ************************************/
1629 static inline COLUMN_INFO
* LISTVIEW_GetColumnInfo(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
)
1631 static COLUMN_INFO mainItem
;
1633 if (nSubItem
== 0 && DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0) return &mainItem
;
1634 assert (nSubItem
>= 0 && nSubItem
< DPA_GetPtrCount(infoPtr
->hdpaColumns
));
1636 /* update cached column rectangles */
1637 if (infoPtr
->colRectsDirty
)
1640 LISTVIEW_INFO
*Ptr
= (LISTVIEW_INFO
*)infoPtr
;
1643 for (i
= 0; i
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); i
++) {
1644 info
= DPA_GetPtr(infoPtr
->hdpaColumns
, i
);
1645 SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMRECT
, i
, (LPARAM
)&info
->rcHeader
);
1647 Ptr
->colRectsDirty
= FALSE
;
1650 return DPA_GetPtr(infoPtr
->hdpaColumns
, nSubItem
);
1653 static INT
LISTVIEW_CreateHeader(LISTVIEW_INFO
*infoPtr
)
1655 DWORD dFlags
= WS_CHILD
| HDS_HORZ
| HDS_FULLDRAG
| HDS_DRAGDROP
;
1658 if (infoPtr
->hwndHeader
) return 0;
1660 TRACE("Creating header for list %p\n", infoPtr
->hwndSelf
);
1662 /* setup creation flags */
1663 dFlags
|= (LVS_NOSORTHEADER
& infoPtr
->dwStyle
) ? 0 : HDS_BUTTONS
;
1664 dFlags
|= (LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
) ? HDS_HIDDEN
: 0;
1666 hInst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
1669 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
, dFlags
,
1670 0, 0, 0, 0, infoPtr
->hwndSelf
, NULL
, hInst
, NULL
);
1671 if (!infoPtr
->hwndHeader
) return -1;
1673 /* set header unicode format */
1674 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, TRUE
, 0);
1676 /* set header font */
1677 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, TRUE
);
1679 /* set header image list */
1680 if (infoPtr
->himlSmall
)
1681 SendMessageW(infoPtr
->hwndHeader
, HDM_SETIMAGELIST
, 0, (LPARAM
)infoPtr
->himlSmall
);
1683 LISTVIEW_UpdateSize(infoPtr
);
1688 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
, LPRECT lprc
)
1690 *lprc
= LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->rcHeader
;
1693 static inline BOOL
LISTVIEW_IsHeaderEnabled(const LISTVIEW_INFO
*infoPtr
)
1695 return (infoPtr
->uView
== LV_VIEW_DETAILS
||
1696 infoPtr
->dwLvExStyle
& LVS_EX_HEADERINALLVIEWS
) &&
1697 !(infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
);
1700 static inline BOOL
LISTVIEW_GetItemW(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
1702 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
1705 /* used to handle collapse main item column case */
1706 static inline BOOL
LISTVIEW_DrawFocusRect(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1708 return (infoPtr
->rcFocus
.left
< infoPtr
->rcFocus
.right
) ?
1709 DrawFocusRect(hdc
, &infoPtr
->rcFocus
) : FALSE
;
1712 /* Listview invalidation functions: use _only_ these functions to invalidate */
1714 static inline BOOL
is_redrawing(const LISTVIEW_INFO
*infoPtr
)
1716 return infoPtr
->redraw
;
1719 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO
*infoPtr
, const RECT
* rect
)
1721 if(!is_redrawing(infoPtr
)) return;
1722 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect
));
1723 InvalidateRect(infoPtr
->hwndSelf
, rect
, TRUE
);
1726 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
1730 if(!is_redrawing(infoPtr
)) return;
1731 LISTVIEW_GetItemBox(infoPtr
, nItem
, &rcBox
);
1732 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1735 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
)
1737 POINT Origin
, Position
;
1740 if(!is_redrawing(infoPtr
)) return;
1741 assert (infoPtr
->uView
== LV_VIEW_DETAILS
);
1742 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1743 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
1744 LISTVIEW_GetHeaderRect(infoPtr
, nSubItem
, &rcBox
);
1746 rcBox
.bottom
= infoPtr
->nItemHeight
;
1747 OffsetRect(&rcBox
, Origin
.x
+ Position
.x
, Origin
.y
+ Position
.y
);
1748 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1751 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO
*infoPtr
)
1753 LISTVIEW_InvalidateRect(infoPtr
, NULL
);
1756 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
1760 if(!is_redrawing(infoPtr
)) return;
1761 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
1762 rcCol
.top
= infoPtr
->rcList
.top
;
1763 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
1764 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
1769 * Retrieves the number of items that can fit vertically in the client area.
1772 * [I] infoPtr : valid pointer to the listview structure
1775 * Number of items per row.
1777 static inline INT
LISTVIEW_GetCountPerRow(const LISTVIEW_INFO
*infoPtr
)
1779 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1781 return max(nListWidth
/(infoPtr
->nItemWidth
? infoPtr
->nItemWidth
: 1), 1);
1786 * Retrieves the number of items that can fit horizontally in the client
1790 * [I] infoPtr : valid pointer to the listview structure
1793 * Number of items per column.
1795 static inline INT
LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO
*infoPtr
)
1797 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1799 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
1803 /*************************************************************************
1804 * LISTVIEW_ProcessLetterKeys
1806 * Processes keyboard messages generated by pressing the letter keys
1808 * What this does is perform a case insensitive search from the
1809 * current position with the following quirks:
1810 * - If two chars or more are pressed in quick succession we search
1811 * for the corresponding string (e.g. 'abc').
1812 * - If there is a delay we wipe away the current search string and
1813 * restart with just that char.
1814 * - If the user keeps pressing the same character, whether slowly or
1815 * fast, so that the search string is entirely composed of this
1816 * character ('aaaaa' for instance), then we search for first item
1817 * that starting with that character.
1818 * - If the user types the above character in quick succession, then
1819 * we must also search for the corresponding string ('aaaaa'), and
1820 * go to that string if there is a match.
1823 * [I] hwnd : handle to the window
1824 * [I] charCode : the character code, the actual character
1825 * [I] keyData : key data
1833 * - The current implementation has a list of characters it will
1834 * accept and it ignores everything else. In particular it will
1835 * ignore accentuated characters which seems to match what
1836 * Windows does. But I'm not sure it makes sense to follow
1838 * - We don't sound a beep when the search fails.
1842 * TREEVIEW_ProcessLetterKeys
1844 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
1846 WCHAR buffer
[MAX_PATH
];
1853 /* simple parameter checking */
1854 if (!charCode
|| !keyData
|| infoPtr
->nItemCount
== 0) return 0;
1856 /* only allow the valid WM_CHARs through */
1857 if (!isalnumW(charCode
) &&
1858 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
1859 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
1860 charCode
!= '%' && charCode
!= '^' && 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
!= '~')
1869 /* update the search parameters */
1870 prevTime
= infoPtr
->lastKeyPressTimestamp
;
1871 infoPtr
->lastKeyPressTimestamp
= GetTickCount();
1872 diff
= infoPtr
->lastKeyPressTimestamp
- prevTime
;
1874 if (diff
>= 0 && diff
< KEY_DELAY
)
1876 if (infoPtr
->nSearchParamLength
< MAX_PATH
- 1)
1877 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++] = charCode
;
1879 if (infoPtr
->charCode
!= charCode
)
1880 infoPtr
->charCode
= charCode
= 0;
1884 infoPtr
->charCode
= charCode
;
1885 infoPtr
->szSearchParam
[0] = charCode
;
1886 infoPtr
->nSearchParamLength
= 1;
1889 /* should start from next after focused item, so next item that matches
1890 will be selected, if there isn't any and focused matches it will be selected
1891 on second search stage from beginning of the list */
1892 if (infoPtr
->nFocusedItem
>= 0 && infoPtr
->nItemCount
> 1)
1894 /* with some accumulated search data available start with current focus, otherwise
1895 it's excluded from search */
1896 startidx
= infoPtr
->nSearchParamLength
> 1 ? infoPtr
->nFocusedItem
: infoPtr
->nFocusedItem
+ 1;
1897 if (startidx
== infoPtr
->nItemCount
) startidx
= 0;
1902 /* let application handle this for virtual listview */
1903 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
1907 memset(&nmlv
.lvfi
, 0, sizeof(nmlv
.lvfi
));
1908 nmlv
.lvfi
.flags
= (LVFI_WRAP
| LVFI_PARTIAL
);
1909 nmlv
.lvfi
.psz
= infoPtr
->szSearchParam
;
1910 nmlv
.iStart
= startidx
;
1912 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
] = 0;
1914 nItem
= notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
1918 int i
= startidx
, endidx
;
1920 /* and search from the current position */
1922 endidx
= infoPtr
->nItemCount
;
1924 /* first search in [startidx, endidx), on failure continue in [0, startidx) */
1927 /* start from first item if not found with >= startidx */
1928 if (i
== infoPtr
->nItemCount
&& startidx
> 0)
1934 for (i
= startidx
; i
< endidx
; i
++)
1937 item
.mask
= LVIF_TEXT
;
1940 item
.pszText
= buffer
;
1941 item
.cchTextMax
= MAX_PATH
;
1942 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
1944 if (!lstrncmpiW(item
.pszText
, infoPtr
->szSearchParam
, infoPtr
->nSearchParamLength
))
1949 /* this is used to find first char match when search string is not available yet,
1950 otherwise every WM_CHAR will search to next item by first char, ignoring that we're
1951 already waiting for user to complete a string */
1952 else if (nItem
== -1 && infoPtr
->nSearchParamLength
== 1 && !lstrncmpiW(item
.pszText
, infoPtr
->szSearchParam
, 1))
1954 /* this would work but we must keep looking for a longer match */
1959 if ( nItem
!= -1 || /* found something */
1960 endidx
!= infoPtr
->nItemCount
|| /* second search done */
1961 (startidx
== 0 && endidx
== infoPtr
->nItemCount
) /* full range for first search */ )
1967 LISTVIEW_KeySelection(infoPtr
, nItem
, FALSE
);
1972 /*************************************************************************
1973 * LISTVIEW_UpdateHeaderSize [Internal]
1975 * Function to resize the header control
1978 * [I] hwnd : handle to a window
1979 * [I] nNewScrollPos : scroll pos to set
1984 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
1989 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
1991 if (!infoPtr
->hwndHeader
) return;
1993 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
1994 point
[0].x
= winRect
.left
;
1995 point
[0].y
= winRect
.top
;
1996 point
[1].x
= winRect
.right
;
1997 point
[1].y
= winRect
.bottom
;
1999 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
2000 point
[0].x
= -nNewScrollPos
;
2001 point
[1].x
+= nNewScrollPos
;
2003 SetWindowPos(infoPtr
->hwndHeader
,0,
2004 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
2005 (infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
|
2006 SWP_NOZORDER
| SWP_NOACTIVATE
);
2009 static INT
LISTVIEW_UpdateHScroll(LISTVIEW_INFO
*infoPtr
)
2011 SCROLLINFO horzInfo
;
2014 ZeroMemory(&horzInfo
, sizeof(SCROLLINFO
));
2015 horzInfo
.cbSize
= sizeof(SCROLLINFO
);
2016 horzInfo
.nPage
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2018 /* for now, we'll set info.nMax to the _count_, and adjust it later */
2019 if (infoPtr
->uView
== LV_VIEW_LIST
)
2021 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
2022 horzInfo
.nMax
= (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
;
2024 /* scroll by at least one column per page */
2025 if(horzInfo
.nPage
< infoPtr
->nItemWidth
)
2026 horzInfo
.nPage
= infoPtr
->nItemWidth
;
2028 if (infoPtr
->nItemWidth
)
2029 horzInfo
.nPage
/= infoPtr
->nItemWidth
;
2031 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2033 horzInfo
.nMax
= infoPtr
->nItemWidth
;
2035 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2039 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) horzInfo
.nMax
= rcView
.right
- rcView
.left
;
2042 if (LISTVIEW_IsHeaderEnabled(infoPtr
))
2044 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
))
2049 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
2050 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
2052 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcHeader
);
2053 horzInfo
.nMax
= rcHeader
.right
;
2054 TRACE("horzInfo.nMax=%d\n", horzInfo
.nMax
);
2058 horzInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
2059 horzInfo
.nMax
= max(horzInfo
.nMax
- 1, 0);
2060 dx
= GetScrollPos(infoPtr
->hwndSelf
, SB_HORZ
);
2061 dx
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
, TRUE
);
2062 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo
));
2064 /* Update the Header Control */
2065 if (infoPtr
->hwndHeader
)
2067 horzInfo
.fMask
= SIF_POS
;
2068 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
);
2069 LISTVIEW_UpdateHeaderSize(infoPtr
, horzInfo
.nPos
);
2072 LISTVIEW_UpdateSize(infoPtr
);
2076 static INT
LISTVIEW_UpdateVScroll(LISTVIEW_INFO
*infoPtr
)
2078 SCROLLINFO vertInfo
;
2081 ZeroMemory(&vertInfo
, sizeof(SCROLLINFO
));
2082 vertInfo
.cbSize
= sizeof(SCROLLINFO
);
2083 vertInfo
.nPage
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2085 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2087 vertInfo
.nMax
= infoPtr
->nItemCount
;
2089 /* scroll by at least one page */
2090 if(vertInfo
.nPage
< infoPtr
->nItemHeight
)
2091 vertInfo
.nPage
= infoPtr
->nItemHeight
;
2093 if (infoPtr
->nItemHeight
> 0)
2094 vertInfo
.nPage
/= infoPtr
->nItemHeight
;
2096 else if (infoPtr
->uView
!= LV_VIEW_LIST
) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2100 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) vertInfo
.nMax
= rcView
.bottom
- rcView
.top
;
2103 vertInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
2104 vertInfo
.nMax
= max(vertInfo
.nMax
- 1, 0);
2105 dy
= GetScrollPos(infoPtr
->hwndSelf
, SB_VERT
);
2106 dy
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &vertInfo
, TRUE
);
2107 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo
));
2109 LISTVIEW_UpdateSize(infoPtr
);
2115 * Update the scrollbars. This function should be called whenever
2116 * the content, size or view changes.
2119 * [I] infoPtr : valid pointer to the listview structure
2124 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO
*infoPtr
)
2128 if ((infoPtr
->dwStyle
& LVS_NOSCROLL
) || !is_redrawing(infoPtr
)) return;
2130 /* Setting the horizontal scroll can change the listview size
2131 * (and potentially everything else) so we need to recompute
2132 * everything again for the vertical scroll and vice-versa
2134 for (dx
= 0, dy
= 0, pass
= 0; pass
<= 1; pass
++)
2136 dx
+= LISTVIEW_UpdateHScroll(infoPtr
);
2137 dy
+= LISTVIEW_UpdateVScroll(infoPtr
);
2140 /* Change of the range may have changed the scroll pos. If so move the content */
2141 if (dx
!= 0 || dy
!= 0)
2144 listRect
= infoPtr
->rcList
;
2145 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &listRect
, &listRect
, 0, 0,
2146 SW_ERASE
| SW_INVALIDATE
);
2153 * Shows/hides the focus rectangle.
2156 * [I] infoPtr : valid pointer to the listview structure
2157 * [I] fShow : TRUE to show the focus, FALSE to hide it.
2162 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO
*infoPtr
, BOOL fShow
)
2166 TRACE("fShow=%d, nItem=%d\n", fShow
, infoPtr
->nFocusedItem
);
2168 if (infoPtr
->nFocusedItem
< 0) return;
2170 /* we need some gymnastics in ICON mode to handle large items */
2171 if (infoPtr
->uView
== LV_VIEW_ICON
)
2175 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcBox
);
2176 if ((rcBox
.bottom
- rcBox
.top
) > infoPtr
->nItemHeight
)
2178 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
2183 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) return;
2185 /* for some reason, owner draw should work only in report mode */
2186 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
2191 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2192 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2194 item
.iItem
= infoPtr
->nFocusedItem
;
2196 item
.mask
= LVIF_PARAM
;
2197 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto done
;
2199 ZeroMemory(&dis
, sizeof(dis
));
2200 dis
.CtlType
= ODT_LISTVIEW
;
2201 dis
.CtlID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
2202 dis
.itemID
= item
.iItem
;
2203 dis
.itemAction
= ODA_FOCUS
;
2204 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
2205 dis
.hwndItem
= infoPtr
->hwndSelf
;
2207 LISTVIEW_GetItemBox(infoPtr
, dis
.itemID
, &dis
.rcItem
);
2208 dis
.itemData
= item
.lParam
;
2210 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
2212 SelectObject(hdc
, hOldFont
);
2215 LISTVIEW_InvalidateItem(infoPtr
, infoPtr
->nFocusedItem
);
2218 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2222 * Invalidates all visible selected items.
2224 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO
*infoPtr
)
2228 iterator_frameditems(&i
, infoPtr
, &infoPtr
->rcList
);
2229 while(iterator_next(&i
))
2231 if (LISTVIEW_GetItemState(infoPtr
, i
.nItem
, LVIS_SELECTED
))
2232 LISTVIEW_InvalidateItem(infoPtr
, i
.nItem
);
2234 iterator_destroy(&i
);
2239 * DESCRIPTION: [INTERNAL]
2240 * Computes an item's (left,top) corner, relative to rcView.
2241 * That is, the position has NOT been made relative to the Origin.
2242 * This is deliberate, to avoid computing the Origin over, and
2243 * over again, when this function is called in a loop. Instead,
2244 * one can factor the computation of the Origin before the loop,
2245 * and offset the value returned by this function, on every iteration.
2248 * [I] infoPtr : valid pointer to the listview structure
2249 * [I] nItem : item number
2250 * [O] lpptOrig : item top, left corner
2255 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
2257 assert(nItem
>= 0 && nItem
< infoPtr
->nItemCount
);
2259 if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
2261 lpptPosition
->x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2262 lpptPosition
->y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2264 else if (infoPtr
->uView
== LV_VIEW_LIST
)
2266 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
2267 lpptPosition
->x
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
2268 lpptPosition
->y
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
2270 else /* LV_VIEW_DETAILS */
2272 lpptPosition
->x
= REPORT_MARGINX
;
2273 /* item is always at zero indexed column */
2274 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2275 lpptPosition
->x
+= LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
;
2276 lpptPosition
->y
= nItem
* infoPtr
->nItemHeight
;
2281 * DESCRIPTION: [INTERNAL]
2282 * Compute the rectangles of an item. This is to localize all
2283 * the computations in one place. If you are not interested in some
2284 * of these values, simply pass in a NULL -- the function is smart
2285 * enough to compute only what's necessary. The function computes
2286 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
2287 * one, the BOX rectangle. This rectangle is very cheap to compute,
2288 * and is guaranteed to contain all the other rectangles. Computing
2289 * the ICON rect is also cheap, but all the others are potentially
2290 * expensive. This gives an easy and effective optimization when
2291 * searching (like point inclusion, or rectangle intersection):
2292 * first test against the BOX, and if TRUE, test against the desired
2294 * If the function does not have all the necessary information
2295 * to computed the requested rectangles, will crash with a
2296 * failed assertion. This is done so we catch all programming
2297 * errors, given that the function is called only from our code.
2299 * We have the following 'special' meanings for a few fields:
2300 * * If LVIS_FOCUSED is set, we assume the item has the focus
2301 * This is important in ICON mode, where it might get a larger
2302 * then usual rectangle
2304 * Please note that subitem support works only in REPORT mode.
2307 * [I] infoPtr : valid pointer to the listview structure
2308 * [I] lpLVItem : item to compute the measures for
2309 * [O] lprcBox : ptr to Box rectangle
2310 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
2311 * [0] lprcSelectBox : ptr to select box rectangle
2312 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
2313 * [O] lprcIcon : ptr to Icon rectangle
2314 * Same as LVM_GETITEMRECT with LVIR_ICON
2315 * [O] lprcStateIcon: ptr to State Icon rectangle
2316 * [O] lprcLabel : ptr to Label rectangle
2317 * Same as LVM_GETITEMRECT with LVIR_LABEL
2322 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
,
2323 LPRECT lprcBox
, LPRECT lprcSelectBox
,
2324 LPRECT lprcIcon
, LPRECT lprcStateIcon
, LPRECT lprcLabel
)
2326 BOOL doSelectBox
= FALSE
, doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
2327 RECT Box
, SelectBox
, Icon
, Label
;
2328 COLUMN_INFO
*lpColumnInfo
= NULL
;
2329 SIZE labelSize
= { 0, 0 };
2331 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem
, TRUE
));
2333 /* Be smart and try to figure out the minimum we have to do */
2334 if (lpLVItem
->iSubItem
) assert(infoPtr
->uView
== LV_VIEW_DETAILS
);
2335 if (infoPtr
->uView
== LV_VIEW_ICON
&& (lprcBox
|| lprcLabel
))
2337 assert((lpLVItem
->mask
& LVIF_STATE
) && (lpLVItem
->stateMask
& LVIS_FOCUSED
));
2338 if (lpLVItem
->state
& LVIS_FOCUSED
) oversizedBox
= doLabel
= TRUE
;
2340 if (lprcSelectBox
) doSelectBox
= TRUE
;
2341 if (lprcLabel
) doLabel
= TRUE
;
2342 if (doLabel
|| lprcIcon
|| lprcStateIcon
) doIcon
= TRUE
;
2349 /************************************************************/
2350 /* compute the box rectangle (it should be cheap to do) */
2351 /************************************************************/
2352 if (lpLVItem
->iSubItem
|| infoPtr
->uView
== LV_VIEW_DETAILS
)
2353 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpLVItem
->iSubItem
);
2355 if (lpLVItem
->iSubItem
)
2357 Box
= lpColumnInfo
->rcHeader
;
2362 Box
.right
= infoPtr
->nItemWidth
;
2365 Box
.bottom
= infoPtr
->nItemHeight
;
2367 /******************************************************************/
2368 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2369 /******************************************************************/
2372 LONG state_width
= 0;
2374 if (infoPtr
->himlState
&& lpLVItem
->iSubItem
== 0)
2375 state_width
= infoPtr
->iconStateSize
.cx
;
2377 if (infoPtr
->uView
== LV_VIEW_ICON
)
2379 Icon
.left
= Box
.left
+ state_width
;
2380 if (infoPtr
->himlNormal
)
2381 Icon
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
- state_width
) / 2;
2382 Icon
.top
= Box
.top
+ ICON_TOP_PADDING
;
2383 Icon
.right
= Icon
.left
;
2384 Icon
.bottom
= Icon
.top
;
2385 if (infoPtr
->himlNormal
)
2387 Icon
.right
+= infoPtr
->iconSize
.cx
;
2388 Icon
.bottom
+= infoPtr
->iconSize
.cy
;
2391 else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2393 Icon
.left
= Box
.left
+ state_width
;
2395 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& lpLVItem
->iSubItem
== 0)
2397 /* we need the indent in report mode */
2398 assert(lpLVItem
->mask
& LVIF_INDENT
);
2399 Icon
.left
+= infoPtr
->iconSize
.cx
* lpLVItem
->iIndent
+ REPORT_MARGINX
;
2403 Icon
.right
= Icon
.left
;
2404 if (infoPtr
->himlSmall
&&
2405 (!lpColumnInfo
|| lpLVItem
->iSubItem
== 0 ||
2406 ((infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
) && lpLVItem
->iImage
!= I_IMAGECALLBACK
)))
2407 Icon
.right
+= infoPtr
->iconSize
.cx
;
2408 Icon
.bottom
= Icon
.top
+ infoPtr
->iconSize
.cy
;
2410 if(lprcIcon
) *lprcIcon
= Icon
;
2411 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon
));
2413 /* TODO: is this correct? */
2416 lprcStateIcon
->left
= Icon
.left
- state_width
;
2417 lprcStateIcon
->right
= Icon
.left
;
2418 lprcStateIcon
->top
= Icon
.top
;
2419 lprcStateIcon
->bottom
= lprcStateIcon
->top
+ infoPtr
->iconSize
.cy
;
2420 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon
));
2423 else Icon
.right
= 0;
2425 /************************************************************/
2426 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2427 /************************************************************/
2430 /* calculate how far to the right can the label stretch */
2431 Label
.right
= Box
.right
;
2432 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2434 if (lpLVItem
->iSubItem
== 0)
2436 /* we need a zero based rect here */
2437 Label
= lpColumnInfo
->rcHeader
;
2438 OffsetRect(&Label
, -Label
.left
, 0);
2442 if (lpLVItem
->iSubItem
|| ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && infoPtr
->uView
== LV_VIEW_DETAILS
))
2444 labelSize
.cx
= infoPtr
->nItemWidth
;
2445 labelSize
.cy
= infoPtr
->nItemHeight
;
2449 /* we need the text in non owner draw mode */
2450 assert(lpLVItem
->mask
& LVIF_TEXT
);
2451 if (is_text(lpLVItem
->pszText
))
2453 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2454 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2455 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2459 /* compute rough rectangle where the label will go */
2460 SetRectEmpty(&rcText
);
2461 rcText
.right
= infoPtr
->nItemWidth
- TRAILING_LABEL_PADDING
;
2462 rcText
.bottom
= infoPtr
->nItemHeight
;
2463 if (infoPtr
->uView
== LV_VIEW_ICON
)
2464 rcText
.bottom
-= ICON_TOP_PADDING
+ infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2466 /* now figure out the flags */
2467 if (infoPtr
->uView
== LV_VIEW_ICON
)
2468 uFormat
= oversizedBox
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
;
2470 uFormat
= LV_SL_DT_FLAGS
;
2472 DrawTextW (hdc
, lpLVItem
->pszText
, -1, &rcText
, uFormat
| DT_CALCRECT
);
2474 if (rcText
.right
!= rcText
.left
)
2475 labelSize
.cx
= min(rcText
.right
- rcText
.left
+ TRAILING_LABEL_PADDING
, infoPtr
->nItemWidth
);
2477 labelSize
.cy
= rcText
.bottom
- rcText
.top
;
2479 SelectObject(hdc
, hOldFont
);
2480 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2484 if (infoPtr
->uView
== LV_VIEW_ICON
)
2486 Label
.left
= Box
.left
+ (infoPtr
->nItemWidth
- labelSize
.cx
) / 2;
2487 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
2488 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2489 Label
.right
= Label
.left
+ labelSize
.cx
;
2490 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2491 if (!oversizedBox
&& labelSize
.cy
> infoPtr
->ntmHeight
)
2493 labelSize
.cy
= min(Box
.bottom
- Label
.top
, labelSize
.cy
);
2494 labelSize
.cy
/= infoPtr
->ntmHeight
;
2495 labelSize
.cy
= max(labelSize
.cy
, 1);
2496 labelSize
.cy
*= infoPtr
->ntmHeight
;
2498 Label
.bottom
= Label
.top
+ labelSize
.cy
+ HEIGHT_PADDING
;
2500 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2502 Label
.left
= Icon
.right
;
2503 Label
.top
= Box
.top
;
2504 Label
.right
= lpLVItem
->iSubItem
? lpColumnInfo
->rcHeader
.right
:
2505 lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
2506 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2508 else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2510 Label
.left
= Icon
.right
;
2511 Label
.top
= Box
.top
;
2512 Label
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2513 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2516 if (lprcLabel
) *lprcLabel
= Label
;
2517 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label
));
2520 /************************************************************/
2521 /* compute SELECT bounding box */
2522 /************************************************************/
2525 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2527 SelectBox
.left
= Icon
.left
;
2528 SelectBox
.top
= Box
.top
;
2529 SelectBox
.bottom
= Box
.bottom
;
2532 SelectBox
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2534 SelectBox
.right
= min(Label
.left
+ MAX_EMPTYTEXT_SELECT_WIDTH
, Label
.right
);
2538 UnionRect(&SelectBox
, &Icon
, &Label
);
2540 if (lprcSelectBox
) *lprcSelectBox
= SelectBox
;
2541 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox
));
2544 /* Fix the Box if necessary */
2547 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
2548 else *lprcBox
= Box
;
2550 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box
));
2554 * DESCRIPTION: [INTERNAL]
2557 * [I] infoPtr : valid pointer to the listview structure
2558 * [I] nItem : item number
2559 * [O] lprcBox : ptr to Box rectangle
2564 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprcBox
)
2566 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2567 POINT Position
, Origin
;
2570 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
2571 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
2573 /* Be smart and try to figure out the minimum we have to do */
2575 if (infoPtr
->uView
== LV_VIEW_ICON
&& infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
2576 lvItem
.mask
|= LVIF_TEXT
;
2577 lvItem
.iItem
= nItem
;
2578 lvItem
.iSubItem
= 0;
2579 lvItem
.pszText
= szDispText
;
2580 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2581 if (lvItem
.mask
) LISTVIEW_GetItemW(infoPtr
, &lvItem
);
2582 if (infoPtr
->uView
== LV_VIEW_ICON
)
2584 lvItem
.mask
|= LVIF_STATE
;
2585 lvItem
.stateMask
= LVIS_FOCUSED
;
2586 lvItem
.state
= (lvItem
.mask
& LVIF_TEXT
? LVIS_FOCUSED
: 0);
2588 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprcBox
, 0, 0, 0, 0);
2590 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
&&
2591 SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, 0, 0))
2593 OffsetRect(lprcBox
, Origin
.x
, Position
.y
+ Origin
.y
);
2596 OffsetRect(lprcBox
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
2599 /* LISTVIEW_MapIdToIndex helper */
2600 static INT CALLBACK
MapIdSearchCompare(LPVOID p1
, LPVOID p2
, LPARAM lParam
)
2602 ITEM_ID
*id1
= (ITEM_ID
*)p1
;
2603 ITEM_ID
*id2
= (ITEM_ID
*)p2
;
2605 if (id1
->id
== id2
->id
) return 0;
2607 return (id1
->id
< id2
->id
) ? -1 : 1;
2612 * Returns the item index for id specified.
2615 * [I] infoPtr : valid pointer to the listview structure
2616 * [I] iID : item id to get index for
2619 * Item index, or -1 on failure.
2621 static INT
LISTVIEW_MapIdToIndex(const LISTVIEW_INFO
*infoPtr
, UINT iID
)
2626 TRACE("iID=%d\n", iID
);
2628 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return -1;
2629 if (infoPtr
->nItemCount
== 0) return -1;
2632 index
= DPA_Search(infoPtr
->hdpaItemIds
, &ID
, -1, MapIdSearchCompare
, 0, DPAS_SORTED
);
2636 ITEM_ID
*lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, index
);
2637 return DPA_GetPtrIndex(infoPtr
->hdpaItems
, lpID
->item
);
2645 * Returns the item id for index given.
2648 * [I] infoPtr : valid pointer to the listview structure
2649 * [I] iItem : item index to get id for
2654 static DWORD
LISTVIEW_MapIndexToId(const LISTVIEW_INFO
*infoPtr
, INT iItem
)
2659 TRACE("iItem=%d\n", iItem
);
2661 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return -1;
2662 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
) return -1;
2664 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, iItem
);
2665 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
2667 return lpItem
->id
->id
;
2672 * Returns the current icon position, and advances it along the top.
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_NextIconPosTop(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2684 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2686 *lpPos
= infoPtr
->currIconPos
;
2688 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2689 if (infoPtr
->currIconPos
.x
+ infoPtr
->nItemWidth
<= nListWidth
) return;
2691 infoPtr
->currIconPos
.x
= 0;
2692 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2698 * Returns the current icon position, and advances it down the left edge.
2699 * The returned position is not offset by Origin.
2702 * [I] infoPtr : valid pointer to the listview structure
2703 * [O] lpPos : will get the current icon position
2708 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2710 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2712 *lpPos
= infoPtr
->currIconPos
;
2714 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2715 if (infoPtr
->currIconPos
.y
+ infoPtr
->nItemHeight
<= nListHeight
) return;
2717 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2718 infoPtr
->currIconPos
.y
= 0;
2724 * Moves an icon to the specified position.
2725 * It takes care of invalidating the item, etc.
2728 * [I] infoPtr : valid pointer to the listview structure
2729 * [I] nItem : the item to move
2730 * [I] lpPos : the new icon position
2731 * [I] isNew : flags the item as being new
2737 static BOOL
LISTVIEW_MoveIconTo(const LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*lppt
, BOOL isNew
)
2743 old
.x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2744 old
.y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2746 if (lppt
->x
== old
.x
&& lppt
->y
== old
.y
) return TRUE
;
2747 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2750 /* Allocating a POINTER for every item is too resource intensive,
2751 * so we'll keep the (x,y) in different arrays */
2752 if (!DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)(LONG_PTR
)lppt
->x
)) return FALSE
;
2753 if (!DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)(LONG_PTR
)lppt
->y
)) return FALSE
;
2755 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2762 * Arranges listview items in icon display mode.
2765 * [I] infoPtr : valid pointer to the listview structure
2766 * [I] nAlignCode : alignment code
2772 static BOOL
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
2774 void (*next_pos
)(LISTVIEW_INFO
*, LPPOINT
);
2778 if (infoPtr
->uView
!= LV_VIEW_ICON
&& infoPtr
->uView
!= LV_VIEW_SMALLICON
) return FALSE
;
2780 TRACE("nAlignCode=%d\n", nAlignCode
);
2782 if (nAlignCode
== LVA_DEFAULT
)
2784 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
) nAlignCode
= LVA_ALIGNLEFT
;
2785 else nAlignCode
= LVA_ALIGNTOP
;
2790 case LVA_ALIGNLEFT
: next_pos
= LISTVIEW_NextIconPosLeft
; break;
2791 case LVA_ALIGNTOP
: next_pos
= LISTVIEW_NextIconPosTop
; break;
2792 case LVA_SNAPTOGRID
: next_pos
= LISTVIEW_NextIconPosTop
; break; /* FIXME */
2793 default: return FALSE
;
2796 infoPtr
->currIconPos
.x
= infoPtr
->currIconPos
.y
= 0;
2797 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2799 next_pos(infoPtr
, &pos
);
2800 LISTVIEW_MoveIconTo(infoPtr
, i
, &pos
, FALSE
);
2808 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2809 * For LVS_REPORT always returns empty rectangle.
2812 * [I] infoPtr : valid pointer to the listview structure
2813 * [O] lprcView : bounding rectangle
2819 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2823 SetRectEmpty(lprcView
);
2825 switch (infoPtr
->uView
)
2828 case LV_VIEW_SMALLICON
:
2829 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2831 x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, i
);
2832 y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, i
);
2833 lprcView
->right
= max(lprcView
->right
, x
);
2834 lprcView
->bottom
= max(lprcView
->bottom
, y
);
2836 if (infoPtr
->nItemCount
> 0)
2838 lprcView
->right
+= infoPtr
->nItemWidth
;
2839 lprcView
->bottom
+= infoPtr
->nItemHeight
;
2844 y
= LISTVIEW_GetCountPerColumn(infoPtr
);
2845 x
= infoPtr
->nItemCount
/ y
;
2846 if (infoPtr
->nItemCount
% y
) x
++;
2847 lprcView
->right
= x
* infoPtr
->nItemWidth
;
2848 lprcView
->bottom
= y
* infoPtr
->nItemHeight
;
2855 * Retrieves the bounding rectangle of all the items.
2858 * [I] infoPtr : valid pointer to the listview structure
2859 * [O] lprcView : bounding rectangle
2865 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2869 TRACE("(lprcView=%p)\n", lprcView
);
2871 if (!lprcView
) return FALSE
;
2873 LISTVIEW_GetAreaRect(infoPtr
, lprcView
);
2875 if (infoPtr
->uView
!= LV_VIEW_DETAILS
)
2877 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
2878 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
2881 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView
));
2888 * Retrieves the subitem pointer associated with the subitem index.
2891 * [I] hdpaSubItems : DPA handle for a specific item
2892 * [I] nSubItem : index of subitem
2895 * SUCCESS : subitem pointer
2898 static SUBITEM_INFO
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
, INT nSubItem
)
2900 SUBITEM_INFO
*lpSubItem
;
2903 /* we should binary search here if need be */
2904 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
2906 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
2907 if (lpSubItem
->iSubItem
== nSubItem
)
2917 * Calculates the desired item width.
2920 * [I] infoPtr : valid pointer to the listview structure
2923 * The desired item width.
2925 static INT
LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO
*infoPtr
)
2929 TRACE("uView=%d\n", infoPtr
->uView
);
2931 if (infoPtr
->uView
== LV_VIEW_ICON
)
2932 nItemWidth
= infoPtr
->iconSpacing
.cx
;
2933 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2935 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2940 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
2941 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
2943 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcHeader
);
2944 nItemWidth
= rcHeader
.right
;
2947 else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
2949 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2953 lvItem
.mask
= LVIF_TEXT
;
2954 lvItem
.iSubItem
= 0;
2956 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2959 lvItem
.pszText
= szDispText
;
2960 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2961 if (LISTVIEW_GetItemW(infoPtr
, &lvItem
))
2962 nItemWidth
= max(LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
),
2966 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
2967 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
2969 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
+ WIDTH_PADDING
);
2977 * Calculates the desired item height.
2980 * [I] infoPtr : valid pointer to the listview structure
2983 * The desired item height.
2985 static INT
LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO
*infoPtr
)
2989 TRACE("uView=%d\n", infoPtr
->uView
);
2991 if (infoPtr
->uView
== LV_VIEW_ICON
)
2992 nItemHeight
= infoPtr
->iconSpacing
.cy
;
2995 nItemHeight
= infoPtr
->ntmHeight
;
2996 if (infoPtr
->himlState
)
2997 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
2998 if (infoPtr
->himlSmall
)
2999 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
3000 nItemHeight
+= HEIGHT_PADDING
;
3001 if (infoPtr
->nMeasureItemHeight
> 0)
3002 nItemHeight
= infoPtr
->nMeasureItemHeight
;
3005 return max(nItemHeight
, 1);
3010 * Updates the width, and height of an item.
3013 * [I] infoPtr : valid pointer to the listview structure
3018 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO
*infoPtr
)
3020 infoPtr
->nItemWidth
= LISTVIEW_CalculateItemWidth(infoPtr
);
3021 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
3027 * Retrieves and saves important text metrics info for the current
3031 * [I] infoPtr : valid pointer to the listview structure
3034 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
3036 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
3037 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
3038 HFONT hOldFont
= SelectObject(hdc
, hFont
);
3042 if (GetTextMetricsW(hdc
, &tm
))
3044 infoPtr
->ntmHeight
= tm
.tmHeight
;
3045 infoPtr
->ntmMaxCharWidth
= tm
.tmMaxCharWidth
;
3048 if (GetTextExtentPoint32A(hdc
, "...", 3, &sz
))
3049 infoPtr
->nEllipsisWidth
= sz
.cx
;
3051 SelectObject(hdc
, hOldFont
);
3052 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
3054 TRACE("tmHeight=%d\n", infoPtr
->ntmHeight
);
3059 * A compare function for ranges
3062 * [I] range1 : pointer to range 1;
3063 * [I] range2 : pointer to range 2;
3067 * > 0 : if range 1 > range 2
3068 * < 0 : if range 2 > range 1
3069 * = 0 : if range intersects range 2
3071 static INT CALLBACK
ranges_cmp(LPVOID range1
, LPVOID range2
, LPARAM flags
)
3075 if (((RANGE
*)range1
)->upper
<= ((RANGE
*)range2
)->lower
)
3077 else if (((RANGE
*)range2
)->upper
<= ((RANGE
*)range1
)->lower
)
3082 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1
), debugrange(range2
), cmp
);
3087 #define ranges_check(ranges, desc) if (TRACE_ON(listview)) ranges_assert(ranges, desc, __FILE__, __LINE__)
3089 static void ranges_assert(RANGES ranges
, LPCSTR desc
, const char *file
, int line
)
3094 TRACE("*** Checking %s:%d:%s ***\n", file
, line
, desc
);
3096 assert (DPA_GetPtrCount(ranges
->hdpa
) >= 0);
3097 ranges_dump(ranges
);
3098 if (DPA_GetPtrCount(ranges
->hdpa
) > 0)
3100 prev
= DPA_GetPtr(ranges
->hdpa
, 0);
3101 assert (prev
->lower
>= 0 && prev
->lower
< prev
->upper
);
3102 for (i
= 1; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3104 curr
= DPA_GetPtr(ranges
->hdpa
, i
);
3105 assert (prev
->upper
<= curr
->lower
);
3106 assert (curr
->lower
< curr
->upper
);
3110 TRACE("--- Done checking---\n");
3113 static RANGES
ranges_create(int count
)
3115 RANGES ranges
= Alloc(sizeof(struct tagRANGES
));
3116 if (!ranges
) return NULL
;
3117 ranges
->hdpa
= DPA_Create(count
);
3118 if (ranges
->hdpa
) return ranges
;
3123 static void ranges_clear(RANGES ranges
)
3127 for(i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3128 Free(DPA_GetPtr(ranges
->hdpa
, i
));
3129 DPA_DeleteAllPtrs(ranges
->hdpa
);
3133 static void ranges_destroy(RANGES ranges
)
3135 if (!ranges
) return;
3136 ranges_clear(ranges
);
3137 DPA_Destroy(ranges
->hdpa
);
3141 static RANGES
ranges_clone(RANGES ranges
)
3146 if (!(clone
= ranges_create(DPA_GetPtrCount(ranges
->hdpa
)))) goto fail
;
3148 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3150 RANGE
*newrng
= Alloc(sizeof(RANGE
));
3151 if (!newrng
) goto fail
;
3152 *newrng
= *((RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
));
3153 if (!DPA_SetPtr(clone
->hdpa
, i
, newrng
))
3162 TRACE ("clone failed\n");
3163 ranges_destroy(clone
);
3167 static RANGES
ranges_diff(RANGES ranges
, RANGES sub
)
3171 for (i
= 0; i
< DPA_GetPtrCount(sub
->hdpa
); i
++)
3172 ranges_del(ranges
, *((RANGE
*)DPA_GetPtr(sub
->hdpa
, i
)));
3177 static void ranges_dump(RANGES ranges
)
3181 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3182 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges
->hdpa
, i
)));
3185 static inline BOOL
ranges_contain(RANGES ranges
, INT nItem
)
3187 RANGE srchrng
= { nItem
, nItem
+ 1 };
3189 TRACE("(nItem=%d)\n", nItem
);
3190 ranges_check(ranges
, "before contain");
3191 return DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
) != -1;
3194 static INT
ranges_itemcount(RANGES ranges
)
3198 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3200 RANGE
*sel
= DPA_GetPtr(ranges
->hdpa
, i
);
3201 count
+= sel
->upper
- sel
->lower
;
3207 static BOOL
ranges_shift(RANGES ranges
, INT nItem
, INT delta
, INT nUpper
)
3209 RANGE srchrng
= { nItem
, nItem
+ 1 }, *chkrng
;
3212 index
= DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
3213 if (index
== -1) return TRUE
;
3215 for (; index
< DPA_GetPtrCount(ranges
->hdpa
); index
++)
3217 chkrng
= DPA_GetPtr(ranges
->hdpa
, index
);
3218 if (chkrng
->lower
>= nItem
)
3219 chkrng
->lower
= max(min(chkrng
->lower
+ delta
, nUpper
- 1), 0);
3220 if (chkrng
->upper
> nItem
)
3221 chkrng
->upper
= max(min(chkrng
->upper
+ delta
, nUpper
), 0);
3226 static BOOL
ranges_add(RANGES ranges
, RANGE range
)
3231 TRACE("(%s)\n", debugrange(&range
));
3232 ranges_check(ranges
, "before add");
3234 /* try find overlapping regions first */
3235 srchrgn
.lower
= range
.lower
- 1;
3236 srchrgn
.upper
= range
.upper
+ 1;
3237 index
= DPA_Search(ranges
->hdpa
, &srchrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
);
3243 TRACE("Adding new range\n");
3245 /* create the brand new range to insert */
3246 newrgn
= Alloc(sizeof(RANGE
));
3247 if(!newrgn
) goto fail
;
3250 /* figure out where to insert it */
3251 index
= DPA_Search(ranges
->hdpa
, newrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
3252 TRACE("index=%d\n", index
);
3253 if (index
== -1) index
= 0;
3255 /* and get it over with */
3256 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
3264 RANGE
*chkrgn
, *mrgrgn
;
3265 INT fromindex
, mergeindex
;
3267 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
3268 TRACE("Merge with %s @%d\n", debugrange(chkrgn
), index
);
3270 chkrgn
->lower
= min(range
.lower
, chkrgn
->lower
);
3271 chkrgn
->upper
= max(range
.upper
, chkrgn
->upper
);
3273 TRACE("New range %s @%d\n", debugrange(chkrgn
), index
);
3275 /* merge now common ranges */
3277 srchrgn
.lower
= chkrgn
->lower
- 1;
3278 srchrgn
.upper
= chkrgn
->upper
+ 1;
3282 mergeindex
= DPA_Search(ranges
->hdpa
, &srchrgn
, fromindex
, ranges_cmp
, 0, 0);
3283 if (mergeindex
== -1) break;
3284 if (mergeindex
== index
)
3286 fromindex
= index
+ 1;
3290 TRACE("Merge with index %i\n", mergeindex
);
3292 mrgrgn
= DPA_GetPtr(ranges
->hdpa
, mergeindex
);
3293 chkrgn
->lower
= min(chkrgn
->lower
, mrgrgn
->lower
);
3294 chkrgn
->upper
= max(chkrgn
->upper
, mrgrgn
->upper
);
3296 DPA_DeletePtr(ranges
->hdpa
, mergeindex
);
3297 if (mergeindex
< index
) index
--;
3301 ranges_check(ranges
, "after add");
3305 ranges_check(ranges
, "failed add");
3309 static BOOL
ranges_del(RANGES ranges
, RANGE range
)
3314 TRACE("(%s)\n", debugrange(&range
));
3315 ranges_check(ranges
, "before del");
3317 /* we don't use DPAS_SORTED here, since we need *
3318 * to find the first overlapping range */
3319 index
= DPA_Search(ranges
->hdpa
, &range
, 0, ranges_cmp
, 0, 0);
3322 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
3324 TRACE("Matches range %s @%d\n", debugrange(chkrgn
), index
);
3326 /* case 1: Same range */
3327 if ( (chkrgn
->upper
== range
.upper
) &&
3328 (chkrgn
->lower
== range
.lower
) )
3330 DPA_DeletePtr(ranges
->hdpa
, index
);
3334 /* case 2: engulf */
3335 else if ( (chkrgn
->upper
<= range
.upper
) &&
3336 (chkrgn
->lower
>= range
.lower
) )
3338 DPA_DeletePtr(ranges
->hdpa
, index
);
3341 /* case 3: overlap upper */
3342 else if ( (chkrgn
->upper
<= range
.upper
) &&
3343 (chkrgn
->lower
< range
.lower
) )
3345 chkrgn
->upper
= range
.lower
;
3347 /* case 4: overlap lower */
3348 else if ( (chkrgn
->upper
> range
.upper
) &&
3349 (chkrgn
->lower
>= range
.lower
) )
3351 chkrgn
->lower
= range
.upper
;
3354 /* case 5: fully internal */
3359 if (!(newrgn
= Alloc(sizeof(RANGE
)))) goto fail
;
3360 newrgn
->lower
= chkrgn
->lower
;
3361 newrgn
->upper
= range
.lower
;
3362 chkrgn
->lower
= range
.upper
;
3363 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
3371 index
= DPA_Search(ranges
->hdpa
, &range
, index
, ranges_cmp
, 0, 0);
3374 ranges_check(ranges
, "after del");
3378 ranges_check(ranges
, "failed del");
3384 * Removes all selection ranges
3387 * [I] infoPtr : valid pointer to the listview structure
3388 * [I] toSkip : item range to skip removing the selection
3394 static BOOL
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO
*infoPtr
, RANGES toSkip
)
3403 lvItem
.stateMask
= LVIS_SELECTED
;
3405 /* need to clone the DPA because callbacks can change it */
3406 if (!(clone
= ranges_clone(infoPtr
->selectionRanges
))) return FALSE
;
3407 iterator_rangesitems(&i
, ranges_diff(clone
, toSkip
));
3408 while(iterator_next(&i
))
3409 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &lvItem
);
3410 /* note that the iterator destructor will free the cloned range */
3411 iterator_destroy(&i
);
3416 static inline BOOL
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3420 if (!(toSkip
= ranges_create(1))) return FALSE
;
3421 if (nItem
!= -1) ranges_additem(toSkip
, nItem
);
3422 LISTVIEW_DeselectAllSkipItems(infoPtr
, toSkip
);
3423 ranges_destroy(toSkip
);
3427 static inline BOOL
LISTVIEW_DeselectAll(LISTVIEW_INFO
*infoPtr
)
3429 return LISTVIEW_DeselectAllSkipItem(infoPtr
, -1);
3434 * Retrieves the number of items that are marked as selected.
3437 * [I] infoPtr : valid pointer to the listview structure
3440 * Number of items selected.
3442 static INT
LISTVIEW_GetSelectedCount(const LISTVIEW_INFO
*infoPtr
)
3444 INT nSelectedCount
= 0;
3446 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3449 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
3451 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
3456 nSelectedCount
= ranges_itemcount(infoPtr
->selectionRanges
);
3458 TRACE("nSelectedCount=%d\n", nSelectedCount
);
3459 return nSelectedCount
;
3464 * Manages the item focus.
3467 * [I] infoPtr : valid pointer to the listview structure
3468 * [I] nItem : item index
3471 * TRUE : focused item changed
3472 * FALSE : focused item has NOT changed
3474 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3476 INT oldFocus
= infoPtr
->nFocusedItem
;
3479 if (nItem
== infoPtr
->nFocusedItem
) return FALSE
;
3481 lvItem
.state
= nItem
== -1 ? 0 : LVIS_FOCUSED
;
3482 lvItem
.stateMask
= LVIS_FOCUSED
;
3483 LISTVIEW_SetItemState(infoPtr
, nItem
== -1 ? infoPtr
->nFocusedItem
: nItem
, &lvItem
);
3485 return oldFocus
!= infoPtr
->nFocusedItem
;
3488 static INT
shift_item(const LISTVIEW_INFO
*infoPtr
, INT nShiftItem
, INT nItem
, INT direction
)
3490 if (nShiftItem
< nItem
) return nShiftItem
;
3492 if (nShiftItem
> nItem
) return nShiftItem
+ direction
;
3494 if (direction
> 0) return nShiftItem
+ direction
;
3496 return min(nShiftItem
, infoPtr
->nItemCount
- 1);
3499 /* This function updates focus index.
3502 focus : current focus index
3503 item : index of item to be added/removed
3504 direction : add/remove flag
3506 static void LISTVIEW_ShiftFocus(LISTVIEW_INFO
*infoPtr
, INT focus
, INT item
, INT direction
)
3508 BOOL old_change
= infoPtr
->bDoChangeNotify
;
3510 infoPtr
->bDoChangeNotify
= FALSE
;
3511 focus
= shift_item(infoPtr
, focus
, item
, direction
);
3512 if (focus
!= infoPtr
->nFocusedItem
)
3513 LISTVIEW_SetItemFocus(infoPtr
, focus
);
3514 infoPtr
->bDoChangeNotify
= old_change
;
3519 * Updates the various indices after an item has been inserted or deleted.
3522 * [I] infoPtr : valid pointer to the listview structure
3523 * [I] nItem : item index
3524 * [I] direction : Direction of shift, +1 or -1.
3529 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
3531 TRACE("Shifting %i, %i steps\n", nItem
, direction
);
3533 ranges_shift(infoPtr
->selectionRanges
, nItem
, direction
, infoPtr
->nItemCount
);
3534 assert(abs(direction
) == 1);
3535 infoPtr
->nSelectionMark
= shift_item(infoPtr
, infoPtr
->nSelectionMark
, nItem
, direction
);
3537 /* But we are not supposed to modify nHotItem! */
3542 * Adds a block of selections.
3545 * [I] infoPtr : valid pointer to the listview structure
3546 * [I] nItem : item index
3549 * Whether the window is still valid.
3551 static BOOL
LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3553 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
3554 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
3555 HWND hwndSelf
= infoPtr
->hwndSelf
;
3556 NMLVODSTATECHANGE nmlv
;
3561 /* Temporarily disable change notification
3562 * If the control is LVS_OWNERDATA, we need to send
3563 * only one LVN_ODSTATECHANGED notification.
3564 * See MSDN documentation for LVN_ITEMCHANGED.
3566 bOldChange
= infoPtr
->bDoChangeNotify
;
3567 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3569 if (nFirst
== -1) nFirst
= nItem
;
3571 item
.state
= LVIS_SELECTED
;
3572 item
.stateMask
= LVIS_SELECTED
;
3574 for (i
= nFirst
; i
<= nLast
; i
++)
3575 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
3577 ZeroMemory(&nmlv
, sizeof(nmlv
));
3578 nmlv
.iFrom
= nFirst
;
3581 nmlv
.uNewState
= item
.state
;
3583 notify_hdr(infoPtr
, LVN_ODSTATECHANGED
, (LPNMHDR
)&nmlv
);
3584 if (!IsWindow(hwndSelf
))
3586 infoPtr
->bDoChangeNotify
= bOldChange
;
3593 * Sets a single group selection.
3596 * [I] infoPtr : valid pointer to the listview structure
3597 * [I] nItem : item index
3602 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3609 if (!(selection
= ranges_create(100))) return;
3611 item
.state
= LVIS_SELECTED
;
3612 item
.stateMask
= LVIS_SELECTED
;
3614 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
3616 if (infoPtr
->nSelectionMark
== -1)
3618 infoPtr
->nSelectionMark
= nItem
;
3619 ranges_additem(selection
, nItem
);
3625 sel
.lower
= min(infoPtr
->nSelectionMark
, nItem
);
3626 sel
.upper
= max(infoPtr
->nSelectionMark
, nItem
) + 1;
3627 ranges_add(selection
, sel
);
3632 RECT rcItem
, rcSel
, rcSelMark
;
3635 rcItem
.left
= LVIR_BOUNDS
;
3636 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) {
3637 ranges_destroy (selection
);
3640 rcSelMark
.left
= LVIR_BOUNDS
;
3641 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) {
3642 ranges_destroy (selection
);
3645 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
3646 iterator_frameditems(&i
, infoPtr
, &rcSel
);
3647 while(iterator_next(&i
))
3649 LISTVIEW_GetItemPosition(infoPtr
, i
.nItem
, &ptItem
);
3650 if (PtInRect(&rcSel
, ptItem
)) ranges_additem(selection
, i
.nItem
);
3652 iterator_destroy(&i
);
3655 /* disable per item notifications on LVS_OWNERDATA style
3656 FIXME: single LVN_ODSTATECHANGED should be used */
3657 bOldChange
= infoPtr
->bDoChangeNotify
;
3658 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3660 LISTVIEW_DeselectAllSkipItems(infoPtr
, selection
);
3663 iterator_rangesitems(&i
, selection
);
3664 while(iterator_next(&i
))
3665 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &item
);
3666 /* this will also destroy the selection */
3667 iterator_destroy(&i
);
3669 infoPtr
->bDoChangeNotify
= bOldChange
;
3671 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3676 * Sets a single selection.
3679 * [I] infoPtr : valid pointer to the listview structure
3680 * [I] nItem : item index
3685 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3689 TRACE("nItem=%d\n", nItem
);
3691 LISTVIEW_DeselectAllSkipItem(infoPtr
, nItem
);
3693 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3694 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3695 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3697 infoPtr
->nSelectionMark
= nItem
;
3702 * Set selection(s) with keyboard.
3705 * [I] infoPtr : valid pointer to the listview structure
3706 * [I] nItem : item index
3707 * [I] space : VK_SPACE code sent
3710 * SUCCESS : TRUE (needs to be repainted)
3711 * FAILURE : FALSE (nothing has changed)
3713 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL space
)
3715 /* FIXME: pass in the state */
3716 WORD wShift
= GetKeyState(VK_SHIFT
) & 0x8000;
3717 WORD wCtrl
= GetKeyState(VK_CONTROL
) & 0x8000;
3718 BOOL bResult
= FALSE
;
3720 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem
, wShift
, wCtrl
);
3721 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3725 if (infoPtr
->dwStyle
& LVS_SINGLESEL
|| (wShift
== 0 && wCtrl
== 0))
3726 LISTVIEW_SetSelection(infoPtr
, nItem
);
3730 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3734 lvItem
.state
= ~LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3735 lvItem
.stateMask
= LVIS_SELECTED
;
3738 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3739 if (lvItem
.state
& LVIS_SELECTED
)
3740 infoPtr
->nSelectionMark
= nItem
;
3742 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3745 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3748 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3752 static BOOL
LISTVIEW_GetItemAtPt(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, POINT pt
)
3754 LVHITTESTINFO lvHitTestInfo
;
3756 ZeroMemory(&lvHitTestInfo
, sizeof(lvHitTestInfo
));
3757 lvHitTestInfo
.pt
.x
= pt
.x
;
3758 lvHitTestInfo
.pt
.y
= pt
.y
;
3760 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
3762 lpLVItem
->mask
= LVIF_PARAM
;
3763 lpLVItem
->iItem
= lvHitTestInfo
.iItem
;
3764 lpLVItem
->iSubItem
= 0;
3766 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
3769 static inline BOOL
LISTVIEW_IsHotTracking(const LISTVIEW_INFO
*infoPtr
)
3771 return ((infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) ||
3772 (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
) ||
3773 (infoPtr
->dwLvExStyle
& LVS_EX_TWOCLICKACTIVATE
));
3778 * Called when the mouse is being actively tracked and has hovered for a specified
3782 * [I] infoPtr : valid pointer to the listview structure
3783 * [I] fwKeys : key indicator
3784 * [I] x,y : mouse position
3787 * 0 if the message was processed, non-zero if there was an error
3790 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3791 * over the item for a certain period of time.
3794 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, INT x
, INT y
)
3798 if (notify_hdr(infoPtr
, NM_HOVER
, &hdr
)) return 0;
3800 if (LISTVIEW_IsHotTracking(infoPtr
))
3808 if (LISTVIEW_GetItemAtPt(infoPtr
, &item
, pt
))
3809 LISTVIEW_SetSelection(infoPtr
, item
.iItem
);
3811 SetFocus(infoPtr
->hwndSelf
);
3817 #define SCROLL_LEFT 0x1
3818 #define SCROLL_RIGHT 0x2
3819 #define SCROLL_UP 0x4
3820 #define SCROLL_DOWN 0x8
3824 * Utility routine to draw and highlight items within a marquee selection rectangle.
3827 * [I] infoPtr : valid pointer to the listview structure
3828 * [I] coords_orig : original co-ordinates of the cursor
3829 * [I] coords_offs : offsetted coordinates of the cursor
3830 * [I] offset : offset amount
3831 * [I] scroll : Bitmask of which directions we should scroll, if at all
3836 static void LISTVIEW_MarqueeHighlight(LISTVIEW_INFO
*infoPtr
, const POINT
*coords_orig
,
3839 BOOL controlDown
= FALSE
;
3841 ITERATOR old_elems
, new_elems
;
3843 POINT coords_offs
, offset
;
3845 /* Ensure coordinates are within client bounds */
3846 coords_offs
.x
= max(min(coords_orig
->x
, infoPtr
->rcList
.right
), 0);
3847 coords_offs
.y
= max(min(coords_orig
->y
, infoPtr
->rcList
.bottom
), 0);
3850 LISTVIEW_GetOrigin(infoPtr
, &offset
);
3852 /* Offset coordinates by the appropriate amount */
3853 coords_offs
.x
-= offset
.x
;
3854 coords_offs
.y
-= offset
.y
;
3856 if (coords_offs
.x
> infoPtr
->marqueeOrigin
.x
)
3858 rect
.left
= infoPtr
->marqueeOrigin
.x
;
3859 rect
.right
= coords_offs
.x
;
3863 rect
.left
= coords_offs
.x
;
3864 rect
.right
= infoPtr
->marqueeOrigin
.x
;
3867 if (coords_offs
.y
> infoPtr
->marqueeOrigin
.y
)
3869 rect
.top
= infoPtr
->marqueeOrigin
.y
;
3870 rect
.bottom
= coords_offs
.y
;
3874 rect
.top
= coords_offs
.y
;
3875 rect
.bottom
= infoPtr
->marqueeOrigin
.y
;
3878 /* Cancel out the old marquee rectangle and draw the new one */
3879 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeDrawRect
);
3881 /* Scroll by the appropriate distance if applicable - speed up scrolling as
3882 the cursor is further away */
3884 if ((scroll
& SCROLL_LEFT
) && (coords_orig
->x
<= 0))
3885 LISTVIEW_Scroll(infoPtr
, coords_orig
->x
, 0);
3887 if ((scroll
& SCROLL_RIGHT
) && (coords_orig
->x
>= infoPtr
->rcList
.right
))
3888 LISTVIEW_Scroll(infoPtr
, (coords_orig
->x
- infoPtr
->rcList
.right
), 0);
3890 if ((scroll
& SCROLL_UP
) && (coords_orig
->y
<= 0))
3891 LISTVIEW_Scroll(infoPtr
, 0, coords_orig
->y
);
3893 if ((scroll
& SCROLL_DOWN
) && (coords_orig
->y
>= infoPtr
->rcList
.bottom
))
3894 LISTVIEW_Scroll(infoPtr
, 0, (coords_orig
->y
- infoPtr
->rcList
.bottom
));
3896 iterator_frameditems_absolute(&old_elems
, infoPtr
, &infoPtr
->marqueeRect
);
3898 infoPtr
->marqueeRect
= rect
;
3899 infoPtr
->marqueeDrawRect
= rect
;
3900 OffsetRect(&infoPtr
->marqueeDrawRect
, offset
.x
, offset
.y
);
3902 iterator_frameditems_absolute(&new_elems
, infoPtr
, &infoPtr
->marqueeRect
);
3903 iterator_remove_common_items(&old_elems
, &new_elems
);
3905 /* Iterate over no longer selected items */
3906 while (iterator_next(&old_elems
))
3908 if (old_elems
.nItem
> -1)
3910 if (LISTVIEW_GetItemState(infoPtr
, old_elems
.nItem
, LVIS_SELECTED
) == LVIS_SELECTED
)
3913 item
.state
= LVIS_SELECTED
;
3915 item
.stateMask
= LVIS_SELECTED
;
3917 LISTVIEW_SetItemState(infoPtr
, old_elems
.nItem
, &item
);
3920 iterator_destroy(&old_elems
);
3923 /* Iterate over newly selected items */
3924 if (GetKeyState(VK_CONTROL
) & 0x8000)
3927 while (iterator_next(&new_elems
))
3929 if (new_elems
.nItem
> -1)
3931 /* If CTRL is pressed, invert. If not, always select the item. */
3932 if ((controlDown
) && (LISTVIEW_GetItemState(infoPtr
, new_elems
.nItem
, LVIS_SELECTED
)))
3935 item
.state
= LVIS_SELECTED
;
3937 item
.stateMask
= LVIS_SELECTED
;
3939 LISTVIEW_SetItemState(infoPtr
, new_elems
.nItem
, &item
);
3942 iterator_destroy(&new_elems
);
3944 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeDrawRect
);
3949 * Called when we are in a marquee selection that involves scrolling the listview (ie,
3950 * the cursor is outside the bounds of the client area). This is a TIMERPROC.
3953 * [I] hwnd : Handle to the listview
3954 * [I] uMsg : WM_TIMER (ignored)
3955 * [I] idEvent : The timer ID interpreted as a pointer to a LISTVIEW_INFO struct
3956 * [I] dwTimer : The elapsed time (ignored)
3961 static VOID CALLBACK
LISTVIEW_ScrollTimer(HWND hWnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
3963 LISTVIEW_INFO
*infoPtr
;
3964 SCROLLINFO scrollInfo
;
3968 infoPtr
= (LISTVIEW_INFO
*) idEvent
;
3973 /* Get the current cursor position and convert to client coordinates */
3974 GetCursorPos(&coords
);
3975 ScreenToClient(hWnd
, &coords
);
3977 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3978 scrollInfo
.fMask
= SIF_ALL
;
3980 /* Work out in which directions we can scroll */
3981 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3983 if (scrollInfo
.nPos
!= scrollInfo
.nMin
)
3984 scroll
|= SCROLL_UP
;
3986 if (((scrollInfo
.nPage
+ scrollInfo
.nPos
) - 1) != scrollInfo
.nMax
)
3987 scroll
|= SCROLL_DOWN
;
3990 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
3992 if (scrollInfo
.nPos
!= scrollInfo
.nMin
)
3993 scroll
|= SCROLL_LEFT
;
3995 if (((scrollInfo
.nPage
+ scrollInfo
.nPos
) - 1) != scrollInfo
.nMax
)
3996 scroll
|= SCROLL_RIGHT
;
3999 if (((coords
.x
<= 0) && (scroll
& SCROLL_LEFT
)) ||
4000 ((coords
.y
<= 0) && (scroll
& SCROLL_UP
)) ||
4001 ((coords
.x
>= infoPtr
->rcList
.right
) && (scroll
& SCROLL_RIGHT
)) ||
4002 ((coords
.y
>= infoPtr
->rcList
.bottom
) && (scroll
& SCROLL_DOWN
)))
4004 LISTVIEW_MarqueeHighlight(infoPtr
, &coords
, scroll
);
4010 * Called whenever WM_MOUSEMOVE is received.
4013 * [I] infoPtr : valid pointer to the listview structure
4014 * [I] fwKeys : key indicator
4015 * [I] x,y : mouse position
4018 * 0 if the message is processed, non-zero if there was an error
4020 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
4029 if (!(fwKeys
& MK_LBUTTON
))
4030 infoPtr
->bLButtonDown
= FALSE
;
4032 if (infoPtr
->bLButtonDown
)
4034 rect
.left
= rect
.right
= infoPtr
->ptClickPos
.x
;
4035 rect
.top
= rect
.bottom
= infoPtr
->ptClickPos
.y
;
4037 InflateRect(&rect
, GetSystemMetrics(SM_CXDRAG
), GetSystemMetrics(SM_CYDRAG
));
4039 if (infoPtr
->bMarqueeSelect
)
4041 /* Enable the timer if we're going outside our bounds, in case the user doesn't
4042 move the mouse again */
4044 if ((x
<= 0) || (y
<= 0) || (x
>= infoPtr
->rcList
.right
) ||
4045 (y
>= infoPtr
->rcList
.bottom
))
4047 if (!infoPtr
->bScrolling
)
4049 infoPtr
->bScrolling
= TRUE
;
4050 SetTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
, 1, LISTVIEW_ScrollTimer
);
4055 infoPtr
->bScrolling
= FALSE
;
4056 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
);
4059 LISTVIEW_MarqueeHighlight(infoPtr
, &pt
, 0);
4064 LISTVIEW_HitTest(infoPtr
, &ht
, TRUE
, TRUE
);
4066 /* reset item marker */
4067 if (infoPtr
->nLButtonDownItem
!= ht
.iItem
)
4068 infoPtr
->nLButtonDownItem
= -1;
4070 if (!PtInRect(&rect
, pt
))
4072 /* this path covers the following:
4073 1. WM_LBUTTONDOWN over selected item (sets focus on it)
4074 2. change focus with keys
4075 3. move mouse over item from step 1 selects it and moves focus on it */
4076 if (infoPtr
->nLButtonDownItem
!= -1 &&
4077 !LISTVIEW_GetItemState(infoPtr
, infoPtr
->nLButtonDownItem
, LVIS_SELECTED
))
4081 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
4082 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
4084 LISTVIEW_SetItemState(infoPtr
, infoPtr
->nLButtonDownItem
, &lvItem
);
4085 infoPtr
->nLButtonDownItem
= -1;
4088 if (!infoPtr
->bDragging
)
4090 ht
.pt
= infoPtr
->ptClickPos
;
4091 LISTVIEW_HitTest(infoPtr
, &ht
, TRUE
, TRUE
);
4093 /* If the click is outside the range of an item, begin a
4094 highlight. If not, begin an item drag. */
4099 /* If we're allowing multiple selections, send notification.
4100 If return value is non-zero, cancel. */
4101 if (!(infoPtr
->dwStyle
& LVS_SINGLESEL
) && (notify_hdr(infoPtr
, LVN_MARQUEEBEGIN
, &hdr
) == 0))
4103 /* Store the absolute coordinates of the click */
4105 LISTVIEW_GetOrigin(infoPtr
, &offset
);
4107 infoPtr
->marqueeOrigin
.x
= infoPtr
->ptClickPos
.x
- offset
.x
;
4108 infoPtr
->marqueeOrigin
.y
= infoPtr
->ptClickPos
.y
- offset
.y
;
4110 /* Begin selection and capture mouse */
4111 infoPtr
->bMarqueeSelect
= TRUE
;
4112 SetCapture(infoPtr
->hwndSelf
);
4119 ZeroMemory(&nmlv
, sizeof(nmlv
));
4120 nmlv
.iItem
= ht
.iItem
;
4121 nmlv
.ptAction
= infoPtr
->ptClickPos
;
4123 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
4124 infoPtr
->bDragging
= TRUE
;
4132 /* see if we are supposed to be tracking mouse hovering */
4133 if (LISTVIEW_IsHotTracking(infoPtr
)) {
4134 TRACKMOUSEEVENT trackinfo
;
4137 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
4138 trackinfo
.dwFlags
= TME_QUERY
;
4140 /* see if we are already tracking this hwnd */
4141 _TrackMouseEvent(&trackinfo
);
4144 if(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)
4147 if((trackinfo
.dwFlags
& flags
) != flags
|| trackinfo
.hwndTrack
!= infoPtr
->hwndSelf
) {
4148 trackinfo
.dwFlags
= flags
;
4149 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
4150 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
4152 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
4153 _TrackMouseEvent(&trackinfo
);
4162 * Tests whether the item is assignable to a list with style lStyle
4164 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
4166 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
4167 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
4168 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
4176 * Helper for LISTVIEW_SetItemT and LISTVIEW_InsertItemT: sets item attributes.
4179 * [I] infoPtr : valid pointer to the listview structure
4180 * [I] lpLVItem : valid pointer to new item attributes
4181 * [I] isNew : the item being set is being inserted
4182 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4183 * [O] bChanged : will be set to TRUE if the item really changed
4189 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
4195 /* stateMask is ignored for LVM_INSERTITEM */
4196 UINT stateMask
= isNew
? ~0 : lpLVItem
->stateMask
;
4200 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
4202 if (lpLVItem
->mask
== 0) return TRUE
;
4204 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4206 /* a virtual listview only stores selection and focus */
4207 if (lpLVItem
->mask
& ~LVIF_STATE
)
4213 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
4214 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
4218 /* we need to get the lParam and state of the item */
4219 item
.iItem
= lpLVItem
->iItem
;
4220 item
.iSubItem
= lpLVItem
->iSubItem
;
4221 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
4222 item
.stateMask
= (infoPtr
->dwStyle
& LVS_OWNERDATA
) ? LVIS_FOCUSED
| LVIS_SELECTED
: ~0;
4226 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
4228 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
4229 /* determine what fields will change */
4230 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & stateMask
& ~infoPtr
->uCallbackMask
))
4231 uChanged
|= LVIF_STATE
;
4233 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
4234 uChanged
|= LVIF_IMAGE
;
4236 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
4237 uChanged
|= LVIF_PARAM
;
4239 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
4240 uChanged
|= LVIF_INDENT
;
4242 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
4243 uChanged
|= LVIF_TEXT
;
4245 TRACE("change mask=0x%x\n", uChanged
);
4247 memset(&nmlv
, 0, sizeof(NMLISTVIEW
));
4248 nmlv
.iItem
= lpLVItem
->iItem
;
4249 nmlv
.uNewState
= (item
.state
& ~stateMask
) | (lpLVItem
->state
& stateMask
);
4250 nmlv
.uOldState
= item
.state
;
4251 nmlv
.uChanged
= uChanged
? uChanged
: lpLVItem
->mask
;
4252 nmlv
.lParam
= item
.lParam
;
4254 /* Send LVN_ITEMCHANGING notification, if the item is not being inserted
4255 and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications
4256 are enabled. Even nothing really changed we still need to send this,
4257 in this case uChanged mask is just set to passed item mask. */
4258 if(lpItem
&& !isNew
&& infoPtr
->bDoChangeNotify
)
4260 HWND hwndSelf
= infoPtr
->hwndSelf
;
4262 if (notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
4264 if (!IsWindow(hwndSelf
))
4268 /* When item is inserted we need to shift existing focus index if new item has lower index. */
4269 if (isNew
&& (stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
) &&
4270 /* this means we won't hit a focus change path later */
4271 ((uChanged
& LVIF_STATE
) == 0 || (!(lpLVItem
->state
& LVIS_FOCUSED
) && (infoPtr
->nFocusedItem
!= lpLVItem
->iItem
))))
4273 if (infoPtr
->nFocusedItem
!= -1 && (lpLVItem
->iItem
<= infoPtr
->nFocusedItem
))
4274 infoPtr
->nFocusedItem
++;
4277 if (!uChanged
) return TRUE
;
4280 /* copy information */
4281 if (lpLVItem
->mask
& LVIF_TEXT
)
4282 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
4284 if (lpLVItem
->mask
& LVIF_IMAGE
)
4285 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
4287 if (lpLVItem
->mask
& LVIF_PARAM
)
4288 lpItem
->lParam
= lpLVItem
->lParam
;
4290 if (lpLVItem
->mask
& LVIF_INDENT
)
4291 lpItem
->iIndent
= lpLVItem
->iIndent
;
4293 if (uChanged
& LVIF_STATE
)
4295 if (lpItem
&& (stateMask
& ~infoPtr
->uCallbackMask
))
4297 lpItem
->state
&= ~stateMask
;
4298 lpItem
->state
|= (lpLVItem
->state
& stateMask
);
4300 if (lpLVItem
->state
& stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
4302 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
4303 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
4305 else if (stateMask
& LVIS_SELECTED
)
4307 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
4309 /* If we are asked to change focus, and we manage it, do it.
4310 It's important to have all new item data stored at this point,
4311 because changing existing focus could result in a redrawing operation,
4312 which in turn could ask for disp data, application should see all data
4313 for inserted item when processing LVN_GETDISPINFO.
4315 The way this works application will see nested item change notifications -
4316 changed item notifications interrupted by ones from item losing focus. */
4317 if (stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
4319 if (lpLVItem
->state
& LVIS_FOCUSED
)
4321 /* update selection mark */
4322 if (infoPtr
->nFocusedItem
== -1 && infoPtr
->nSelectionMark
== -1)
4323 infoPtr
->nSelectionMark
= lpLVItem
->iItem
;
4325 if (infoPtr
->nFocusedItem
!= -1)
4327 /* remove current focus */
4328 item
.mask
= LVIF_STATE
;
4330 item
.stateMask
= LVIS_FOCUSED
;
4332 /* recurse with redrawing an item */
4333 LISTVIEW_SetItemState(infoPtr
, infoPtr
->nFocusedItem
, &item
);
4336 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
4337 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, infoPtr
->uView
== LV_VIEW_LIST
);
4339 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
4341 infoPtr
->nFocusedItem
= -1;
4346 /* if we're inserting the item, we're done */
4347 if (isNew
) return TRUE
;
4349 /* send LVN_ITEMCHANGED notification */
4350 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
4351 if (infoPtr
->bDoChangeNotify
) notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
4358 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
4361 * [I] infoPtr : valid pointer to the listview structure
4362 * [I] lpLVItem : valid pointer to new subitem attributes
4363 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4364 * [O] bChanged : will be set to TRUE if the item really changed
4370 static BOOL
set_sub_item(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
4373 SUBITEM_INFO
*lpSubItem
;
4375 /* we do not support subitems for virtual listviews */
4376 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
4378 /* set subitem only if column is present */
4379 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4381 /* First do some sanity checks */
4382 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
4383 particularly useful. We currently do not actually do anything with
4384 the flag on subitems.
4386 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
| LVIF_DI_SETITEM
)) return FALSE
;
4387 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
))) return TRUE
;
4389 /* get the subitem structure, and create it if not there */
4390 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
4391 assert (hdpaSubItems
);
4393 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
4396 SUBITEM_INFO
*tmpSubItem
;
4399 lpSubItem
= Alloc(sizeof(SUBITEM_INFO
));
4400 if (!lpSubItem
) return FALSE
;
4401 /* we could binary search here, if need be...*/
4402 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4404 tmpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
4405 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
4407 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
4412 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
4413 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
4417 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
))
4419 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
4423 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
4425 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
4434 * Sets item attributes.
4437 * [I] infoPtr : valid pointer to the listview structure
4438 * [I] lpLVItem : new item attributes
4439 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4445 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, LVITEMW
*lpLVItem
, BOOL isW
)
4447 HWND hwndSelf
= infoPtr
->hwndSelf
;
4448 LPWSTR pszText
= NULL
;
4449 BOOL bResult
, bChanged
= FALSE
;
4452 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
4454 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
4457 /* Store old item area */
4458 LISTVIEW_GetItemBox(infoPtr
, lpLVItem
->iItem
, &oldItemArea
);
4460 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
4461 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_text(lpLVItem
->pszText
))
4463 pszText
= lpLVItem
->pszText
;
4464 lpLVItem
->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
4467 /* actually set the fields */
4468 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
4470 if (lpLVItem
->iSubItem
)
4471 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
4473 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
4474 if (!IsWindow(hwndSelf
))
4477 /* redraw item, if necessary */
4478 if (bChanged
&& !infoPtr
->bIsDrawing
)
4480 /* this little optimization eliminates some nasty flicker */
4481 if ( infoPtr
->uView
== LV_VIEW_DETAILS
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
4482 !(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) &&
4483 lpLVItem
->iSubItem
> 0 && lpLVItem
->iSubItem
<= DPA_GetPtrCount(infoPtr
->hdpaColumns
) )
4484 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
4487 LISTVIEW_InvalidateRect(infoPtr
, &oldItemArea
);
4488 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
4494 textfreeT(lpLVItem
->pszText
, isW
);
4495 lpLVItem
->pszText
= pszText
;
4503 * Retrieves the index of the item at coordinate (0, 0) of the client area.
4506 * [I] infoPtr : valid pointer to the listview structure
4511 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*infoPtr
)
4514 SCROLLINFO scrollInfo
;
4516 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
4517 scrollInfo
.fMask
= SIF_POS
;
4519 if (infoPtr
->uView
== LV_VIEW_LIST
)
4521 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
4522 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
4524 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
4526 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
4527 nItem
= scrollInfo
.nPos
;
4531 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
4532 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
4535 TRACE("nItem=%d\n", nItem
);
4543 * Erases the background of the given rectangle
4546 * [I] infoPtr : valid pointer to the listview structure
4547 * [I] hdc : device context handle
4548 * [I] lprcBox : clipping rectangle
4554 static inline BOOL
LISTVIEW_FillBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
4556 if (!infoPtr
->hBkBrush
) return FALSE
;
4558 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, wine_dbgstr_rect(lprcBox
), infoPtr
->hBkBrush
);
4560 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
4563 /* Draw main item or subitem */
4564 static void LISTVIEW_DrawItemPart(LISTVIEW_INFO
*infoPtr
, LVITEMW
*item
, const NMLVCUSTOMDRAW
*nmlvcd
, const POINT
*pos
)
4566 RECT rcSelect
, rcLabel
, rcBox
, rcStateIcon
, rcIcon
;
4567 const RECT
*background
;
4572 /* now check if we need to update the focus rectangle */
4573 focus
= infoPtr
->bFocus
&& (item
->state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
4574 if (!focus
) item
->state
&= ~LVIS_FOCUSED
;
4576 LISTVIEW_GetItemMetrics(infoPtr
, item
, &rcBox
, &rcSelect
, &rcIcon
, &rcStateIcon
, &rcLabel
);
4577 OffsetRect(&rcBox
, pos
->x
, pos
->y
);
4578 OffsetRect(&rcSelect
, pos
->x
, pos
->y
);
4579 OffsetRect(&rcIcon
, pos
->x
, pos
->y
);
4580 OffsetRect(&rcStateIcon
, pos
->x
, pos
->y
);
4581 OffsetRect(&rcLabel
, pos
->x
, pos
->y
);
4582 TRACE("%d: box=%s, select=%s, icon=%s. label=%s\n", item
->iSubItem
,
4583 wine_dbgstr_rect(&rcBox
), wine_dbgstr_rect(&rcSelect
),
4584 wine_dbgstr_rect(&rcIcon
), wine_dbgstr_rect(&rcLabel
));
4586 /* FIXME: temporary hack */
4587 rcSelect
.left
= rcLabel
.left
;
4589 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& item
->iSubItem
== 0)
4591 if (!(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
4592 OffsetRect(&rcSelect
, LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
, 0);
4593 OffsetRect(&rcIcon
, LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
, 0);
4594 OffsetRect(&rcStateIcon
, LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
, 0);
4595 OffsetRect(&rcLabel
, LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
, 0);
4598 /* in icon mode, the label rect is really what we want to draw the
4600 /* in detail mode, we want to paint background for label rect when
4601 * item is not selected or listview has full row select; otherwise paint
4602 * background for text only */
4603 if ( infoPtr
->uView
== LV_VIEW_ICON
||
4604 (infoPtr
->uView
== LV_VIEW_DETAILS
&& (!(item
->state
& LVIS_SELECTED
) ||
4605 (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))))
4606 background
= &rcLabel
;
4608 background
= &rcSelect
;
4610 if (nmlvcd
->clrTextBk
!= CLR_NONE
)
4611 ExtTextOutW(nmlvcd
->nmcd
.hdc
, background
->left
, background
->top
, ETO_OPAQUE
, background
, NULL
, 0, NULL
);
4613 if (item
->state
& LVIS_FOCUSED
)
4615 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
4617 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4619 /* we have to update left focus bound too if item isn't in leftmost column
4620 and reduce right box bound */
4621 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
4625 if ((leftmost
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, 0, 0)))
4627 INT Originx
= pos
->x
- LISTVIEW_GetColumnInfo(infoPtr
, leftmost
)->rcHeader
.left
;
4628 INT rightmost
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
4629 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
4631 rcBox
.right
= LISTVIEW_GetColumnInfo(infoPtr
, rightmost
)->rcHeader
.right
+ Originx
;
4632 rcSelect
.left
= LISTVIEW_GetColumnInfo(infoPtr
, leftmost
)->rcHeader
.left
+ Originx
;
4635 rcSelect
.right
= rcBox
.right
;
4637 infoPtr
->rcFocus
= rcSelect
;
4640 infoPtr
->rcFocus
= rcLabel
;
4644 if (infoPtr
->himlState
&& STATEIMAGEINDEX(item
->state
) && (item
->iSubItem
== 0))
4646 UINT stateimage
= STATEIMAGEINDEX(item
->state
);
4649 TRACE("stateimage=%d\n", stateimage
);
4650 ImageList_Draw(infoPtr
->himlState
, stateimage
-1, nmlvcd
->nmcd
.hdc
, rcStateIcon
.left
, rcStateIcon
.top
, ILD_NORMAL
);
4655 himl
= (infoPtr
->uView
== LV_VIEW_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
4656 if (himl
&& item
->iImage
>= 0 && !IsRectEmpty(&rcIcon
))
4660 TRACE("iImage=%d\n", item
->iImage
);
4662 if (item
->state
& (LVIS_SELECTED
| LVIS_CUT
) && infoPtr
->bFocus
)
4663 style
= ILD_SELECTED
;
4667 ImageList_DrawEx(himl
, item
->iImage
, nmlvcd
->nmcd
.hdc
, rcIcon
.left
, rcIcon
.top
,
4668 rcIcon
.right
- rcIcon
.left
, rcIcon
.bottom
- rcIcon
.top
, infoPtr
->clrBk
,
4669 item
->state
& LVIS_CUT
? RGB(255, 255, 255) : CLR_DEFAULT
,
4670 style
| (item
->state
& LVIS_OVERLAYMASK
));
4673 /* Don't bother painting item being edited */
4674 if (infoPtr
->hwndEdit
&& item
->iItem
== infoPtr
->nEditLabelItem
&& item
->iSubItem
== 0) return;
4676 /* figure out the text drawing flags */
4677 format
= (infoPtr
->uView
== LV_VIEW_ICON
? (focus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
4678 if (infoPtr
->uView
== LV_VIEW_ICON
)
4679 format
= (focus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
4680 else if (item
->iSubItem
)
4682 switch (LISTVIEW_GetColumnInfo(infoPtr
, item
->iSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
4684 case LVCFMT_RIGHT
: format
|= DT_RIGHT
; break;
4685 case LVCFMT_CENTER
: format
|= DT_CENTER
; break;
4686 default: format
|= DT_LEFT
;
4689 if (!(format
& (DT_RIGHT
| DT_CENTER
)))
4691 if (himl
&& item
->iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
4692 else rcLabel
.left
+= LABEL_HOR_PADDING
;
4694 else if (format
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
4696 /* for GRIDLINES reduce the bottom so the text formats correctly */
4697 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
4700 DrawTextW(nmlvcd
->nmcd
.hdc
, item
->pszText
, -1, &rcLabel
, format
);
4708 * [I] infoPtr : valid pointer to the listview structure
4709 * [I] hdc : device context handle
4710 * [I] nItem : item index
4711 * [I] nSubItem : subitem index
4712 * [I] pos : item position in client coordinates
4713 * [I] cdmode : custom draw mode
4719 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, ITERATOR
*subitems
, POINT pos
, DWORD cdmode
)
4721 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
4722 static WCHAR callbackW
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
4723 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
4725 NMLVCUSTOMDRAW nmlvcd
;
4728 TRACE("(hdc=%p, nItem=%d, subitems=%p, pos=%s)\n", hdc
, nItem
, subitems
, wine_dbgstr_point(&pos
));
4730 /* get information needed for drawing the item */
4731 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_PARAM
| LVIF_STATE
;
4732 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
4733 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
| LVIS_CUT
| LVIS_OVERLAYMASK
;
4734 lvItem
.iItem
= nItem
;
4735 lvItem
.iSubItem
= 0;
4738 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4739 lvItem
.pszText
= szDispText
;
4740 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
4741 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= callbackW
;
4742 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
4744 /* now check if we need to update the focus rectangle */
4745 focus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
4746 if (!focus
) lvItem
.state
&= ~LVIS_FOCUSED
;
4748 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, NULL
, NULL
, NULL
);
4749 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
4751 /* Full custom draw stage sequence looks like this:
4756 - CDDS_ITEMPREPAINT|CDDS_SUBITEM | => sent n times, where n is number of subitems,
4757 CDDS_ITEMPOSTPAINT|CDDS_SUBITEM | including item itself
4758 - CDDS_ITEMPOSTPAINT
4763 - CDDS_ITEMPOSTPAINT
4766 /* fill in the custom draw structure */
4767 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
4768 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
4769 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_ITEMPREPAINT
, &nmlvcd
);
4770 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
4774 while (iterator_next(subitems
))
4776 DWORD subitemstage
= CDRF_DODEFAULT
;
4778 /* We need to query for each subitem, item's data (subitem == 0) is already here at this point */
4779 if (subitems
->nItem
)
4781 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_PARAM
| LVIF_INDENT
;
4782 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
| LVIS_CUT
| LVIS_OVERLAYMASK
;
4783 lvItem
.iItem
= nItem
;
4784 lvItem
.iSubItem
= subitems
->nItem
;
4787 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4788 lvItem
.pszText
= szDispText
;
4789 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
4790 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4791 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
4792 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= callbackW
;
4793 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
4795 /* update custom draw data */
4796 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &nmlvcd
.nmcd
.rc
, NULL
, NULL
, NULL
, NULL
);
4797 OffsetRect(&nmlvcd
.nmcd
.rc
, pos
.x
, pos
.y
);
4798 nmlvcd
.iSubItem
= subitems
->nItem
;
4801 if (cdsubitemmode
& CDRF_NOTIFYSUBITEMDRAW
)
4802 subitemstage
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
4805 nmlvcd
.clrTextBk
= infoPtr
->clrTextBk
;
4806 nmlvcd
.clrText
= infoPtr
->clrText
;
4809 if (subitems
->nItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
4810 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
4811 else if (!(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
4812 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, TRUE
);
4814 if (!(subitemstage
& CDRF_SKIPDEFAULT
))
4815 LISTVIEW_DrawItemPart(infoPtr
, &lvItem
, &nmlvcd
, &pos
);
4817 if (subitemstage
& CDRF_NOTIFYPOSTPAINT
)
4818 subitemstage
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPOSTPAINT
, &nmlvcd
);
4823 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
4824 LISTVIEW_DrawItemPart(infoPtr
, &lvItem
, &nmlvcd
, &pos
);
4828 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
4830 nmlvcd
.iSubItem
= 0;
4831 notify_customdraw(infoPtr
, CDDS_ITEMPOSTPAINT
, &nmlvcd
);
4839 * Draws listview items when in owner draw mode.
4842 * [I] infoPtr : valid pointer to the listview structure
4843 * [I] hdc : device context handle
4848 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4850 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
4851 DWORD cditemmode
= CDRF_DODEFAULT
;
4852 NMLVCUSTOMDRAW nmlvcd
;
4853 POINT Origin
, Position
;
4859 ZeroMemory(&dis
, sizeof(dis
));
4861 /* Get scroll info once before loop */
4862 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4864 /* iterate through the invalidated rows */
4865 while(iterator_next(i
))
4867 item
.iItem
= i
->nItem
;
4869 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4870 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4871 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
4873 dis
.CtlType
= ODT_LISTVIEW
;
4875 dis
.itemID
= item
.iItem
;
4876 dis
.itemAction
= ODA_DRAWENTIRE
;
4878 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
4879 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
4880 dis
.hwndItem
= infoPtr
->hwndSelf
;
4882 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
4883 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
4884 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
4885 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
4886 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
4887 dis
.itemData
= item
.lParam
;
4889 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), wine_dbgstr_rect(&dis
.rcItem
));
4892 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4893 * structure for the rest. of the paint cycle
4895 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
4896 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
4897 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4899 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
4901 prepaint_setup (infoPtr
, hdc
, &nmlvcd
, FALSE
);
4902 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
4905 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
4906 notify_postpaint(infoPtr
, &nmlvcd
);
4912 * Draws listview items when in report display mode.
4915 * [I] infoPtr : valid pointer to the listview structure
4916 * [I] hdc : device context handle
4917 * [I] cdmode : custom draw mode
4922 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4925 RECT rcClip
, rcItem
;
4933 /* figure out what to draw */
4934 rgntype
= GetClipBox(hdc
, &rcClip
);
4935 if (rgntype
== NULLREGION
) return;
4937 /* Get scroll info once before loop */
4938 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4940 colRanges
= ranges_create(DPA_GetPtrCount(infoPtr
->hdpaColumns
));
4942 /* narrow down the columns we need to paint */
4943 for(col
= 0; col
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); col
++)
4945 INT index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, col
, 0);
4947 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
4948 if ((rcItem
.right
+ Origin
.x
>= rcClip
.left
) && (rcItem
.left
+ Origin
.x
< rcClip
.right
))
4949 ranges_additem(colRanges
, index
);
4951 iterator_rangesitems(&j
, colRanges
);
4953 /* in full row select, we _have_ to draw the main item */
4954 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4957 /* iterate through the invalidated rows */
4958 while(iterator_next(i
))
4964 SelectObject(hdc
, infoPtr
->hFont
);
4965 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4966 Position
.x
= Origin
.x
;
4967 Position
.y
+= Origin
.y
;
4969 subitems
= ranges_create(DPA_GetPtrCount(infoPtr
->hdpaColumns
));
4971 /* iterate through the invalidated columns */
4972 while(iterator_next(&j
))
4974 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4976 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
4979 rcItem
.bottom
= infoPtr
->nItemHeight
;
4980 OffsetRect(&rcItem
, Origin
.x
, Position
.y
);
4981 if (!RectVisible(hdc
, &rcItem
)) continue;
4984 ranges_additem(subitems
, j
.nItem
);
4987 iterator_rangesitems(&k
, subitems
);
4988 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, &k
, Position
, cdmode
);
4989 iterator_destroy(&k
);
4991 iterator_destroy(&j
);
4996 * Draws the gridlines if necessary when in report display mode.
4999 * [I] infoPtr : valid pointer to the listview structure
5000 * [I] hdc : device context handle
5005 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
5011 RECT rcClip
, rcItem
= {0};
5019 /* figure out what to draw */
5020 rgntype
= GetClipBox(hdc
, &rcClip
);
5021 if (rgntype
== NULLREGION
) return;
5023 /* Get scroll info once before loop */
5024 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5026 colRanges
= ranges_create(DPA_GetPtrCount(infoPtr
->hdpaColumns
));
5028 /* narrow down the columns we need to paint */
5029 for(col
= 0; col
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); col
++)
5031 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, col
, 0);
5033 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
5034 if ((rcItem
.right
+ Origin
.x
>= rcClip
.left
) && (rcItem
.left
+ Origin
.x
< rcClip
.right
))
5035 ranges_additem(colRanges
, index
);
5038 /* is right most vertical line visible? */
5039 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
5041 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
5042 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
5043 rmost
= (rcItem
.right
+ Origin
.x
< rcClip
.right
);
5046 if ((hPen
= CreatePen( PS_SOLID
, 1, comctl32_color
.clr3dFace
)))
5048 hOldPen
= SelectObject ( hdc
, hPen
);
5050 /* draw the vertical lines for the columns */
5051 iterator_rangesitems(&j
, colRanges
);
5052 while(iterator_next(&j
))
5054 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
5055 if (rcItem
.left
== 0) continue; /* skip leftmost column */
5056 rcItem
.left
+= Origin
.x
;
5057 rcItem
.right
+= Origin
.x
;
5058 rcItem
.top
= infoPtr
->rcList
.top
;
5059 rcItem
.bottom
= infoPtr
->rcList
.bottom
;
5060 TRACE("vert col=%d, rcItem=%s\n", j
.nItem
, wine_dbgstr_rect(&rcItem
));
5061 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
5062 LineTo (hdc
, rcItem
.left
, rcItem
.bottom
);
5064 iterator_destroy(&j
);
5065 /* draw rightmost grid line if visible */
5068 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
5069 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
5070 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
5072 rcItem
.right
+= Origin
.x
;
5074 MoveToEx (hdc
, rcItem
.right
, infoPtr
->rcList
.top
, NULL
);
5075 LineTo (hdc
, rcItem
.right
, infoPtr
->rcList
.bottom
);
5078 /* draw the horizontal lines for the rows */
5079 itemheight
= LISTVIEW_CalculateItemHeight(infoPtr
);
5080 rcItem
.left
= infoPtr
->rcList
.left
;
5081 rcItem
.right
= infoPtr
->rcList
.right
;
5082 for(y
= Origin
.y
> 1 ? Origin
.y
- 1 : itemheight
- 1 + Origin
.y
% itemheight
; y
<=infoPtr
->rcList
.bottom
; y
+=itemheight
)
5084 rcItem
.bottom
= rcItem
.top
= y
;
5085 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem
));
5086 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
5087 LineTo (hdc
, rcItem
.right
, rcItem
.top
);
5090 SelectObject( hdc
, hOldPen
);
5091 DeleteObject( hPen
);
5094 ranges_destroy(colRanges
);
5099 * Draws listview items when in list display mode.
5102 * [I] infoPtr : valid pointer to the listview structure
5103 * [I] hdc : device context handle
5104 * [I] cdmode : custom draw mode
5109 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
5111 POINT Origin
, Position
;
5113 /* Get scroll info once before loop */
5114 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5116 while(iterator_prev(i
))
5118 SelectObject(hdc
, infoPtr
->hFont
);
5119 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
5120 Position
.x
+= Origin
.x
;
5121 Position
.y
+= Origin
.y
;
5123 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, NULL
, Position
, cdmode
);
5130 * Draws listview items.
5133 * [I] infoPtr : valid pointer to the listview structure
5134 * [I] hdc : device context handle
5135 * [I] prcErase : rect to be erased before refresh (may be NULL)
5140 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*prcErase
)
5142 COLORREF oldTextColor
= 0, oldBkColor
= 0;
5143 NMLVCUSTOMDRAW nmlvcd
;
5150 HBITMAP hbmp
= NULL
;
5153 LISTVIEW_DUMP(infoPtr
);
5155 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
5156 TRACE("double buffering\n");
5158 hdc
= CreateCompatibleDC(hdcOrig
);
5160 ERR("Failed to create DC for backbuffer\n");
5163 hbmp
= CreateCompatibleBitmap(hdcOrig
, infoPtr
->rcList
.right
,
5164 infoPtr
->rcList
.bottom
);
5166 ERR("Failed to create bitmap for backbuffer\n");
5171 SelectObject(hdc
, hbmp
);
5172 SelectObject(hdc
, infoPtr
->hFont
);
5174 if(GetClipBox(hdcOrig
, &rcClient
))
5175 IntersectClipRect(hdc
, rcClient
.left
, rcClient
.top
, rcClient
.right
, rcClient
.bottom
);
5177 /* Save dc values we're gonna trash while drawing
5178 * FIXME: Should be done in LISTVIEW_DrawItem() */
5179 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
5180 oldBkMode
= GetBkMode(hdc
);
5181 oldBkColor
= GetBkColor(hdc
);
5182 oldTextColor
= GetTextColor(hdc
);
5185 infoPtr
->bIsDrawing
= TRUE
;
5188 LISTVIEW_FillBkgnd(infoPtr
, hdc
, prcErase
);
5189 } else if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
5190 /* If no erasing was done (usually because RedrawWindow was called
5191 * with RDW_INVALIDATE only) we need to copy the old contents into
5192 * the backbuffer before continuing. */
5193 BitBlt(hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
5194 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
5195 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
5196 hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
5199 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
5200 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
5201 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
5202 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
5204 /* nothing to draw */
5205 if(infoPtr
->nItemCount
== 0) goto enddraw
;
5207 /* figure out what we need to draw */
5208 iterator_visibleitems(&i
, infoPtr
, hdc
);
5209 range
= iterator_range(&i
);
5211 /* send cache hint notification */
5212 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5216 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
5217 nmlv
.iFrom
= range
.lower
;
5218 nmlv
.iTo
= range
.upper
- 1;
5219 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
5222 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
5223 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
5226 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
5227 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
5228 else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */
5229 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
5231 /* if we have a focus rect and it's visible, draw it */
5232 if (infoPtr
->bFocus
&& range
.lower
<= infoPtr
->nFocusedItem
&&
5233 (range
.upper
- 1) >= infoPtr
->nFocusedItem
)
5234 LISTVIEW_DrawFocusRect(infoPtr
, hdc
);
5236 iterator_destroy(&i
);
5239 /* For LVS_EX_GRIDLINES go and draw lines */
5240 /* This includes the case where there were *no* items */
5241 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
5242 LISTVIEW_RefreshReportGrid(infoPtr
, hdc
);
5244 /* Draw marquee rectangle if appropriate */
5245 if (infoPtr
->bMarqueeSelect
)
5246 DrawFocusRect(hdc
, &infoPtr
->marqueeDrawRect
);
5248 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
5249 notify_postpaint(infoPtr
, &nmlvcd
);
5252 BitBlt(hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
5253 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
5254 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
5255 hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
5260 SelectObject(hdc
, hOldFont
);
5261 SetBkMode(hdc
, oldBkMode
);
5262 SetBkColor(hdc
, oldBkColor
);
5263 SetTextColor(hdc
, oldTextColor
);
5266 infoPtr
->bIsDrawing
= FALSE
;
5272 * Calculates the approximate width and height of a given number of items.
5275 * [I] infoPtr : valid pointer to the listview structure
5276 * [I] nItemCount : number of items
5277 * [I] wWidth : width
5278 * [I] wHeight : height
5281 * Returns a DWORD. The width in the low word and the height in high word.
5283 static DWORD
LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
5284 WORD wWidth
, WORD wHeight
)
5286 DWORD dwViewRect
= 0;
5288 if (nItemCount
== -1)
5289 nItemCount
= infoPtr
->nItemCount
;
5291 if (infoPtr
->uView
== LV_VIEW_LIST
)
5293 INT nItemCountPerColumn
= 1;
5294 INT nColumnCount
= 0;
5296 if (wHeight
== 0xFFFF)
5298 /* use current height */
5299 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
5302 if (wHeight
< infoPtr
->nItemHeight
)
5303 wHeight
= infoPtr
->nItemHeight
;
5307 if (infoPtr
->nItemHeight
> 0)
5309 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
5310 if (nItemCountPerColumn
== 0)
5311 nItemCountPerColumn
= 1;
5313 if (nItemCount
% nItemCountPerColumn
!= 0)
5314 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
5316 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
5320 /* Microsoft padding magic */
5321 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
5322 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
5324 dwViewRect
= MAKELONG(wWidth
, wHeight
);
5326 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
5330 if (infoPtr
->nItemCount
> 0)
5332 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
5333 wWidth
= rcBox
.right
- rcBox
.left
;
5334 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
5338 /* use current height and width */
5339 if (wHeight
== 0xffff)
5340 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
5341 if (wWidth
== 0xffff)
5342 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
5345 dwViewRect
= MAKELONG(wWidth
, wHeight
);
5347 else if (infoPtr
->uView
== LV_VIEW_ICON
)
5353 nItemWidth
= infoPtr
->iconSpacing
.cx
;
5354 nItemHeight
= infoPtr
->iconSpacing
.cy
;
5356 if (wWidth
== 0xffff)
5357 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
5359 if (wWidth
< nItemWidth
)
5360 wWidth
= nItemWidth
;
5362 cols
= wWidth
/ nItemWidth
;
5363 if (cols
> nItemCount
)
5370 rows
= nItemCount
/ cols
;
5371 if (nItemCount
% cols
)
5377 wHeight
= (nItemHeight
* rows
)+2;
5378 wWidth
= (nItemWidth
* cols
)+2;
5380 dwViewRect
= MAKELONG(wWidth
, wHeight
);
5382 else if (infoPtr
->uView
== LV_VIEW_SMALLICON
)
5383 FIXME("uView == LV_VIEW_SMALLICON: not implemented\n");
5390 * Cancel edit label with saving item text.
5393 * [I] infoPtr : valid pointer to the listview structure
5396 * Always returns TRUE.
5398 static LRESULT
LISTVIEW_CancelEditLabel(LISTVIEW_INFO
*infoPtr
)
5400 if (infoPtr
->hwndEdit
)
5402 /* handle value will be lost after LISTVIEW_EndEditLabelT */
5403 HWND edit
= infoPtr
->hwndEdit
;
5405 LISTVIEW_EndEditLabelT(infoPtr
, TRUE
, IsWindowUnicode(infoPtr
->hwndEdit
));
5406 SendMessageW(edit
, WM_CLOSE
, 0, 0);
5414 * Create a drag image list for the specified item.
5417 * [I] infoPtr : valid pointer to the listview structure
5418 * [I] iItem : index of item
5419 * [O] lppt : Upper-left corner of the image
5422 * Returns a handle to the image list if successful, NULL otherwise.
5424 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
5430 HBITMAP hbmp
, hOldbmp
;
5432 HIMAGELIST dragList
= 0;
5433 TRACE("iItem=%d Count=%d\n", iItem
, infoPtr
->nItemCount
);
5435 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
|| !lppt
)
5438 rcItem
.left
= LVIR_BOUNDS
;
5439 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
5442 lppt
->x
= rcItem
.left
;
5443 lppt
->y
= rcItem
.top
;
5445 size
.cx
= rcItem
.right
- rcItem
.left
;
5446 size
.cy
= rcItem
.bottom
- rcItem
.top
;
5448 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
5449 hdc
= CreateCompatibleDC(hdcOrig
);
5450 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
5451 hOldbmp
= SelectObject(hdc
, hbmp
);
5452 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
5454 SetRect(&rcItem
, 0, 0, size
.cx
, size
.cy
);
5455 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
5458 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, NULL
, pos
, CDRF_DODEFAULT
))
5460 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
5461 SelectObject(hdc
, hOldbmp
);
5462 ImageList_Add(dragList
, hbmp
, 0);
5465 SelectObject(hdc
, hOldbmp
);
5467 SelectObject(hdc
, hOldFont
);
5470 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
5472 TRACE("ret=%p\n", dragList
);
5480 * Removes all listview items and subitems.
5483 * [I] infoPtr : valid pointer to the listview structure
5489 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
, BOOL destroy
)
5491 HDPA hdpaSubItems
= NULL
;
5492 BOOL suppress
= FALSE
;
5500 /* we do it directly, to avoid notifications */
5501 ranges_clear(infoPtr
->selectionRanges
);
5502 infoPtr
->nSelectionMark
= -1;
5503 infoPtr
->nFocusedItem
= -1;
5504 SetRectEmpty(&infoPtr
->rcFocus
);
5505 /* But we are supposed to leave nHotItem as is! */
5507 /* send LVN_DELETEALLITEMS notification */
5508 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) || !destroy
)
5512 memset(&nmlv
, 0, sizeof(NMLISTVIEW
));
5514 suppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
5517 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
5519 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
5521 /* send LVN_DELETEITEM notification, if not suppressed
5522 and if it is not a virtual listview */
5523 if (!suppress
) notify_deleteitem(infoPtr
, i
);
5524 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
5525 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5526 /* free id struct */
5527 j
= DPA_GetPtrIndex(infoPtr
->hdpaItemIds
, lpItem
->id
);
5528 lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, j
);
5529 DPA_DeletePtr(infoPtr
->hdpaItemIds
, j
);
5531 /* both item and subitem start with ITEMHDR header */
5532 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
5534 hdrItem
= DPA_GetPtr(hdpaSubItems
, j
);
5535 if (is_text(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
5538 DPA_Destroy(hdpaSubItems
);
5539 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
5541 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
5542 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
5543 infoPtr
->nItemCount
--;
5548 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
5549 LISTVIEW_UpdateScroll(infoPtr
);
5551 LISTVIEW_InvalidateList(infoPtr
);
5558 * Scrolls, and updates the columns, when a column is changing width.
5561 * [I] infoPtr : valid pointer to the listview structure
5562 * [I] nColumn : column to scroll
5563 * [I] dx : amount of scroll, in pixels
5568 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
5570 COLUMN_INFO
*lpColumnInfo
;
5576 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
5577 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
5578 rcCol
= lpColumnInfo
->rcHeader
;
5579 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
5580 rcCol
.left
= rcCol
.right
;
5582 /* adjust the other columns */
5583 hdi
.mask
= HDI_ORDER
;
5584 if (SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdi
))
5586 INT nOrder
= hdi
.iOrder
;
5587 for (nCol
= 0; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
5589 hdi
.mask
= HDI_ORDER
;
5590 SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nCol
, (LPARAM
)&hdi
);
5591 if (hdi
.iOrder
>= nOrder
) {
5592 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
5593 lpColumnInfo
->rcHeader
.left
+= dx
;
5594 lpColumnInfo
->rcHeader
.right
+= dx
;
5599 /* do not update screen if not in report mode */
5600 if (!is_redrawing(infoPtr
) || infoPtr
->uView
!= LV_VIEW_DETAILS
) return;
5602 /* Need to reset the item width when inserting a new column */
5603 infoPtr
->nItemWidth
+= dx
;
5605 LISTVIEW_UpdateScroll(infoPtr
);
5606 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
5608 /* scroll to cover the deleted column, and invalidate for redraw */
5609 rcOld
= infoPtr
->rcList
;
5610 rcOld
.left
= ptOrigin
.x
+ rcCol
.left
+ dx
;
5611 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
5616 * Removes a column from the listview control.
5619 * [I] infoPtr : valid pointer to the listview structure
5620 * [I] nColumn : column index
5626 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
5630 TRACE("nColumn=%d\n", nColumn
);
5632 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
5635 /* While the MSDN specifically says that column zero should not be deleted,
5636 what actually happens is that the column itself is deleted but no items or subitems
5640 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
5642 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nColumn
, 0))
5645 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
5646 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
5648 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
5650 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
5652 INT nItem
, nSubItem
, i
;
5654 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
5656 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
5659 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
5661 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
5662 if (lpSubItem
->iSubItem
== nColumn
)
5665 lpDelItem
= lpSubItem
;
5667 else if (lpSubItem
->iSubItem
> nColumn
)
5669 lpSubItem
->iSubItem
--;
5673 /* if we found our subitem, zap it */
5677 if (is_text(lpDelItem
->hdr
.pszText
))
5678 Free(lpDelItem
->hdr
.pszText
);
5683 /* free dpa memory */
5684 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
5689 /* update the other column info */
5690 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
5691 LISTVIEW_InvalidateList(infoPtr
);
5693 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
5694 LISTVIEW_UpdateItemSize(infoPtr
);
5701 * Invalidates the listview after an item's insertion or deletion.
5704 * [I] infoPtr : valid pointer to the listview structure
5705 * [I] nItem : item index
5706 * [I] dir : -1 if deleting, 1 if inserting
5711 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
5713 INT nPerCol
, nItemCol
, nItemRow
;
5717 /* if we don't refresh, what's the point of scrolling? */
5718 if (!is_redrawing(infoPtr
)) return;
5720 assert (abs(dir
) == 1);
5722 /* arrange icons if autoarrange is on */
5723 if (is_autoarrange(infoPtr
))
5725 BOOL arrange
= TRUE
;
5726 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
5727 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
5728 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
5731 /* scrollbars need updating */
5732 LISTVIEW_UpdateScroll(infoPtr
);
5734 /* figure out the item's position */
5735 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
5736 nPerCol
= infoPtr
->nItemCount
+ 1;
5737 else if (infoPtr
->uView
== LV_VIEW_LIST
)
5738 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
5739 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
5742 nItemCol
= nItem
/ nPerCol
;
5743 nItemRow
= nItem
% nPerCol
;
5744 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5746 /* move the items below up a slot */
5747 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
5748 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
5749 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
5750 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
5751 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
5752 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
5753 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
5755 TRACE("Invalidating rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll
), wine_dbgstr_rect(&infoPtr
->rcList
));
5756 InvalidateRect(infoPtr
->hwndSelf
, &rcScroll
, TRUE
);
5759 /* report has only that column, so we're done */
5760 if (infoPtr
->uView
== LV_VIEW_DETAILS
) return;
5762 /* now for LISTs, we have to deal with the columns to the right */
5763 SetRect(&rcScroll
, (nItemCol
+ 1) * infoPtr
->nItemWidth
, 0,
5764 (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
,
5765 nPerCol
* infoPtr
->nItemHeight
);
5766 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
5767 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
5768 InvalidateRect(infoPtr
->hwndSelf
, &rcScroll
, TRUE
);
5773 * Removes an item from the listview control.
5776 * [I] infoPtr : valid pointer to the listview structure
5777 * [I] nItem : item index
5783 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
5786 const BOOL is_icon
= (infoPtr
->uView
== LV_VIEW_SMALLICON
|| infoPtr
->uView
== LV_VIEW_ICON
);
5787 INT focus
= infoPtr
->nFocusedItem
;
5789 TRACE("(nItem=%d)\n", nItem
);
5791 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5793 /* remove selection, and focus */
5795 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
5796 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
5798 /* send LVN_DELETEITEM notification. */
5799 if (!notify_deleteitem(infoPtr
, nItem
)) return FALSE
;
5801 /* we need to do this here, because we'll be deleting stuff */
5803 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
5805 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
5813 hdpaSubItems
= DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
5814 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5816 /* free id struct */
5817 i
= DPA_GetPtrIndex(infoPtr
->hdpaItemIds
, lpItem
->id
);
5818 lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, i
);
5819 DPA_DeletePtr(infoPtr
->hdpaItemIds
, i
);
5821 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
5823 hdrItem
= DPA_GetPtr(hdpaSubItems
, i
);
5824 if (is_text(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
5827 DPA_Destroy(hdpaSubItems
);
5832 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
5833 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
5836 infoPtr
->nItemCount
--;
5837 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
5838 LISTVIEW_ShiftFocus(infoPtr
, focus
, nItem
, -1);
5840 /* now is the invalidation fun */
5842 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
5849 * Callback implementation for editlabel control
5852 * [I] infoPtr : valid pointer to the listview structure
5853 * [I] storeText : store edit box text as item text
5854 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
5860 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, BOOL storeText
, BOOL isW
)
5862 HWND hwndSelf
= infoPtr
->hwndSelf
;
5863 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
5864 NMLVDISPINFOW dispInfo
;
5865 INT editedItem
= infoPtr
->nEditLabelItem
;
5867 WCHAR
*pszText
= NULL
;
5872 DWORD len
= isW
? GetWindowTextLengthW(infoPtr
->hwndEdit
) : GetWindowTextLengthA(infoPtr
->hwndEdit
);
5876 if (!(pszText
= Alloc(len
* (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))))
5880 GetWindowTextW(infoPtr
->hwndEdit
, pszText
, len
);
5882 GetWindowTextA(infoPtr
->hwndEdit
, (CHAR
*)pszText
, len
);
5886 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
5888 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5889 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
5890 dispInfo
.item
.iItem
= editedItem
;
5891 dispInfo
.item
.iSubItem
= 0;
5892 dispInfo
.item
.stateMask
= ~0;
5893 dispInfo
.item
.pszText
= szDispText
;
5894 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
5895 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
))
5902 same
= (lstrcmpW(dispInfo
.item
.pszText
, pszText
) == 0);
5905 LPWSTR tmp
= textdupTtoW(pszText
, FALSE
);
5906 same
= (lstrcmpW(dispInfo
.item
.pszText
, tmp
) == 0);
5907 textfreeT(tmp
, FALSE
);
5910 /* add the text from the edit in */
5911 dispInfo
.item
.mask
|= LVIF_TEXT
;
5912 dispInfo
.item
.pszText
= same
? NULL
: pszText
;
5913 dispInfo
.item
.cchTextMax
= textlenT(dispInfo
.item
.pszText
, isW
);
5915 /* Do we need to update the Item Text */
5916 res
= notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
);
5918 infoPtr
->nEditLabelItem
= -1;
5919 infoPtr
->hwndEdit
= 0;
5921 if (!res
) goto cleanup
;
5923 if (!IsWindow(hwndSelf
))
5928 if (!pszText
) return TRUE
;
5935 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
5937 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, editedItem
);
5938 ITEM_INFO
* lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5939 if (lpItem
&& lpItem
->hdr
.pszText
== LPSTR_TEXTCALLBACKW
)
5941 LISTVIEW_InvalidateItem(infoPtr
, editedItem
);
5947 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5948 dispInfo
.item
.mask
= LVIF_TEXT
;
5949 dispInfo
.item
.iItem
= editedItem
;
5950 dispInfo
.item
.iSubItem
= 0;
5951 dispInfo
.item
.pszText
= pszText
;
5952 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
5953 res
= LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
5963 * Subclassed edit control windproc function
5966 * [I] hwnd : the edit window handle
5967 * [I] uMsg : the message that is to be processed
5968 * [I] wParam : first message parameter
5969 * [I] lParam : second message parameter
5970 * [I] isW : TRUE if input is Unicode
5975 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
5977 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
5980 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
5981 hwnd
, uMsg
, wParam
, lParam
, isW
);
5986 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
5990 WNDPROC editProc
= infoPtr
->EditWndProc
;
5991 infoPtr
->EditWndProc
= 0;
5992 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
5993 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
5997 if (VK_ESCAPE
== (INT
)wParam
)
6002 else if (VK_RETURN
== (INT
)wParam
)
6006 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
6010 if (infoPtr
->hwndEdit
)
6011 LISTVIEW_EndEditLabelT(infoPtr
, save
, isW
);
6013 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
6019 * Subclassed edit control Unicode windproc function
6022 * [I] hwnd : the edit window handle
6023 * [I] uMsg : the message that is to be processed
6024 * [I] wParam : first message parameter
6025 * [I] lParam : second message parameter
6029 static LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
6031 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
6036 * Subclassed edit control ANSI windproc function
6039 * [I] hwnd : the edit window handle
6040 * [I] uMsg : the message that is to be processed
6041 * [I] wParam : first message parameter
6042 * [I] lParam : second message parameter
6046 static LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
6048 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
6053 * Creates a subclassed edit control
6056 * [I] infoPtr : valid pointer to the listview structure
6057 * [I] text : initial text for the edit
6058 * [I] style : the window style
6059 * [I] isW : TRUE if input is Unicode
6063 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, BOOL isW
)
6065 static const DWORD style
= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
|WS_VISIBLE
;
6066 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
6069 TRACE("(%p, text=%s, isW=%d)\n", infoPtr
, debugtext_t(text
, isW
), isW
);
6071 /* window will be resized and positioned after LVN_BEGINLABELEDIT */
6073 hedit
= CreateWindowW(WC_EDITW
, text
, style
, 0, 0, 0, 0, infoPtr
->hwndSelf
, 0, hinst
, 0);
6075 hedit
= CreateWindowA(WC_EDITA
, (LPCSTR
)text
, style
, 0, 0, 0, 0, infoPtr
->hwndSelf
, 0, hinst
, 0);
6077 if (!hedit
) return 0;
6079 infoPtr
->EditWndProc
= (WNDPROC
)
6080 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
6081 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
6083 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);
6084 SendMessageW(hedit
, EM_SETLIMITTEXT
, DISP_TEXT_SIZE
-1, 0);
6091 * Begin in place editing of specified list view item
6094 * [I] infoPtr : valid pointer to the listview structure
6095 * [I] nItem : item index
6096 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
6102 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
6104 WCHAR disptextW
[DISP_TEXT_SIZE
] = { 0 };
6105 HWND hwndSelf
= infoPtr
->hwndSelf
;
6106 NMLVDISPINFOW dispInfo
;
6107 HFONT hOldFont
= NULL
;
6113 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
6115 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
6117 /* remove existing edit box */
6118 if (infoPtr
->hwndEdit
)
6120 SetFocus(infoPtr
->hwndSelf
);
6121 infoPtr
->hwndEdit
= 0;
6124 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6126 infoPtr
->nEditLabelItem
= nItem
;
6128 LISTVIEW_SetSelection(infoPtr
, nItem
);
6129 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
6130 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
6132 rect
.left
= LVIR_LABEL
;
6133 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
6135 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
6136 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
6137 dispInfo
.item
.iItem
= nItem
;
6138 dispInfo
.item
.iSubItem
= 0;
6139 dispInfo
.item
.stateMask
= ~0;
6140 dispInfo
.item
.pszText
= disptextW
;
6141 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
6142 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
6144 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, isW
);
6145 if (!infoPtr
->hwndEdit
) return 0;
6147 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
6149 if (!IsWindow(hwndSelf
))
6151 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
6152 infoPtr
->hwndEdit
= 0;
6156 TRACE("disp text=%s\n", debugtext_t(dispInfo
.item
.pszText
, isW
));
6158 /* position and display edit box */
6159 hdc
= GetDC(infoPtr
->hwndSelf
);
6161 /* select the font to get appropriate metric dimensions */
6163 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
6165 /* use real edit box content, it could be altered during LVN_BEGINLABELEDIT notification */
6166 GetWindowTextW(infoPtr
->hwndEdit
, disptextW
, DISP_TEXT_SIZE
);
6167 TRACE("edit box text=%s\n", debugstr_w(disptextW
));
6169 /* get string length in pixels */
6170 GetTextExtentPoint32W(hdc
, disptextW
, lstrlenW(disptextW
), &sz
);
6172 /* add extra spacing for the next character */
6173 GetTextMetricsW(hdc
, &tm
);
6174 sz
.cx
+= tm
.tmMaxCharWidth
* 2;
6177 SelectObject(hdc
, hOldFont
);
6179 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6181 sz
.cy
= rect
.bottom
- rect
.top
+ 2;
6184 TRACE("moving edit=(%d,%d)-(%d,%d)\n", rect
.left
, rect
.top
, sz
.cx
, sz
.cy
);
6185 MoveWindow(infoPtr
->hwndEdit
, rect
.left
, rect
.top
, sz
.cx
, sz
.cy
, FALSE
);
6186 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
6187 SetFocus(infoPtr
->hwndEdit
);
6188 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
6189 return infoPtr
->hwndEdit
;
6195 * Ensures the specified item is visible, scrolling into view if necessary.
6198 * [I] infoPtr : valid pointer to the listview structure
6199 * [I] nItem : item index
6200 * [I] bPartial : partially or entirely visible
6206 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
6208 INT nScrollPosHeight
= 0;
6209 INT nScrollPosWidth
= 0;
6210 INT nHorzAdjust
= 0;
6211 INT nVertAdjust
= 0;
6214 RECT rcItem
, rcTemp
;
6216 rcItem
.left
= LVIR_BOUNDS
;
6217 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
6219 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
6221 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
6223 /* scroll left/right, but in LV_VIEW_DETAILS mode */
6224 if (infoPtr
->uView
== LV_VIEW_LIST
)
6225 nScrollPosWidth
= infoPtr
->nItemWidth
;
6226 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
6227 nScrollPosWidth
= 1;
6229 if (rcItem
.left
< infoPtr
->rcList
.left
)
6232 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
6237 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
6241 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
6243 /* scroll up/down, but not in LVS_LIST mode */
6244 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
6245 nScrollPosHeight
= infoPtr
->nItemHeight
;
6246 else if ((infoPtr
->uView
== LV_VIEW_ICON
) || (infoPtr
->uView
== LV_VIEW_SMALLICON
))
6247 nScrollPosHeight
= 1;
6249 if (rcItem
.top
< infoPtr
->rcList
.top
)
6252 if (infoPtr
->uView
!= LV_VIEW_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
6257 if (infoPtr
->uView
!= LV_VIEW_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
6261 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
6263 if (nScrollPosWidth
)
6265 INT diff
= nHorzDiff
/ nScrollPosWidth
;
6266 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
6267 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
);
6270 if (nScrollPosHeight
)
6272 INT diff
= nVertDiff
/ nScrollPosHeight
;
6273 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
6274 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
);
6282 * Searches for an item with specific characteristics.
6285 * [I] hwnd : window handle
6286 * [I] nStart : base item index
6287 * [I] lpFindInfo : item information to look for
6290 * SUCCESS : index of item
6293 static INT
LISTVIEW_FindItemW(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
6294 const LVFINDINFOW
*lpFindInfo
)
6296 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6297 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
6298 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
6299 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
6300 POINT Position
, Destination
;
6303 /* Search in virtual listviews should be done by application, not by
6304 listview control, so we just send LVN_ODFINDITEMW and return the result */
6305 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
6309 nmlv
.iStart
= nStart
;
6310 nmlv
.lvfi
= *lpFindInfo
;
6311 return notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
6314 if (!lpFindInfo
|| nItem
< 0) return -1;
6317 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
| LVFI_SUBSTRING
))
6319 lvItem
.mask
|= LVIF_TEXT
;
6320 lvItem
.pszText
= szDispText
;
6321 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6324 if (lpFindInfo
->flags
& LVFI_WRAP
)
6327 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
6328 (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
))
6333 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6334 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
6335 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
6336 switch(lpFindInfo
->vkDirection
)
6338 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
6339 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
6340 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
6341 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
6342 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
6343 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
6344 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
6346 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
6347 Destination
.x
= rcArea
.right
;
6348 Destination
.y
= rcArea
.bottom
;
6350 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
6354 else Destination
.x
= Destination
.y
= 0;
6356 /* if LVFI_PARAM is specified, all other flags are ignored */
6357 if (lpFindInfo
->flags
& LVFI_PARAM
)
6359 lvItem
.mask
|= LVIF_PARAM
;
6361 lvItem
.mask
&= ~LVIF_TEXT
;
6364 nItem
= bNearest
? -1 : nStart
+ 1;
6367 for (; nItem
< nLast
; nItem
++)
6369 lvItem
.iItem
= nItem
;
6370 lvItem
.iSubItem
= 0;
6371 lvItem
.pszText
= szDispText
;
6372 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
6374 if (lvItem
.mask
& LVIF_PARAM
)
6376 if (lpFindInfo
->lParam
== lvItem
.lParam
)
6382 if (lvItem
.mask
& LVIF_TEXT
)
6384 if (lpFindInfo
->flags
& (LVFI_PARTIAL
| LVFI_SUBSTRING
))
6386 WCHAR
*p
= strstrW(lvItem
.pszText
, lpFindInfo
->psz
);
6387 if (!p
|| p
!= lvItem
.pszText
) continue;
6391 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
6395 if (!bNearest
) return nItem
;
6397 /* This is very inefficient. To do a good job here,
6398 * we need a sorted array of (x,y) item positions */
6399 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
6401 /* compute the distance^2 to the destination */
6402 xdist
= Destination
.x
- Position
.x
;
6403 ydist
= Destination
.y
- Position
.y
;
6404 dist
= xdist
* xdist
+ ydist
* ydist
;
6406 /* remember the distance, and item if it's closer */
6410 nNearestItem
= nItem
;
6417 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
6422 return nNearestItem
;
6427 * Searches for an item with specific characteristics.
6430 * [I] hwnd : window handle
6431 * [I] nStart : base item index
6432 * [I] lpFindInfo : item information to look for
6435 * SUCCESS : index of item
6438 static INT
LISTVIEW_FindItemA(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
6439 const LVFINDINFOA
*lpFindInfo
)
6445 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
6446 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
| LVFI_SUBSTRING
))
6447 fiw
.psz
= strW
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
6448 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
6449 textfreeT(strW
, FALSE
);
6455 * Retrieves column attributes.
6458 * [I] infoPtr : valid pointer to the listview structure
6459 * [I] nColumn : column index
6460 * [IO] lpColumn : column information
6461 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
6462 * otherwise it is in fact a LPLVCOLUMNA
6468 static BOOL
LISTVIEW_GetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
6470 COLUMN_INFO
*lpColumnInfo
;
6473 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6474 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
6476 /* initialize memory */
6477 ZeroMemory(&hdi
, sizeof(hdi
));
6479 if (lpColumn
->mask
& LVCF_TEXT
)
6481 hdi
.mask
|= HDI_TEXT
;
6482 hdi
.pszText
= lpColumn
->pszText
;
6483 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
6486 if (lpColumn
->mask
& LVCF_IMAGE
)
6487 hdi
.mask
|= HDI_IMAGE
;
6489 if (lpColumn
->mask
& LVCF_ORDER
)
6490 hdi
.mask
|= HDI_ORDER
;
6492 if (lpColumn
->mask
& LVCF_SUBITEM
)
6493 hdi
.mask
|= HDI_LPARAM
;
6495 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
6497 if (lpColumn
->mask
& LVCF_FMT
)
6498 lpColumn
->fmt
= lpColumnInfo
->fmt
;
6500 if (lpColumn
->mask
& LVCF_WIDTH
)
6501 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
6503 if (lpColumn
->mask
& LVCF_IMAGE
)
6504 lpColumn
->iImage
= hdi
.iImage
;
6506 if (lpColumn
->mask
& LVCF_ORDER
)
6507 lpColumn
->iOrder
= hdi
.iOrder
;
6509 if (lpColumn
->mask
& LVCF_SUBITEM
)
6510 lpColumn
->iSubItem
= hdi
.lParam
;
6512 if (lpColumn
->mask
& LVCF_MINWIDTH
)
6513 lpColumn
->cxMin
= lpColumnInfo
->cxMin
;
6518 static inline BOOL
LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
6520 if (!infoPtr
->hwndHeader
) return FALSE
;
6521 return SendMessageW(infoPtr
->hwndHeader
, HDM_GETORDERARRAY
, iCount
, (LPARAM
)lpiArray
);
6526 * Retrieves the column width.
6529 * [I] infoPtr : valid pointer to the listview structure
6530 * [I] int : column index
6533 * SUCCESS : column width
6536 static INT
LISTVIEW_GetColumnWidth(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
6538 INT nColumnWidth
= 0;
6541 TRACE("nColumn=%d\n", nColumn
);
6543 /* we have a 'column' in LIST and REPORT mode only */
6544 switch(infoPtr
->uView
)
6547 nColumnWidth
= infoPtr
->nItemWidth
;
6549 case LV_VIEW_DETAILS
:
6550 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDN_ITEMCHANGED.
6551 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
6552 * HDN_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
6554 hdItem
.mask
= HDI_WIDTH
;
6555 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdItem
))
6557 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr
->hwndSelf
, nColumn
);
6560 nColumnWidth
= hdItem
.cxy
;
6564 TRACE("nColumnWidth=%d\n", nColumnWidth
);
6565 return nColumnWidth
;
6570 * In list or report display mode, retrieves the number of items that can fit
6571 * vertically in the visible area. In icon or small icon display mode,
6572 * retrieves the total number of visible items.
6575 * [I] infoPtr : valid pointer to the listview structure
6578 * Number of fully visible items.
6580 static INT
LISTVIEW_GetCountPerPage(const LISTVIEW_INFO
*infoPtr
)
6582 switch (infoPtr
->uView
)
6585 case LV_VIEW_SMALLICON
:
6586 return infoPtr
->nItemCount
;
6587 case LV_VIEW_DETAILS
:
6588 return LISTVIEW_GetCountPerColumn(infoPtr
);
6590 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
6598 * Retrieves an image list handle.
6601 * [I] infoPtr : valid pointer to the listview structure
6602 * [I] nImageList : image list identifier
6605 * SUCCESS : image list handle
6608 static HIMAGELIST
LISTVIEW_GetImageList(const LISTVIEW_INFO
*infoPtr
, INT nImageList
)
6612 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
6613 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
6614 case LVSIL_STATE
: return infoPtr
->himlState
;
6615 case LVSIL_GROUPHEADER
:
6616 FIXME("LVSIL_GROUPHEADER not supported\n");
6619 WARN("got unknown imagelist index - %d\n", nImageList
);
6624 /* LISTVIEW_GetISearchString */
6628 * Retrieves item attributes.
6631 * [I] hwnd : window handle
6632 * [IO] lpLVItem : item info
6633 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6634 * if FALSE, then lpLVItem is a LPLVITEMA.
6637 * This is the internal 'GetItem' interface -- it tries to
6638 * be smart and avoid text copies, if possible, by modifying
6639 * lpLVItem->pszText to point to the text string. Please note
6640 * that this is not always possible (e.g. OWNERDATA), so on
6641 * entry you *must* supply valid values for pszText, and cchTextMax.
6642 * The only difference to the documented interface is that upon
6643 * return, you should use *only* the lpLVItem->pszText, rather than
6644 * the buffer pointer you provided on input. Most code already does
6645 * that, so it's not a problem.
6646 * For the two cases when the text must be copied (that is,
6647 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
6653 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
6655 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
6656 NMLVDISPINFOW dispInfo
;
6662 TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6664 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
6667 if (lpLVItem
->mask
== 0) return TRUE
;
6668 TRACE("mask=%x\n", lpLVItem
->mask
);
6670 /* make a local copy */
6671 isubitem
= lpLVItem
->iSubItem
;
6673 if (isubitem
&& (lpLVItem
->mask
& LVIF_STATE
))
6674 lpLVItem
->state
= 0;
6676 /* a quick optimization if all we're asked is the focus state
6677 * these queries are worth optimising since they are common,
6678 * and can be answered in constant time, without the heavy accesses */
6679 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
6680 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
6682 lpLVItem
->state
= 0;
6683 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
&& isubitem
== 0)
6684 lpLVItem
->state
|= LVIS_FOCUSED
;
6688 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
6690 /* if the app stores all the data, handle it separately */
6691 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
6693 dispInfo
.item
.state
= 0;
6695 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
6696 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) ||
6697 ((lpLVItem
->mask
& LVIF_STATE
) && (infoPtr
->uCallbackMask
& lpLVItem
->stateMask
)))
6699 UINT mask
= lpLVItem
->mask
;
6701 /* NOTE: copy only fields which we _know_ are initialized, some apps
6702 * depend on the uninitialized fields being 0 */
6703 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
6704 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
6705 dispInfo
.item
.iSubItem
= isubitem
;
6706 if (lpLVItem
->mask
& LVIF_TEXT
)
6708 if (lpLVItem
->mask
& LVIF_NORECOMPUTE
)
6710 dispInfo
.item
.mask
&= ~(LVIF_TEXT
| LVIF_NORECOMPUTE
);
6713 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
6714 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
6717 if (lpLVItem
->mask
& LVIF_STATE
)
6718 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
6719 /* could be zeroed on LVIF_NORECOMPUTE case */
6720 if (dispInfo
.item
.mask
)
6722 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
6723 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
6724 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6726 /* full size structure expected - _WIN32IE >= 0x560 */
6727 *lpLVItem
= dispInfo
.item
;
6729 else if (lpLVItem
->mask
& LVIF_INDENT
)
6731 /* indent member expected - _WIN32IE >= 0x300 */
6732 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
6736 /* minimal structure expected */
6737 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
6739 lpLVItem
->mask
= mask
;
6740 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
6744 /* make sure lParam is zeroed out */
6745 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
6747 /* callback marked pointer required here */
6748 if ((lpLVItem
->mask
& LVIF_TEXT
) && (lpLVItem
->mask
& LVIF_NORECOMPUTE
))
6749 lpLVItem
->pszText
= LPSTR_TEXTCALLBACKW
;
6751 /* we store only a little state, so if we're not asked, we're done */
6752 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
6754 /* if focus is handled by us, report it */
6755 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
6757 lpLVItem
->state
&= ~LVIS_FOCUSED
;
6758 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
6759 lpLVItem
->state
|= LVIS_FOCUSED
;
6762 /* and do the same for selection, if we handle it */
6763 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
6765 lpLVItem
->state
&= ~LVIS_SELECTED
;
6766 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
6767 lpLVItem
->state
|= LVIS_SELECTED
;
6773 /* find the item and subitem structures before we proceed */
6774 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
6775 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
6780 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
6781 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
6784 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
6789 pItemHdr
= &lpItem
->hdr
;
6791 /* Do we need to query the state from the app? */
6792 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
6794 dispInfo
.item
.mask
|= LVIF_STATE
;
6795 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
6798 /* Do we need to enquire about the image? */
6799 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
6800 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
6802 dispInfo
.item
.mask
|= LVIF_IMAGE
;
6803 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
6806 /* Only items support indentation */
6807 if ((lpLVItem
->mask
& LVIF_INDENT
) && lpItem
->iIndent
== I_INDENTCALLBACK
&&
6810 dispInfo
.item
.mask
|= LVIF_INDENT
;
6811 dispInfo
.item
.iIndent
= I_INDENTCALLBACK
;
6814 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
6815 if ((lpLVItem
->mask
& LVIF_TEXT
) && !(lpLVItem
->mask
& LVIF_NORECOMPUTE
) &&
6816 !is_text(pItemHdr
->pszText
))
6818 dispInfo
.item
.mask
|= LVIF_TEXT
;
6819 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
6820 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
6821 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
6822 *dispInfo
.item
.pszText
= '\0';
6825 /* If we don't have all the requested info, query the application */
6826 if (dispInfo
.item
.mask
)
6828 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
6829 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
6830 dispInfo
.item
.lParam
= lpItem
->lParam
;
6831 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
6832 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
6835 /* we should not store values for subitems */
6836 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
6838 /* Now, handle the iImage field */
6839 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
6841 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
6842 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
6843 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
6845 else if (lpLVItem
->mask
& LVIF_IMAGE
)
6847 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
6848 lpLVItem
->iImage
= pItemHdr
->iImage
;
6850 lpLVItem
->iImage
= 0;
6853 /* The pszText field */
6854 if (dispInfo
.item
.mask
& LVIF_TEXT
)
6856 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
6857 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
6859 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
6861 else if (lpLVItem
->mask
& LVIF_TEXT
)
6863 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
6864 if (isW
|| !is_text(pItemHdr
->pszText
)) lpLVItem
->pszText
= pItemHdr
->pszText
;
6865 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
6868 /* Next is the lParam field */
6869 if (dispInfo
.item
.mask
& LVIF_PARAM
)
6871 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
6872 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
6873 lpItem
->lParam
= dispInfo
.item
.lParam
;
6875 else if (lpLVItem
->mask
& LVIF_PARAM
)
6876 lpLVItem
->lParam
= lpItem
->lParam
;
6878 /* if this is a subitem, we're done */
6879 if (isubitem
) return TRUE
;
6881 /* ... the state field (this one is different due to uCallbackmask) */
6882 if (lpLVItem
->mask
& LVIF_STATE
)
6884 lpLVItem
->state
= lpItem
->state
& lpLVItem
->stateMask
;
6885 if (dispInfo
.item
.mask
& LVIF_STATE
)
6887 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
6888 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
6890 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
6892 lpLVItem
->state
&= ~LVIS_FOCUSED
;
6893 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
6894 lpLVItem
->state
|= LVIS_FOCUSED
;
6896 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
6898 lpLVItem
->state
&= ~LVIS_SELECTED
;
6899 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
6900 lpLVItem
->state
|= LVIS_SELECTED
;
6904 /* and last, but not least, the indent field */
6905 if (dispInfo
.item
.mask
& LVIF_INDENT
)
6907 lpLVItem
->iIndent
= dispInfo
.item
.iIndent
;
6908 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && lpItem
->iIndent
== I_INDENTCALLBACK
)
6909 lpItem
->iIndent
= dispInfo
.item
.iIndent
;
6911 else if (lpLVItem
->mask
& LVIF_INDENT
)
6913 lpLVItem
->iIndent
= lpItem
->iIndent
;
6921 * Retrieves item attributes.
6924 * [I] hwnd : window handle
6925 * [IO] lpLVItem : item info
6926 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6927 * if FALSE, then lpLVItem is a LPLVITEMA.
6930 * This is the external 'GetItem' interface -- it properly copies
6931 * the text in the provided buffer.
6937 static BOOL
LISTVIEW_GetItemExtT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
6942 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
6945 pszText
= lpLVItem
->pszText
;
6946 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
6947 if (bResult
&& (lpLVItem
->mask
& LVIF_TEXT
) && lpLVItem
->pszText
!= pszText
)
6949 if (lpLVItem
->pszText
!= LPSTR_TEXTCALLBACKW
)
6950 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
6952 pszText
= LPSTR_TEXTCALLBACKW
;
6954 lpLVItem
->pszText
= pszText
;
6962 * Retrieves the position (upper-left) of the listview control item.
6963 * Note that for LVS_ICON style, the upper-left is that of the icon
6964 * and not the bounding box.
6967 * [I] infoPtr : valid pointer to the listview structure
6968 * [I] nItem : item index
6969 * [O] lpptPosition : coordinate information
6975 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
6979 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
6981 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
6983 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6984 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
6986 if (infoPtr
->uView
== LV_VIEW_ICON
)
6988 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
6989 lpptPosition
->y
+= ICON_TOP_PADDING
;
6991 lpptPosition
->x
+= Origin
.x
;
6992 lpptPosition
->y
+= Origin
.y
;
6994 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition
));
7001 * Retrieves the bounding rectangle for a listview control item.
7004 * [I] infoPtr : valid pointer to the listview structure
7005 * [I] nItem : item index
7006 * [IO] lprc : bounding rectangle coordinates
7007 * lprc->left specifies the portion of the item for which the bounding
7008 * rectangle will be retrieved.
7010 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
7011 * including the icon and label.
7014 * * Experiment shows that native control returns:
7015 * * width = min (48, length of text line)
7016 * * .left = position.x - (width - iconsize.cx)/2
7017 * * .right = .left + width
7018 * * height = #lines of text * ntmHeight + icon height + 8
7019 * * .top = position.y - 2
7020 * * .bottom = .top + height
7021 * * separation between items .y = itemSpacing.cy - height
7022 * * .x = itemSpacing.cx - width
7023 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
7026 * * Experiment shows that native control returns:
7027 * * width = iconSize.cx + 16
7028 * * .left = position.x - (width - iconsize.cx)/2
7029 * * .right = .left + width
7030 * * height = iconSize.cy + 4
7031 * * .top = position.y - 2
7032 * * .bottom = .top + height
7033 * * separation between items .y = itemSpacing.cy - height
7034 * * .x = itemSpacing.cx - width
7035 * LVIR_LABEL Returns the bounding rectangle of the item text.
7038 * * Experiment shows that native control returns:
7039 * * width = text length
7040 * * .left = position.x - width/2
7041 * * .right = .left + width
7042 * * height = ntmH * linecount + 2
7043 * * .top = position.y + iconSize.cy + 6
7044 * * .bottom = .top + height
7045 * * separation between items .y = itemSpacing.cy - height
7046 * * .x = itemSpacing.cx - width
7047 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
7048 * rectangles, but excludes columns in report view.
7055 * Note that the bounding rectangle of the label in the LVS_ICON view depends
7056 * upon whether the window has the focus currently and on whether the item
7057 * is the one with the focus. Ensure that the control's record of which
7058 * item has the focus agrees with the items' records.
7060 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
7062 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
7063 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
7064 POINT Position
, Origin
;
7068 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
7070 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
7072 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7073 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
7075 /* Be smart and try to figure out the minimum we have to do */
7076 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
7077 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
7078 if (infoPtr
->uView
== LV_VIEW_ICON
&& lprc
->left
!= LVIR_ICON
&&
7079 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
7080 oversizedBox
= TRUE
;
7082 /* get what we need from the item before hand, so we make
7083 * only one request. This can speed up things, if data
7084 * is stored on the app side */
7086 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
7087 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
7088 lvItem
.iItem
= nItem
;
7089 lvItem
.iSubItem
= 0;
7090 lvItem
.pszText
= szDispText
;
7091 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7092 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
7093 /* we got the state already up, simulate it here, to avoid a reget */
7094 if (infoPtr
->uView
== LV_VIEW_ICON
&& (lprc
->left
!= LVIR_ICON
))
7096 lvItem
.mask
|= LVIF_STATE
;
7097 lvItem
.stateMask
= LVIS_FOCUSED
;
7098 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
7101 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
7102 lprc
->left
= LVIR_BOUNDS
;
7108 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
7112 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, NULL
, lprc
);
7116 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
7119 case LVIR_SELECTBOUNDS
:
7120 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, lprc
, NULL
, NULL
, NULL
);
7124 WARN("Unknown value: %d\n", lprc
->left
);
7128 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7130 if (mode
!= LVIR_BOUNDS
)
7131 OffsetRect(lprc
, Origin
.x
+ LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
,
7132 Position
.y
+ Origin
.y
);
7134 OffsetRect(lprc
, Origin
.x
, Position
.y
+ Origin
.y
);
7137 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
7139 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc
));
7146 * Retrieves the spacing between listview control items.
7149 * [I] infoPtr : valid pointer to the listview structure
7150 * [IO] lprc : rectangle to receive the output
7151 * on input, lprc->top = nSubItem
7152 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
7154 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
7155 * not only those of the first column.
7161 static BOOL
LISTVIEW_GetSubItemRect(const LISTVIEW_INFO
*infoPtr
, INT item
, LPRECT lprc
)
7163 RECT rect
= { 0, 0, 0, 0 };
7167 if (!lprc
) return FALSE
;
7169 TRACE("(item=%d, subitem=%d, type=%d)\n", item
, lprc
->top
, lprc
->left
);
7170 /* Subitem of '0' means item itself, and this works for all control view modes */
7172 return LISTVIEW_GetItemRect(infoPtr
, item
, lprc
);
7174 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) return FALSE
;
7176 LISTVIEW_GetOrigin(infoPtr
, &origin
);
7177 /* this works for any item index, no matter if it exists or not */
7178 y
= item
* infoPtr
->nItemHeight
+ origin
.y
;
7180 if (infoPtr
->hwndHeader
&& SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMRECT
, lprc
->top
, (LPARAM
)&rect
))
7183 rect
.bottom
= infoPtr
->nItemHeight
;
7187 /* Native implementation is broken for this case and garbage is left for left and right fields,
7188 we zero them to get predictable output */
7189 lprc
->left
= lprc
->right
= lprc
->top
= 0;
7190 lprc
->bottom
= infoPtr
->nItemHeight
;
7191 OffsetRect(lprc
, origin
.x
, y
);
7192 TRACE("return rect %s\n", wine_dbgstr_rect(lprc
));
7200 /* it doesn't matter if main item actually has an icon, if imagelist is set icon width is returned */
7201 if (infoPtr
->himlSmall
)
7202 rect
.right
= rect
.left
+ infoPtr
->iconSize
.cx
;
7204 rect
.right
= rect
.left
;
7206 rect
.bottom
= rect
.top
+ infoPtr
->iconSize
.cy
;
7214 ERR("Unknown bounds=%d\n", lprc
->left
);
7218 OffsetRect(&rect
, origin
.x
, y
);
7220 TRACE("return rect %s\n", wine_dbgstr_rect(lprc
));
7227 * Retrieves the spacing between listview control items.
7230 * [I] infoPtr : valid pointer to the listview structure
7231 * [I] bSmall : flag for small or large icon
7234 * Horizontal + vertical spacing
7236 static LONG
LISTVIEW_GetItemSpacing(const LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
7242 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
7246 if (infoPtr
->uView
== LV_VIEW_ICON
)
7247 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
7249 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
7256 * Retrieves the state of a listview control item.
7259 * [I] infoPtr : valid pointer to the listview structure
7260 * [I] nItem : item index
7261 * [I] uMask : state mask
7264 * State specified by the mask.
7266 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
7270 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
7272 lvItem
.iItem
= nItem
;
7273 lvItem
.iSubItem
= 0;
7274 lvItem
.mask
= LVIF_STATE
;
7275 lvItem
.stateMask
= uMask
;
7276 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
7278 return lvItem
.state
& uMask
;
7283 * Retrieves the text of a listview control item or subitem.
7286 * [I] hwnd : window handle
7287 * [I] nItem : item index
7288 * [IO] lpLVItem : item information
7289 * [I] isW : TRUE if lpLVItem is Unicode
7292 * SUCCESS : string length
7295 static INT
LISTVIEW_GetItemTextT(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
7297 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
7299 lpLVItem
->mask
= LVIF_TEXT
;
7300 lpLVItem
->iItem
= nItem
;
7301 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
7303 return textlenT(lpLVItem
->pszText
, isW
);
7308 * Searches for an item based on properties + relationships.
7311 * [I] infoPtr : valid pointer to the listview structure
7312 * [I] nItem : item index
7313 * [I] uFlags : relationship flag
7316 * SUCCESS : item index
7319 static INT
LISTVIEW_GetNextItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
7322 LVFINDINFOW lvFindInfo
;
7323 INT nCountPerColumn
;
7327 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
7328 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
7330 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
7332 if (uFlags
& LVNI_CUT
)
7335 if (uFlags
& LVNI_DROPHILITED
)
7336 uMask
|= LVIS_DROPHILITED
;
7338 if (uFlags
& LVNI_FOCUSED
)
7339 uMask
|= LVIS_FOCUSED
;
7341 if (uFlags
& LVNI_SELECTED
)
7342 uMask
|= LVIS_SELECTED
;
7344 /* if we're asked for the focused item, that's only one,
7345 * so it's worth optimizing */
7346 if (uFlags
& LVNI_FOCUSED
)
7348 if ((LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) != uMask
) return -1;
7349 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
7352 if (uFlags
& LVNI_ABOVE
)
7354 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
7359 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7365 /* Special case for autoarrange - move 'til the top of a list */
7366 if (is_autoarrange(infoPtr
))
7368 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7369 while (nItem
- nCountPerRow
>= 0)
7371 nItem
-= nCountPerRow
;
7372 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7377 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7378 lvFindInfo
.vkDirection
= VK_UP
;
7379 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7380 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7382 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7387 else if (uFlags
& LVNI_BELOW
)
7389 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
7391 while (nItem
< infoPtr
->nItemCount
)
7394 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7400 /* Special case for autoarrange - move 'til the bottom of a list */
7401 if (is_autoarrange(infoPtr
))
7403 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7404 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
7406 nItem
+= nCountPerRow
;
7407 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7412 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7413 lvFindInfo
.vkDirection
= VK_DOWN
;
7414 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7415 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7417 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7422 else if (uFlags
& LVNI_TOLEFT
)
7424 if (infoPtr
->uView
== LV_VIEW_LIST
)
7426 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
7427 while (nItem
- nCountPerColumn
>= 0)
7429 nItem
-= nCountPerColumn
;
7430 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7434 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
7436 /* Special case for autoarrange - move 'til the beginning of a row */
7437 if (is_autoarrange(infoPtr
))
7439 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7440 while (nItem
% nCountPerRow
> 0)
7443 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7448 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7449 lvFindInfo
.vkDirection
= VK_LEFT
;
7450 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7451 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7453 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7458 else if (uFlags
& LVNI_TORIGHT
)
7460 if (infoPtr
->uView
== LV_VIEW_LIST
)
7462 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
7463 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
7465 nItem
+= nCountPerColumn
;
7466 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7470 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
7472 /* Special case for autoarrange - move 'til the end of a row */
7473 if (is_autoarrange(infoPtr
))
7475 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7476 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
7479 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7484 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7485 lvFindInfo
.vkDirection
= VK_RIGHT
;
7486 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7487 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7489 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7498 /* search by index */
7499 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
7501 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
7509 /* LISTVIEW_GetNumberOfWorkAreas */
7513 * Retrieves the origin coordinates when in icon or small icon display mode.
7516 * [I] infoPtr : valid pointer to the listview structure
7517 * [O] lpptOrigin : coordinate information
7522 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
7524 INT nHorzPos
= 0, nVertPos
= 0;
7525 SCROLLINFO scrollInfo
;
7527 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7528 scrollInfo
.fMask
= SIF_POS
;
7530 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
7531 nHorzPos
= scrollInfo
.nPos
;
7532 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
7533 nVertPos
= scrollInfo
.nPos
;
7535 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
7537 lpptOrigin
->x
= infoPtr
->rcList
.left
;
7538 lpptOrigin
->y
= infoPtr
->rcList
.top
;
7539 if (infoPtr
->uView
== LV_VIEW_LIST
)
7540 nHorzPos
*= infoPtr
->nItemWidth
;
7541 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7542 nVertPos
*= infoPtr
->nItemHeight
;
7544 lpptOrigin
->x
-= nHorzPos
;
7545 lpptOrigin
->y
-= nVertPos
;
7547 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin
));
7552 * Retrieves the width of a string.
7555 * [I] hwnd : window handle
7556 * [I] lpszText : text string to process
7557 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
7560 * SUCCESS : string width (in pixels)
7563 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
7568 if (is_text(lpszText
))
7570 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
7571 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
7572 HFONT hOldFont
= SelectObject(hdc
, hFont
);
7575 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
7577 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
7578 SelectObject(hdc
, hOldFont
);
7579 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7581 return stringSize
.cx
;
7586 * Determines which listview item is located at the specified position.
7589 * [I] infoPtr : valid pointer to the listview structure
7590 * [IO] lpht : hit test information
7591 * [I] subitem : fill out iSubItem.
7592 * [I] select : return the index only if the hit selects the item
7595 * (mm 20001022): We must not allow iSubItem to be touched, for
7596 * an app might pass only a structure with space up to iItem!
7597 * (MS Office 97 does that for instance in the file open dialog)
7600 * SUCCESS : item index
7603 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
7605 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
7606 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
7607 POINT Origin
, Position
, opt
;
7613 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht
->pt
), subitem
, select
);
7617 if (subitem
) lpht
->iSubItem
= 0;
7619 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7621 /* set whole list relation flags */
7622 if (subitem
&& infoPtr
->uView
== LV_VIEW_DETAILS
)
7624 /* LVM_SUBITEMHITTEST checks left bound of possible client area */
7625 if (infoPtr
->rcList
.left
> lpht
->pt
.x
&& Origin
.x
< lpht
->pt
.x
)
7626 lpht
->flags
|= LVHT_TOLEFT
;
7628 if (lpht
->pt
.y
< infoPtr
->rcList
.top
&& lpht
->pt
.y
>= 0)
7629 opt
.y
= lpht
->pt
.y
+ infoPtr
->rcList
.top
;
7633 if (infoPtr
->rcList
.bottom
< opt
.y
)
7634 lpht
->flags
|= LVHT_BELOW
;
7638 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
7639 lpht
->flags
|= LVHT_TOLEFT
;
7640 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
7641 lpht
->flags
|= LVHT_TORIGHT
;
7643 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
7644 lpht
->flags
|= LVHT_ABOVE
;
7645 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
7646 lpht
->flags
|= LVHT_BELOW
;
7649 /* even if item is invalid try to find subitem */
7650 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& subitem
)
7655 opt
.x
= lpht
->pt
.x
- Origin
.x
;
7657 lpht
->iSubItem
= -1;
7658 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
7660 pRect
= &LISTVIEW_GetColumnInfo(infoPtr
, j
)->rcHeader
;
7662 if ((opt
.x
>= pRect
->left
) && (opt
.x
< pRect
->right
))
7668 TRACE("lpht->iSubItem=%d\n", lpht
->iSubItem
);
7670 /* if we're outside horizontal columns bounds there's nothing to test further */
7671 if (lpht
->iSubItem
== -1)
7674 lpht
->flags
= LVHT_NOWHERE
;
7679 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
7680 if (lpht
->flags
) return -1;
7682 lpht
->flags
|= LVHT_NOWHERE
;
7684 /* first deal with the large items */
7685 rcSearch
.left
= lpht
->pt
.x
;
7686 rcSearch
.top
= lpht
->pt
.y
;
7687 rcSearch
.right
= rcSearch
.left
+ 1;
7688 rcSearch
.bottom
= rcSearch
.top
+ 1;
7690 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
7691 iterator_next(&i
); /* go to first item in the sequence */
7693 iterator_destroy(&i
);
7695 TRACE("lpht->iItem=%d\n", iItem
);
7696 if (iItem
== -1) return -1;
7698 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
7699 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
7700 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
7701 if (infoPtr
->uView
== LV_VIEW_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
7702 lvItem
.iItem
= iItem
;
7703 lvItem
.iSubItem
= subitem
? lpht
->iSubItem
: 0;
7704 lvItem
.pszText
= szDispText
;
7705 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7706 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
7707 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
7709 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
7710 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
7711 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
7713 if (lpht
->pt
.y
< infoPtr
->rcList
.top
&& lpht
->pt
.y
>= 0)
7714 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
+ infoPtr
->rcList
.top
;
7716 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
7718 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7721 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
7722 opt
.x
= lpht
->pt
.x
- Origin
.x
;
7726 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
7727 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
7729 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds
));
7730 if (!PtInRect(&rcBounds
, opt
)) return -1;
7732 /* That's a special case - row rectangle is used as item rectangle and
7733 returned flags contain all item parts. */
7734 is_fullrow
= (infoPtr
->uView
== LV_VIEW_DETAILS
) && ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) || (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
));
7736 if (PtInRect(&rcIcon
, opt
))
7737 lpht
->flags
|= LVHT_ONITEMICON
;
7738 else if (PtInRect(&rcLabel
, opt
))
7739 lpht
->flags
|= LVHT_ONITEMLABEL
;
7740 else if (infoPtr
->himlState
&& PtInRect(&rcState
, opt
))
7741 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
7742 if (is_fullrow
&& !(lpht
->flags
& LVHT_ONITEM
))
7744 lpht
->flags
= LVHT_ONITEM
| LVHT_ABOVE
;
7746 if (lpht
->flags
& LVHT_ONITEM
)
7747 lpht
->flags
&= ~LVHT_NOWHERE
;
7748 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
7750 if (select
&& !is_fullrow
)
7752 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7754 /* get main item bounds */
7755 lvItem
.iSubItem
= 0;
7756 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
7757 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
7758 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
7760 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
7762 return lpht
->iItem
= iItem
;
7767 * Inserts a new item in the listview control.
7770 * [I] infoPtr : valid pointer to the listview structure
7771 * [I] lpLVItem : item information
7772 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
7775 * SUCCESS : new item index
7778 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
7785 BOOL is_sorted
, has_changed
;
7787 HWND hwndSelf
= infoPtr
->hwndSelf
;
7789 TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
7791 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
7793 /* make sure it's an item, and not a subitem; cannot insert a subitem */
7794 if (!lpLVItem
|| lpLVItem
->iSubItem
) return -1;
7796 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
7798 if (!(lpItem
= Alloc(sizeof(ITEM_INFO
)))) return -1;
7800 /* insert item in listview control data structure */
7801 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
7802 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
7804 /* link with id struct */
7805 if (!(lpID
= Alloc(sizeof(ITEM_ID
)))) goto fail
;
7807 lpID
->item
= hdpaSubItems
;
7808 lpID
->id
= get_next_itemid(infoPtr
);
7809 if ( DPA_InsertPtr(infoPtr
->hdpaItemIds
, infoPtr
->nItemCount
, lpID
) == -1) goto fail
;
7811 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
7812 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
7814 if (lpLVItem
->iItem
< 0 && !is_sorted
) return -1;
7816 /* calculate new item index */
7824 textW
= textdupTtoW(lpLVItem
->pszText
, isW
);
7826 while (i
< infoPtr
->nItemCount
)
7828 hItem
= DPA_GetPtr( infoPtr
->hdpaItems
, i
);
7829 item_s
= DPA_GetPtr(hItem
, 0);
7831 cmpv
= textcmpWT(item_s
->hdr
.pszText
, textW
, TRUE
);
7832 if (infoPtr
->dwStyle
& LVS_SORTDESCENDING
) cmpv
*= -1;
7834 if (cmpv
>= 0) break;
7838 textfreeT(textW
, isW
);
7843 nItem
= min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
7845 TRACE("inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
7846 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
7847 if (nItem
== -1) goto fail
;
7848 infoPtr
->nItemCount
++;
7850 /* shift indices first so they don't get tangled */
7851 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
7853 /* set the item attributes */
7854 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
7856 /* full size structure expected - _WIN32IE >= 0x560 */
7859 else if (lpLVItem
->mask
& LVIF_INDENT
)
7861 /* indent member expected - _WIN32IE >= 0x300 */
7862 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
7866 /* minimal structure expected */
7867 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
7870 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
7872 if (item
.mask
& LVIF_STATE
)
7874 item
.stateMask
|= LVIS_STATEIMAGEMASK
;
7875 item
.state
&= ~LVIS_STATEIMAGEMASK
;
7876 item
.state
|= INDEXTOSTATEIMAGEMASK(1);
7880 item
.mask
|= LVIF_STATE
;
7881 item
.stateMask
= LVIS_STATEIMAGEMASK
;
7882 item
.state
= INDEXTOSTATEIMAGEMASK(1);
7886 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
7888 /* make room for the position, if we are in the right mode */
7889 if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
7891 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
7893 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
7895 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
7900 /* send LVN_INSERTITEM notification */
7901 memset(&nmlv
, 0, sizeof(NMLISTVIEW
));
7903 nmlv
.lParam
= lpItem
->lParam
;
7904 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
7905 if (!IsWindow(hwndSelf
))
7908 /* align items (set position of each item) */
7909 if (infoPtr
->uView
== LV_VIEW_SMALLICON
|| infoPtr
->uView
== LV_VIEW_ICON
)
7913 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
7914 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
7916 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
7918 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
7921 /* now is the invalidation fun */
7922 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
7926 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
7927 LISTVIEW_ShiftFocus(infoPtr
, infoPtr
->nFocusedItem
, nItem
, -1);
7928 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
7929 infoPtr
->nItemCount
--;
7931 DPA_DeletePtr(hdpaSubItems
, 0);
7932 DPA_Destroy (hdpaSubItems
);
7939 * Checks item visibility.
7942 * [I] infoPtr : valid pointer to the listview structure
7943 * [I] nFirst : item index to check for
7946 * Item visible : TRUE
7947 * Item invisible or failure : FALSE
7949 static BOOL
LISTVIEW_IsItemVisible(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
7951 POINT Origin
, Position
;
7956 TRACE("nItem=%d\n", nItem
);
7958 if (nItem
< 0 || nItem
>= DPA_GetPtrCount(infoPtr
->hdpaItems
)) return FALSE
;
7960 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7961 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
7962 rcItem
.left
= Position
.x
+ Origin
.x
;
7963 rcItem
.top
= Position
.y
+ Origin
.y
;
7964 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
7965 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
7967 hdc
= GetDC(infoPtr
->hwndSelf
);
7968 if (!hdc
) return FALSE
;
7969 ret
= RectVisible(hdc
, &rcItem
);
7970 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7977 * Redraws a range of items.
7980 * [I] infoPtr : valid pointer to the listview structure
7981 * [I] nFirst : first item
7982 * [I] nLast : last item
7988 static BOOL
LISTVIEW_RedrawItems(const LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
7992 for (i
= max(nFirst
, 0); i
<= min(nLast
, infoPtr
->nItemCount
- 1); i
++)
7993 LISTVIEW_InvalidateItem(infoPtr
, i
);
8000 * Scroll the content of a listview.
8003 * [I] infoPtr : valid pointer to the listview structure
8004 * [I] dx : horizontal scroll amount in pixels
8005 * [I] dy : vertical scroll amount in pixels
8012 * If the control is in report view (LV_VIEW_DETAILS) the control can
8013 * be scrolled only in line increments. "dy" will be rounded to the
8014 * nearest number of pixels that are a whole line. Ex: if line height
8015 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
8016 * is passed, then the scroll will be 0. (per MSDN 7/2002)
8018 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
8020 switch(infoPtr
->uView
) {
8021 case LV_VIEW_DETAILS
:
8022 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
8023 dy
/= infoPtr
->nItemHeight
;
8026 if (dy
!= 0) return FALSE
;
8032 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
);
8033 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
);
8040 * Sets the background color.
8043 * [I] infoPtr : valid pointer to the listview structure
8044 * [I] color : background color
8050 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF color
)
8052 TRACE("(color=%x)\n", color
);
8054 if(infoPtr
->clrBk
!= color
) {
8055 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
8056 infoPtr
->clrBk
= color
;
8057 if (color
== CLR_NONE
)
8058 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
8061 infoPtr
->hBkBrush
= CreateSolidBrush(color
);
8062 infoPtr
->dwLvExStyle
&= ~LVS_EX_TRANSPARENTBKGND
;
8069 /* LISTVIEW_SetBkImage */
8071 /*** Helper for {Insert,Set}ColumnT *only* */
8072 static void column_fill_hditem(const LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
,
8073 const LVCOLUMNW
*lpColumn
, BOOL isW
)
8075 if (lpColumn
->mask
& LVCF_FMT
)
8077 /* format member is valid */
8078 lphdi
->mask
|= HDI_FORMAT
;
8080 /* set text alignment (leftmost column must be left-aligned) */
8081 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
8082 lphdi
->fmt
|= HDF_LEFT
;
8083 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
8084 lphdi
->fmt
|= HDF_RIGHT
;
8085 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
8086 lphdi
->fmt
|= HDF_CENTER
;
8088 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
8089 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
8091 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
8093 lphdi
->fmt
|= HDF_IMAGE
;
8094 lphdi
->iImage
= I_IMAGECALLBACK
;
8097 if (lpColumn
->fmt
& LVCFMT_FIXED_WIDTH
)
8098 lphdi
->fmt
|= HDF_FIXEDWIDTH
;
8101 if (lpColumn
->mask
& LVCF_WIDTH
)
8103 lphdi
->mask
|= HDI_WIDTH
;
8104 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
8106 /* make it fill the remainder of the controls width */
8110 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
8112 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
8113 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
8116 /* retrieve the layout of the header */
8117 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
8118 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, wine_dbgstr_rect(&rcHeader
));
8120 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
8123 lphdi
->cxy
= lpColumn
->cx
;
8126 if (lpColumn
->mask
& LVCF_TEXT
)
8128 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
8129 lphdi
->fmt
|= HDF_STRING
;
8130 lphdi
->pszText
= lpColumn
->pszText
;
8131 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
8134 if (lpColumn
->mask
& LVCF_IMAGE
)
8136 lphdi
->mask
|= HDI_IMAGE
;
8137 lphdi
->iImage
= lpColumn
->iImage
;
8140 if (lpColumn
->mask
& LVCF_ORDER
)
8142 lphdi
->mask
|= HDI_ORDER
;
8143 lphdi
->iOrder
= lpColumn
->iOrder
;
8150 * Inserts a new column.
8153 * [I] infoPtr : valid pointer to the listview structure
8154 * [I] nColumn : column index
8155 * [I] lpColumn : column information
8156 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
8159 * SUCCESS : new column index
8162 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
8163 const LVCOLUMNW
*lpColumn
, BOOL isW
)
8165 COLUMN_INFO
*lpColumnInfo
;
8169 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
8171 if (!lpColumn
|| nColumn
< 0) return -1;
8172 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
8174 ZeroMemory(&hdi
, sizeof(HDITEMW
));
8175 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
8178 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
8179 * (can be seen in SPY) otherwise column never gets added.
8181 if (!(lpColumn
->mask
& LVCF_WIDTH
)) {
8182 hdi
.mask
|= HDI_WIDTH
;
8187 * when the iSubItem is available Windows copies it to the header lParam. It seems
8188 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
8190 if (lpColumn
->mask
& LVCF_SUBITEM
)
8192 hdi
.mask
|= HDI_LPARAM
;
8193 hdi
.lParam
= lpColumn
->iSubItem
;
8196 /* create header if not present */
8197 LISTVIEW_CreateHeader(infoPtr
);
8198 if (!(LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
) &&
8199 (infoPtr
->uView
== LV_VIEW_DETAILS
) && (WS_VISIBLE
& infoPtr
->dwStyle
))
8201 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
8204 /* insert item in header control */
8205 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
8206 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
8207 nColumn
, (LPARAM
)&hdi
);
8208 if (nNewColumn
== -1) return -1;
8209 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
8211 /* create our own column info */
8212 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
8213 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
8215 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
8216 if (lpColumn
->mask
& LVCF_MINWIDTH
) lpColumnInfo
->cxMin
= lpColumn
->cxMin
;
8217 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMRECT
, nNewColumn
, (LPARAM
)&lpColumnInfo
->rcHeader
))
8220 /* now we have to actually adjust the data */
8221 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
8223 SUBITEM_INFO
*lpSubItem
;
8229 item
.iSubItem
= nNewColumn
;
8230 item
.mask
= LVIF_TEXT
| LVIF_IMAGE
;
8231 item
.iImage
= I_IMAGECALLBACK
;
8232 item
.pszText
= LPSTR_TEXTCALLBACKW
;
8234 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
8236 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
8237 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
8239 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
8240 if (lpSubItem
->iSubItem
>= nNewColumn
)
8241 lpSubItem
->iSubItem
++;
8244 /* add new subitem for each item */
8246 set_sub_item(infoPtr
, &item
, isW
, &changed
);
8250 /* make space for the new column */
8251 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
8252 LISTVIEW_UpdateItemSize(infoPtr
);
8257 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
8260 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
8268 * Sets the attributes of a header item.
8271 * [I] infoPtr : valid pointer to the listview structure
8272 * [I] nColumn : column index
8273 * [I] lpColumn : column attributes
8274 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
8280 static BOOL
LISTVIEW_SetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
,
8281 const LVCOLUMNW
*lpColumn
, BOOL isW
)
8283 HDITEMW hdi
, hdiget
;
8286 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
8288 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
8290 ZeroMemory(&hdi
, sizeof(HDITEMW
));
8291 if (lpColumn
->mask
& LVCF_FMT
)
8293 hdi
.mask
|= HDI_FORMAT
;
8294 hdiget
.mask
= HDI_FORMAT
;
8295 if (SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdiget
))
8296 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
8298 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
8300 /* set header item attributes */
8301 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, nColumn
, (LPARAM
)&hdi
);
8302 if (!bResult
) return FALSE
;
8304 if (lpColumn
->mask
& LVCF_FMT
)
8306 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
8307 INT oldFmt
= lpColumnInfo
->fmt
;
8309 lpColumnInfo
->fmt
= lpColumn
->fmt
;
8310 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
8312 if (infoPtr
->uView
== LV_VIEW_DETAILS
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
8316 if (lpColumn
->mask
& LVCF_MINWIDTH
)
8317 LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->cxMin
= lpColumn
->cxMin
;
8324 * Sets the column order array
8327 * [I] infoPtr : valid pointer to the listview structure
8328 * [I] iCount : number of elements in column order array
8329 * [I] lpiArray : pointer to column order array
8335 static BOOL
LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
8337 if (!infoPtr
->hwndHeader
) return FALSE
;
8338 infoPtr
->colRectsDirty
= TRUE
;
8339 return SendMessageW(infoPtr
->hwndHeader
, HDM_SETORDERARRAY
, iCount
, (LPARAM
)lpiArray
);
8344 * Sets the width of a column
8347 * [I] infoPtr : valid pointer to the listview structure
8348 * [I] nColumn : column index
8349 * [I] cx : column width
8355 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
8357 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
8361 TRACE("(nColumn=%d, cx=%d)\n", nColumn
, cx
);
8363 /* set column width only if in report or list mode */
8364 if (infoPtr
->uView
!= LV_VIEW_DETAILS
&& infoPtr
->uView
!= LV_VIEW_LIST
) return FALSE
;
8366 /* take care of invalid cx values - LVSCW_AUTOSIZE_* values are negative,
8367 with _USEHEADER being the lowest */
8368 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& cx
< LVSCW_AUTOSIZE_USEHEADER
) cx
= LVSCW_AUTOSIZE
;
8369 else if (infoPtr
->uView
== LV_VIEW_LIST
&& cx
<= 0) return FALSE
;
8371 /* resize all columns if in LV_VIEW_LIST mode */
8372 if(infoPtr
->uView
== LV_VIEW_LIST
)
8374 infoPtr
->nItemWidth
= cx
;
8375 LISTVIEW_InvalidateList(infoPtr
);
8379 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
8381 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
8386 lvItem
.mask
= LVIF_TEXT
;
8388 lvItem
.iSubItem
= nColumn
;
8389 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
8390 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
8392 lvItem
.pszText
= szDispText
;
8393 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
8394 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
8395 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
8397 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
8398 max_cx
+= infoPtr
->iconSize
.cx
;
8399 max_cx
+= TRAILING_LABEL_PADDING
;
8400 if (nColumn
== 0 && (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
))
8401 max_cx
+= GetSystemMetrics(SM_CXSMICON
);
8404 /* autosize based on listview items width */
8405 if(cx
== LVSCW_AUTOSIZE
)
8407 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
8409 /* if iCol is the last column make it fill the remainder of the controls width */
8410 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
8415 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
8416 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
8418 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
8422 /* Despite what the MS docs say, if this is not the last
8423 column, then MS resizes the column to the width of the
8424 largest text string in the column, including headers
8425 and items. This is different from LVSCW_AUTOSIZE in that
8426 LVSCW_AUTOSIZE ignores the header string length. */
8429 /* retrieve header text */
8430 hdi
.mask
= HDI_TEXT
|HDI_FORMAT
|HDI_IMAGE
|HDI_BITMAP
;
8431 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
8432 hdi
.pszText
= szDispText
;
8433 if (SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdi
))
8435 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
8436 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
8437 HIMAGELIST himl
= (HIMAGELIST
)SendMessageW(infoPtr
->hwndHeader
, HDM_GETIMAGELIST
, 0, 0);
8438 INT bitmap_margin
= 0;
8441 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
8442 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
8444 if (hdi
.fmt
& (HDF_IMAGE
|HDF_BITMAP
))
8445 bitmap_margin
= SendMessageW(infoPtr
->hwndHeader
, HDM_GETBITMAPMARGIN
, 0, 0);
8447 if ((hdi
.fmt
& HDF_IMAGE
) && himl
)
8449 INT icon_cx
, icon_cy
;
8451 if (!ImageList_GetIconSize(himl
, &icon_cx
, &icon_cy
))
8452 cx
+= icon_cx
+ 2*bitmap_margin
;
8454 else if (hdi
.fmt
& HDF_BITMAP
)
8458 GetObjectW(hdi
.hbm
, sizeof(BITMAP
), &bmp
);
8459 cx
+= bmp
.bmWidth
+ 2*bitmap_margin
;
8462 SelectObject(hdc
, old_font
);
8463 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
8465 cx
= max (cx
, max_cx
);
8469 if (cx
< 0) return FALSE
;
8471 /* call header to update the column change */
8472 hdi
.mask
= HDI_WIDTH
;
8473 hdi
.cxy
= max(cx
, LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->cxMin
);
8474 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
8475 return SendMessageW(infoPtr
->hwndHeader
, HDM_SETITEMW
, nColumn
, (LPARAM
)&hdi
);
8479 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
8482 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO
*infoPtr
)
8485 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
8487 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
8488 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
8491 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
8492 ILC_COLOR
| ILC_MASK
, 2, 2);
8493 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
8494 hdc
= CreateCompatibleDC(hdc_wnd
);
8495 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
8496 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
8497 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
8499 SetRect(&rc
, 0, 0, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
8500 hbm_orig
= SelectObject(hdc
, hbm_mask
);
8501 FillRect(hdc
, &rc
, hbr_white
);
8502 InflateRect(&rc
, -2, -2);
8503 FillRect(hdc
, &rc
, hbr_black
);
8505 SelectObject(hdc
, hbm_im
);
8506 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
8507 SelectObject(hdc
, hbm_orig
);
8508 ImageList_Add(himl
, hbm_im
, hbm_mask
);
8510 SelectObject(hdc
, hbm_im
);
8511 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
8512 SelectObject(hdc
, hbm_orig
);
8513 ImageList_Add(himl
, hbm_im
, hbm_mask
);
8515 DeleteObject(hbm_mask
);
8516 DeleteObject(hbm_im
);
8524 * Sets the extended listview style.
8527 * [I] infoPtr : valid pointer to the listview structure
8529 * [I] dwStyle : style
8532 * SUCCESS : previous style
8535 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD mask
, DWORD ex_style
)
8537 DWORD old_ex_style
= infoPtr
->dwLvExStyle
;
8539 TRACE("mask=0x%08x, ex_style=0x%08x\n", mask
, ex_style
);
8543 infoPtr
->dwLvExStyle
= (old_ex_style
& ~mask
) | (ex_style
& mask
);
8545 infoPtr
->dwLvExStyle
= ex_style
;
8547 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_CHECKBOXES
)
8549 HIMAGELIST himl
= 0;
8550 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
8553 item
.mask
= LVIF_STATE
;
8554 item
.stateMask
= LVIS_STATEIMAGEMASK
;
8555 item
.state
= INDEXTOSTATEIMAGEMASK(1);
8556 LISTVIEW_SetItemState(infoPtr
, -1, &item
);
8558 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
8559 if(!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
8560 ImageList_Destroy(infoPtr
->himlState
);
8562 himl
= LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
8563 /* checkbox list replaces previous custom list or... */
8564 if(((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) &&
8565 !(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
)) ||
8566 /* ...previous was checkbox list */
8567 (old_ex_style
& LVS_EX_CHECKBOXES
))
8568 ImageList_Destroy(himl
);
8571 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_HEADERDRAGDROP
)
8575 /* if not already created */
8576 LISTVIEW_CreateHeader(infoPtr
);
8578 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
8579 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
)
8580 style
|= HDS_DRAGDROP
;
8582 style
&= ~HDS_DRAGDROP
;
8583 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
);
8586 /* GRIDLINES adds decoration at top so changes sizes */
8587 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_GRIDLINES
)
8589 LISTVIEW_CreateHeader(infoPtr
);
8590 LISTVIEW_UpdateSize(infoPtr
);
8593 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_FULLROWSELECT
)
8595 LISTVIEW_CreateHeader(infoPtr
);
8598 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_TRANSPARENTBKGND
)
8600 if (infoPtr
->dwLvExStyle
& LVS_EX_TRANSPARENTBKGND
)
8601 LISTVIEW_SetBkColor(infoPtr
, CLR_NONE
);
8604 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_HEADERINALLVIEWS
)
8606 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERINALLVIEWS
)
8607 LISTVIEW_CreateHeader(infoPtr
);
8609 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
8610 LISTVIEW_UpdateSize(infoPtr
);
8611 LISTVIEW_UpdateScroll(infoPtr
);
8614 LISTVIEW_InvalidateList(infoPtr
);
8615 return old_ex_style
;
8620 * Sets the new hot cursor used during hot tracking and hover selection.
8623 * [I] infoPtr : valid pointer to the listview structure
8624 * [I] hCursor : the new hot cursor handle
8627 * Returns the previous hot cursor
8629 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
8631 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
8633 infoPtr
->hHotCursor
= hCursor
;
8641 * Sets the hot item index.
8644 * [I] infoPtr : valid pointer to the listview structure
8645 * [I] iIndex : index
8648 * SUCCESS : previous hot item index
8649 * FAILURE : -1 (no hot item)
8651 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
8653 INT iOldIndex
= infoPtr
->nHotItem
;
8655 infoPtr
->nHotItem
= iIndex
;
8663 * Sets the amount of time the cursor must hover over an item before it is selected.
8666 * [I] infoPtr : valid pointer to the listview structure
8667 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
8670 * Returns the previous hover time
8672 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
8674 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
8676 infoPtr
->dwHoverTime
= dwHoverTime
;
8678 return oldHoverTime
;
8683 * Sets spacing for icons of LVS_ICON style.
8686 * [I] infoPtr : valid pointer to the listview structure
8687 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
8688 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
8691 * MAKELONG(oldcx, oldcy)
8693 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
8695 INT iconWidth
= 0, iconHeight
= 0;
8696 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
8698 TRACE("requested=(%d,%d)\n", cx
, cy
);
8700 /* set to defaults, if instructed to */
8701 if (cx
== -1 && cy
== -1)
8703 infoPtr
->autoSpacing
= TRUE
;
8704 if (infoPtr
->himlNormal
)
8705 ImageList_GetIconSize(infoPtr
->himlNormal
, &iconWidth
, &iconHeight
);
8706 cx
= GetSystemMetrics(SM_CXICONSPACING
) - GetSystemMetrics(SM_CXICON
) + iconWidth
;
8707 cy
= GetSystemMetrics(SM_CYICONSPACING
) - GetSystemMetrics(SM_CYICON
) + iconHeight
;
8710 infoPtr
->autoSpacing
= FALSE
;
8712 /* if 0 then keep width */
8714 infoPtr
->iconSpacing
.cx
= cx
;
8716 /* if 0 then keep height */
8718 infoPtr
->iconSpacing
.cy
= cy
;
8720 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
8721 LOWORD(oldspacing
), HIWORD(oldspacing
), infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
,
8722 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
8723 infoPtr
->ntmHeight
);
8725 /* these depend on the iconSpacing */
8726 LISTVIEW_UpdateItemSize(infoPtr
);
8731 static inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL small
)
8735 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
8742 size
->cx
= GetSystemMetrics(small
? SM_CXSMICON
: SM_CXICON
);
8743 size
->cy
= GetSystemMetrics(small
? SM_CYSMICON
: SM_CYICON
);
8752 * [I] infoPtr : valid pointer to the listview structure
8753 * [I] nType : image list type
8754 * [I] himl : image list handle
8757 * SUCCESS : old image list
8760 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
8762 INT oldHeight
= infoPtr
->nItemHeight
;
8763 HIMAGELIST himlOld
= 0;
8765 TRACE("(nType=%d, himl=%p)\n", nType
, himl
);
8770 himlOld
= infoPtr
->himlNormal
;
8771 infoPtr
->himlNormal
= himl
;
8772 if (infoPtr
->uView
== LV_VIEW_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
8773 if (infoPtr
->autoSpacing
)
8774 LISTVIEW_SetIconSpacing(infoPtr
, -1, -1);
8778 himlOld
= infoPtr
->himlSmall
;
8779 infoPtr
->himlSmall
= himl
;
8780 if (infoPtr
->uView
!= LV_VIEW_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
8781 if (infoPtr
->hwndHeader
)
8782 SendMessageW(infoPtr
->hwndHeader
, HDM_SETIMAGELIST
, 0, (LPARAM
)himl
);
8786 himlOld
= infoPtr
->himlState
;
8787 infoPtr
->himlState
= himl
;
8788 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
8789 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
8793 ERR("Unknown icon type=%d\n", nType
);
8797 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
8798 if (infoPtr
->nItemHeight
!= oldHeight
)
8799 LISTVIEW_UpdateScroll(infoPtr
);
8806 * Preallocates memory (does *not* set the actual count of items !)
8809 * [I] infoPtr : valid pointer to the listview structure
8810 * [I] nItems : item count (projected number of items to allocate)
8811 * [I] dwFlags : update flags
8817 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
8819 TRACE("(nItems=%d, dwFlags=%x)\n", nItems
, dwFlags
);
8821 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
8823 INT nOldCount
= infoPtr
->nItemCount
;
8824 infoPtr
->nItemCount
= nItems
;
8826 if (nItems
< nOldCount
)
8828 RANGE range
= { nItems
, nOldCount
};
8829 ranges_del(infoPtr
->selectionRanges
, range
);
8830 if (infoPtr
->nFocusedItem
>= nItems
)
8832 LISTVIEW_SetItemFocus(infoPtr
, -1);
8833 infoPtr
->nFocusedItem
= -1;
8834 SetRectEmpty(&infoPtr
->rcFocus
);
8838 LISTVIEW_UpdateScroll(infoPtr
);
8840 /* the flags are valid only in ownerdata report and list modes */
8841 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
) dwFlags
= 0;
8843 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
8844 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
8846 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
8847 LISTVIEW_InvalidateList(infoPtr
);
8854 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
8855 nFrom
= min(nOldCount
, nItems
);
8856 nTo
= max(nOldCount
, nItems
);
8858 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
8860 SetRect(&rcErase
, 0, nFrom
* infoPtr
->nItemHeight
, infoPtr
->nItemWidth
,
8861 nTo
* infoPtr
->nItemHeight
);
8862 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
8863 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
8864 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
8866 else /* LV_VIEW_LIST */
8868 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
8870 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
8871 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
8872 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
8873 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
8874 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
8875 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
8876 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
8878 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
8880 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
8881 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
8882 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
8883 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
8884 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
8890 /* According to MSDN for non-LVS_OWNERDATA this is just
8891 * a performance issue. The control allocates its internal
8892 * data structures for the number of items specified. It
8893 * cuts down on the number of memory allocations. Therefore
8894 * we will just issue a WARN here
8896 WARN("for non-ownerdata performance option not implemented.\n");
8904 * Sets the position of an item.
8907 * [I] infoPtr : valid pointer to the listview structure
8908 * [I] nItem : item index
8909 * [I] pt : coordinate
8915 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*pt
)
8919 TRACE("(nItem=%d, pt=%s)\n", nItem
, wine_dbgstr_point(pt
));
8921 if (!pt
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
8922 !(infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)) return FALSE
;
8925 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
8927 /* This point value seems to be an undocumented feature.
8928 * The best guess is that it means either at the origin,
8929 * or at true beginning of the list. I will assume the origin. */
8930 if ((Pt
.x
== -1) && (Pt
.y
== -1))
8933 if (infoPtr
->uView
== LV_VIEW_ICON
)
8935 Pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
8936 Pt
.y
-= ICON_TOP_PADDING
;
8941 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &Pt
, FALSE
);
8946 * Sets the state of one or many items.
8949 * [I] infoPtr : valid pointer to the listview structure
8950 * [I] nItem : item index
8951 * [I] item : item or subitem info
8957 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*item
)
8962 if (!item
) return FALSE
;
8964 lvItem
.iItem
= nItem
;
8965 lvItem
.iSubItem
= 0;
8966 lvItem
.mask
= LVIF_STATE
;
8967 lvItem
.state
= item
->state
;
8968 lvItem
.stateMask
= item
->stateMask
;
8969 TRACE("item=%s\n", debuglvitem_t(&lvItem
, TRUE
));
8976 /* special case optimization for recurring attempt to deselect all */
8977 if (lvItem
.state
== 0 && lvItem
.stateMask
== LVIS_SELECTED
&& !LISTVIEW_GetSelectedCount(infoPtr
))
8980 /* select all isn't allowed in LVS_SINGLESEL */
8981 if ((lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) && (infoPtr
->dwStyle
& LVS_SINGLESEL
))
8984 /* focus all isn't allowed */
8985 if (lvItem
.state
& lvItem
.stateMask
& LVIS_FOCUSED
) return FALSE
;
8987 notify
= infoPtr
->bDoChangeNotify
;
8988 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
8990 infoPtr
->bDoChangeNotify
= FALSE
;
8991 if (!(lvItem
.state
& LVIS_SELECTED
) && LISTVIEW_GetSelectedCount(infoPtr
))
8992 oldstate
|= LVIS_SELECTED
;
8993 if (infoPtr
->nFocusedItem
!= -1) oldstate
|= LVIS_FOCUSED
;
8996 /* apply to all items */
8997 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
8998 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) ret
= FALSE
;
9000 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
9004 infoPtr
->bDoChangeNotify
= notify
;
9008 nmlv
.uNewState
= lvItem
.state
& lvItem
.stateMask
;
9009 nmlv
.uOldState
= oldstate
& lvItem
.stateMask
;
9010 nmlv
.uChanged
= LVIF_STATE
;
9011 nmlv
.ptAction
.x
= nmlv
.ptAction
.y
= 0;
9014 notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
9018 ret
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
9025 * Sets the text of an item or subitem.
9028 * [I] hwnd : window handle
9029 * [I] nItem : item index
9030 * [I] lpLVItem : item or subitem info
9031 * [I] isW : TRUE if input is Unicode
9037 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
9041 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
9042 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
9044 lvItem
.iItem
= nItem
;
9045 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
9046 lvItem
.mask
= LVIF_TEXT
;
9047 lvItem
.pszText
= lpLVItem
->pszText
;
9048 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
9050 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
9052 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
9057 * Set item index that marks the start of a multiple selection.
9060 * [I] infoPtr : valid pointer to the listview structure
9061 * [I] nIndex : index
9064 * Index number or -1 if there is no selection mark.
9066 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
9068 INT nOldIndex
= infoPtr
->nSelectionMark
;
9070 TRACE("(nIndex=%d)\n", nIndex
);
9072 if (nIndex
>= -1 && nIndex
< infoPtr
->nItemCount
)
9073 infoPtr
->nSelectionMark
= nIndex
;
9080 * Sets the text background color.
9083 * [I] infoPtr : valid pointer to the listview structure
9084 * [I] color : text background color
9090 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF color
)
9092 TRACE("(color=%x)\n", color
);
9094 infoPtr
->clrTextBk
= color
;
9100 * Sets the text foreground color.
9103 * [I] infoPtr : valid pointer to the listview structure
9104 * [I] color : text color
9110 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF color
)
9112 TRACE("(color=%x)\n", color
);
9114 infoPtr
->clrText
= color
;
9120 * Sets new ToolTip window to ListView control.
9123 * [I] infoPtr : valid pointer to the listview structure
9124 * [I] hwndNewToolTip : handle to new ToolTip
9129 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
9131 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
9132 infoPtr
->hwndToolTip
= hwndNewToolTip
;
9133 return hwndOldToolTip
;
9138 * sets the Unicode character format flag for the control
9140 * [I] infoPtr :valid pointer to the listview structure
9141 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
9144 * Old Unicode Format
9146 static BOOL
LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO
*infoPtr
, BOOL unicode
)
9148 SHORT rc
= infoPtr
->notifyFormat
;
9149 infoPtr
->notifyFormat
= (unicode
) ? NFR_UNICODE
: NFR_ANSI
;
9150 return rc
== NFR_UNICODE
;
9155 * sets the control view mode
9157 * [I] infoPtr :valid pointer to the listview structure
9158 * [I] nView :new view mode value
9164 static INT
LISTVIEW_SetView(LISTVIEW_INFO
*infoPtr
, DWORD nView
)
9168 if (infoPtr
->uView
== nView
) return 1;
9170 if ((INT
)nView
< 0 || nView
> LV_VIEW_MAX
) return -1;
9171 if (nView
== LV_VIEW_TILE
)
9173 FIXME("View LV_VIEW_TILE unimplemented\n");
9177 infoPtr
->uView
= nView
;
9179 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9180 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
9182 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
9183 SetRectEmpty(&infoPtr
->rcFocus
);
9185 himl
= (nView
== LV_VIEW_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
9186 set_icon_size(&infoPtr
->iconSize
, himl
, nView
!= LV_VIEW_ICON
);
9191 case LV_VIEW_SMALLICON
:
9192 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9194 case LV_VIEW_DETAILS
:
9199 LISTVIEW_CreateHeader( infoPtr
);
9201 hl
.prc
= &infoPtr
->rcList
;
9203 SendMessageW(infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9204 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9205 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9212 LISTVIEW_UpdateItemSize(infoPtr
);
9213 LISTVIEW_UpdateSize(infoPtr
);
9214 LISTVIEW_UpdateScroll(infoPtr
);
9215 LISTVIEW_InvalidateList(infoPtr
);
9217 TRACE("nView=%d\n", nView
);
9222 /* LISTVIEW_SetWorkAreas */
9226 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
9229 * [I] first : pointer to first ITEM_INFO to compare
9230 * [I] second : pointer to second ITEM_INFO to compare
9231 * [I] lParam : HWND of control
9234 * if first comes before second : negative
9235 * if first comes after second : positive
9236 * if first and second are equivalent : zero
9238 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
9240 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
9241 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
9242 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
9244 /* Forward the call to the client defined callback */
9245 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
9250 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
9253 * [I] first : pointer to first ITEM_INFO to compare
9254 * [I] second : pointer to second ITEM_INFO to compare
9255 * [I] lParam : HWND of control
9258 * if first comes before second : negative
9259 * if first comes after second : positive
9260 * if first and second are equivalent : zero
9262 static INT WINAPI
LISTVIEW_CallBackCompareEx(LPVOID first
, LPVOID second
, LPARAM lParam
)
9264 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
9265 INT first_idx
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, first
);
9266 INT second_idx
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, second
);
9268 /* Forward the call to the client defined callback */
9269 return (infoPtr
->pfnCompare
)( first_idx
, second_idx
, infoPtr
->lParamSort
);
9274 * Sorts the listview items.
9277 * [I] infoPtr : valid pointer to the listview structure
9278 * [I] pfnCompare : application-defined value
9279 * [I] lParamSort : pointer to comparison callback
9280 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
9286 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
,
9287 LPARAM lParamSort
, BOOL IsEx
)
9291 LPVOID selectionMarkItem
= NULL
;
9292 LPVOID focusedItem
= NULL
;
9295 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
9297 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
9299 if (!pfnCompare
) return FALSE
;
9300 if (!infoPtr
->hdpaItems
) return FALSE
;
9302 /* if there are 0 or 1 items, there is no need to sort */
9303 if (infoPtr
->nItemCount
< 2) return TRUE
;
9305 /* clear selection */
9306 ranges_clear(infoPtr
->selectionRanges
);
9308 /* save selection mark and focused item */
9309 if (infoPtr
->nSelectionMark
>= 0)
9310 selectionMarkItem
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
);
9311 if (infoPtr
->nFocusedItem
>= 0)
9312 focusedItem
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
9314 infoPtr
->pfnCompare
= pfnCompare
;
9315 infoPtr
->lParamSort
= lParamSort
;
9317 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompareEx
, (LPARAM
)infoPtr
);
9319 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
9321 /* restore selection ranges */
9322 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
9324 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
9325 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
9327 if (lpItem
->state
& LVIS_SELECTED
)
9328 ranges_additem(infoPtr
->selectionRanges
, i
);
9330 /* restore selection mark and focused item */
9331 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
9332 infoPtr
->nFocusedItem
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, focusedItem
);
9334 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
9336 /* refresh the display */
9337 LISTVIEW_InvalidateList(infoPtr
);
9343 * Update theme handle after a theme change.
9346 * [I] infoPtr : valid pointer to the listview structure
9350 * FAILURE : something else
9352 static LRESULT
LISTVIEW_ThemeChanged(const LISTVIEW_INFO
*infoPtr
)
9354 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
9355 CloseThemeData(theme
);
9356 OpenThemeData(infoPtr
->hwndSelf
, themeClass
);
9362 * Updates an items or rearranges the listview control.
9365 * [I] infoPtr : valid pointer to the listview structure
9366 * [I] nItem : item index
9372 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
9374 TRACE("(nItem=%d)\n", nItem
);
9376 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
9378 /* rearrange with default alignment style */
9379 if (is_autoarrange(infoPtr
))
9380 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9382 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
9389 * Draw the track line at the place defined in the infoPtr structure.
9390 * The line is drawn with a XOR pen so drawing the line for the second time
9391 * in the same place erases the line.
9394 * [I] infoPtr : valid pointer to the listview structure
9400 static BOOL
LISTVIEW_DrawTrackLine(const LISTVIEW_INFO
*infoPtr
)
9404 if (infoPtr
->xTrackLine
== -1)
9407 if (!(hdc
= GetDC(infoPtr
->hwndSelf
)))
9409 PatBlt( hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.top
,
9410 1, infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
, DSTINVERT
);
9411 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
9417 * Called when an edit control should be displayed. This function is called after
9418 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
9421 * [I] hwnd : Handle to the listview
9422 * [I] uMsg : WM_TIMER (ignored)
9423 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
9424 * [I] dwTimer : The elapsed time (ignored)
9429 static VOID CALLBACK
LISTVIEW_DelayedEditItem(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
9431 DELAYED_ITEM_EDIT
*editItem
= (DELAYED_ITEM_EDIT
*)idEvent
;
9432 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9434 KillTimer(hwnd
, idEvent
);
9435 editItem
->fEnabled
= FALSE
;
9436 /* check if the item is still selected */
9437 if (infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, editItem
->iItem
, LVIS_SELECTED
))
9438 LISTVIEW_EditLabelT(infoPtr
, editItem
->iItem
, TRUE
);
9443 * Creates the listview control - the WM_NCCREATE phase.
9446 * [I] hwnd : window handle
9447 * [I] lpcs : the create parameters
9453 static LRESULT
LISTVIEW_NCCreate(HWND hwnd
, WPARAM wParam
, const CREATESTRUCTW
*lpcs
)
9455 LISTVIEW_INFO
*infoPtr
;
9458 TRACE("(lpcs=%p)\n", lpcs
);
9460 /* initialize info pointer */
9461 infoPtr
= Alloc(sizeof(LISTVIEW_INFO
));
9462 if (!infoPtr
) return FALSE
;
9464 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
9466 infoPtr
->hwndSelf
= hwnd
;
9467 infoPtr
->dwStyle
= lpcs
->style
; /* Note: may be changed in WM_CREATE */
9468 map_style_view(infoPtr
);
9469 /* determine the type of structures to use */
9470 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
9471 /* infoPtr->notifyFormat will be filled in WM_CREATE */
9473 /* initialize color information */
9474 infoPtr
->clrBk
= CLR_NONE
;
9475 infoPtr
->clrText
= CLR_DEFAULT
;
9476 infoPtr
->clrTextBk
= CLR_DEFAULT
;
9477 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
9479 /* set default values */
9480 infoPtr
->nFocusedItem
= -1;
9481 infoPtr
->nSelectionMark
= -1;
9482 infoPtr
->nHotItem
= -1;
9483 infoPtr
->redraw
= TRUE
;
9484 infoPtr
->bNoItemMetrics
= TRUE
;
9485 infoPtr
->bDoChangeNotify
= TRUE
;
9486 infoPtr
->autoSpacing
= TRUE
;
9487 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
) - GetSystemMetrics(SM_CXICON
);
9488 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
) - GetSystemMetrics(SM_CYICON
);
9489 infoPtr
->nEditLabelItem
= -1;
9490 infoPtr
->nLButtonDownItem
= -1;
9491 infoPtr
->dwHoverTime
= HOVER_DEFAULT
; /* default system hover time */
9492 infoPtr
->cWheelRemainder
= 0;
9493 infoPtr
->nMeasureItemHeight
= 0;
9494 infoPtr
->xTrackLine
= -1; /* no track line */
9495 infoPtr
->itemEdit
.fEnabled
= FALSE
;
9496 infoPtr
->iVersion
= COMCTL32_VERSION
;
9497 infoPtr
->colRectsDirty
= FALSE
;
9499 /* get default font (icon title) */
9500 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
9501 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
9502 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
9503 LISTVIEW_SaveTextMetrics(infoPtr
);
9505 /* allocate memory for the data structure */
9506 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
9507 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
9508 if (!(infoPtr
->hdpaItemIds
= DPA_Create(10))) goto fail
;
9509 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
9510 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
9511 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
9513 return DefWindowProcW(hwnd
, WM_NCCREATE
, wParam
, (LPARAM
)lpcs
);
9516 DestroyWindow(infoPtr
->hwndHeader
);
9517 ranges_destroy(infoPtr
->selectionRanges
);
9518 DPA_Destroy(infoPtr
->hdpaItems
);
9519 DPA_Destroy(infoPtr
->hdpaItemIds
);
9520 DPA_Destroy(infoPtr
->hdpaPosX
);
9521 DPA_Destroy(infoPtr
->hdpaPosY
);
9522 DPA_Destroy(infoPtr
->hdpaColumns
);
9529 * Creates the listview control - the WM_CREATE phase. Most of the data is
9530 * already set up in LISTVIEW_NCCreate
9533 * [I] hwnd : window handle
9534 * [I] lpcs : the create parameters
9540 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
9542 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9544 TRACE("(lpcs=%p, style=0x%08x)\n", lpcs
, lpcs
->style
);
9546 infoPtr
->dwStyle
= lpcs
->style
;
9547 map_style_view(infoPtr
);
9549 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
9550 (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
9551 /* on error defaulting to ANSI notifications */
9552 if (infoPtr
->notifyFormat
== 0) infoPtr
->notifyFormat
= NFR_ANSI
;
9553 TRACE("notify format=%d\n", infoPtr
->notifyFormat
);
9555 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && (lpcs
->style
& WS_VISIBLE
))
9557 if (LISTVIEW_CreateHeader(infoPtr
) < 0) return -1;
9560 infoPtr
->hwndHeader
= 0;
9562 /* init item size to avoid division by 0 */
9563 LISTVIEW_UpdateItemSize (infoPtr
);
9564 LISTVIEW_UpdateSize (infoPtr
);
9566 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
9568 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
) && (WS_VISIBLE
& lpcs
->style
))
9570 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
9572 LISTVIEW_UpdateScroll(infoPtr
);
9573 /* send WM_MEASUREITEM notification */
9574 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) notify_measureitem(infoPtr
);
9577 OpenThemeData(hwnd
, themeClass
);
9579 /* initialize the icon sizes */
9580 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, infoPtr
->uView
!= LV_VIEW_ICON
);
9581 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
9587 * Destroys the listview control.
9590 * [I] infoPtr : valid pointer to the listview structure
9596 static LRESULT
LISTVIEW_Destroy(LISTVIEW_INFO
*infoPtr
)
9598 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
9599 CloseThemeData(theme
);
9601 /* delete all items */
9602 LISTVIEW_DeleteAllItems(infoPtr
, TRUE
);
9609 * Enables the listview control.
9612 * [I] infoPtr : valid pointer to the listview structure
9613 * [I] bEnable : specifies whether to enable or disable the window
9619 static BOOL
LISTVIEW_Enable(const LISTVIEW_INFO
*infoPtr
)
9621 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
9622 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
9628 * Erases the background of the listview control.
9631 * [I] infoPtr : valid pointer to the listview structure
9632 * [I] hdc : device context handle
9638 static inline BOOL
LISTVIEW_EraseBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
9642 TRACE("(hdc=%p)\n", hdc
);
9644 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
9646 if (infoPtr
->clrBk
== CLR_NONE
)
9648 if (infoPtr
->dwLvExStyle
& LVS_EX_TRANSPARENTBKGND
)
9649 return SendMessageW(infoPtr
->hwndNotify
, WM_PRINTCLIENT
,
9650 (WPARAM
)hdc
, PRF_ERASEBKGND
);
9652 return SendMessageW(infoPtr
->hwndNotify
, WM_ERASEBKGND
, (WPARAM
)hdc
, 0);
9655 /* for double buffered controls we need to do this during refresh */
9656 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) return FALSE
;
9658 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
9664 * Helper function for LISTVIEW_[HV]Scroll *only*.
9665 * Performs vertical/horizontal scrolling by a give amount.
9668 * [I] infoPtr : valid pointer to the listview structure
9669 * [I] dx : amount of horizontal scroll
9670 * [I] dy : amount of vertical scroll
9672 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
9674 /* now we can scroll the list */
9675 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
9676 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
9677 /* if we have focus, adjust rect */
9678 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
9679 UpdateWindow(infoPtr
->hwndSelf
);
9684 * Performs vertical scrolling.
9687 * [I] infoPtr : valid pointer to the listview structure
9688 * [I] nScrollCode : scroll code
9689 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9690 * [I] hScrollWnd : scrollbar control window handle
9696 * SB_LINEUP/SB_LINEDOWN:
9697 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
9698 * for LVS_REPORT is 1 line
9699 * for LVS_LIST cannot occur
9702 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
9705 INT nOldScrollPos
, nNewScrollPos
;
9706 SCROLLINFO scrollInfo
;
9709 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
9710 debugscrollcode(nScrollCode
), nScrollDiff
);
9712 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9714 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
9715 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
9717 is_an_icon
= ((infoPtr
->uView
== LV_VIEW_ICON
) || (infoPtr
->uView
== LV_VIEW_SMALLICON
));
9719 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
9721 nOldScrollPos
= scrollInfo
.nPos
;
9722 switch (nScrollCode
)
9728 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
9732 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
9736 nScrollDiff
= -scrollInfo
.nPage
;
9740 nScrollDiff
= scrollInfo
.nPage
;
9743 case SB_THUMBPOSITION
:
9745 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
9752 /* quit right away if pos isn't changing */
9753 if (nScrollDiff
== 0) return 0;
9755 /* calculate new position, and handle overflows */
9756 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
9757 if (nScrollDiff
> 0) {
9758 if (nNewScrollPos
< nOldScrollPos
||
9759 nNewScrollPos
> scrollInfo
.nMax
)
9760 nNewScrollPos
= scrollInfo
.nMax
;
9762 if (nNewScrollPos
> nOldScrollPos
||
9763 nNewScrollPos
< scrollInfo
.nMin
)
9764 nNewScrollPos
= scrollInfo
.nMin
;
9767 /* set the new position, and reread in case it changed */
9768 scrollInfo
.fMask
= SIF_POS
;
9769 scrollInfo
.nPos
= nNewScrollPos
;
9770 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
9772 /* carry on only if it really changed */
9773 if (nNewScrollPos
== nOldScrollPos
) return 0;
9775 /* now adjust to client coordinates */
9776 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
9777 if (infoPtr
->uView
== LV_VIEW_DETAILS
) nScrollDiff
*= infoPtr
->nItemHeight
;
9779 /* and scroll the window */
9780 scroll_list(infoPtr
, 0, nScrollDiff
);
9787 * Performs horizontal scrolling.
9790 * [I] infoPtr : valid pointer to the listview structure
9791 * [I] nScrollCode : scroll code
9792 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9793 * [I] hScrollWnd : scrollbar control window handle
9799 * SB_LINELEFT/SB_LINERIGHT:
9800 * for LVS_ICON, LVS_SMALLICON 1 pixel
9801 * for LVS_REPORT is 1 pixel
9802 * for LVS_LIST is 1 column --> which is a 1 because the
9803 * scroll is based on columns not pixels
9806 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
9809 INT nOldScrollPos
, nNewScrollPos
;
9810 SCROLLINFO scrollInfo
;
9813 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
9814 debugscrollcode(nScrollCode
), nScrollDiff
);
9816 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9818 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
9819 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
9821 is_an_icon
= ((infoPtr
->uView
== LV_VIEW_ICON
) || (infoPtr
->uView
== LV_VIEW_SMALLICON
));
9823 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
9825 nOldScrollPos
= scrollInfo
.nPos
;
9827 switch (nScrollCode
)
9833 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
9837 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
9841 nScrollDiff
= -scrollInfo
.nPage
;
9845 nScrollDiff
= scrollInfo
.nPage
;
9848 case SB_THUMBPOSITION
:
9850 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
9857 /* quit right away if pos isn't changing */
9858 if (nScrollDiff
== 0) return 0;
9860 /* calculate new position, and handle overflows */
9861 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
9862 if (nScrollDiff
> 0) {
9863 if (nNewScrollPos
< nOldScrollPos
||
9864 nNewScrollPos
> scrollInfo
.nMax
)
9865 nNewScrollPos
= scrollInfo
.nMax
;
9867 if (nNewScrollPos
> nOldScrollPos
||
9868 nNewScrollPos
< scrollInfo
.nMin
)
9869 nNewScrollPos
= scrollInfo
.nMin
;
9872 /* set the new position, and reread in case it changed */
9873 scrollInfo
.fMask
= SIF_POS
;
9874 scrollInfo
.nPos
= nNewScrollPos
;
9875 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
9877 /* carry on only if it really changed */
9878 if (nNewScrollPos
== nOldScrollPos
) return 0;
9880 if (infoPtr
->hwndHeader
) LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
9882 /* now adjust to client coordinates */
9883 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
9884 if (infoPtr
->uView
== LV_VIEW_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
9886 /* and scroll the window */
9887 scroll_list(infoPtr
, nScrollDiff
, 0);
9892 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
9894 UINT pulScrollLines
= 3;
9896 TRACE("(wheelDelta=%d)\n", wheelDelta
);
9898 switch(infoPtr
->uView
)
9901 case LV_VIEW_SMALLICON
:
9903 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
9904 * should be fixed in the future.
9906 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (wheelDelta
> 0) ?
9907 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
);
9910 case LV_VIEW_DETAILS
:
9911 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
9913 /* if scrolling changes direction, ignore left overs */
9914 if ((wheelDelta
< 0 && infoPtr
->cWheelRemainder
< 0) ||
9915 (wheelDelta
> 0 && infoPtr
->cWheelRemainder
> 0))
9916 infoPtr
->cWheelRemainder
+= wheelDelta
;
9918 infoPtr
->cWheelRemainder
= wheelDelta
;
9919 if (infoPtr
->cWheelRemainder
&& pulScrollLines
)
9922 pulScrollLines
= min((UINT
)LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
9923 cLineScroll
= pulScrollLines
* (float)infoPtr
->cWheelRemainder
/ WHEEL_DELTA
;
9924 infoPtr
->cWheelRemainder
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
9925 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, -cLineScroll
);
9930 LISTVIEW_HScroll(infoPtr
, (wheelDelta
> 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0);
9941 * [I] infoPtr : valid pointer to the listview structure
9942 * [I] nVirtualKey : virtual key
9943 * [I] lKeyData : key data
9948 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
9950 HWND hwndSelf
= infoPtr
->hwndSelf
;
9952 NMLVKEYDOWN nmKeyDown
;
9954 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey
, lKeyData
);
9956 /* send LVN_KEYDOWN notification */
9957 nmKeyDown
.wVKey
= nVirtualKey
;
9958 nmKeyDown
.flags
= 0;
9959 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
9960 if (!IsWindow(hwndSelf
))
9963 switch (nVirtualKey
)
9966 nItem
= infoPtr
->nFocusedItem
;
9967 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
9968 toggle_checkbox_state(infoPtr
, infoPtr
->nFocusedItem
);
9972 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
9974 if (!notify(infoPtr
, NM_RETURN
)) return 0;
9975 if (!notify(infoPtr
, LVN_ITEMACTIVATE
)) return 0;
9980 if (infoPtr
->nItemCount
> 0)
9985 if (infoPtr
->nItemCount
> 0)
9986 nItem
= infoPtr
->nItemCount
- 1;
9990 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
9994 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
9998 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
10002 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
10006 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
10008 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
10009 if (infoPtr
->nFocusedItem
== topidx
)
10010 nItem
= topidx
- LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
10015 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
10016 * LISTVIEW_GetCountPerRow(infoPtr
);
10017 if(nItem
< 0) nItem
= 0;
10021 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
10023 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
10024 INT cnt
= LISTVIEW_GetCountPerColumn(infoPtr
);
10025 if (infoPtr
->nFocusedItem
== topidx
+ cnt
- 1)
10026 nItem
= infoPtr
->nFocusedItem
+ cnt
- 1;
10028 nItem
= topidx
+ cnt
- 1;
10031 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
10032 * LISTVIEW_GetCountPerRow(infoPtr
);
10033 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
10037 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
|| nVirtualKey
== VK_SPACE
))
10038 LISTVIEW_KeySelection(infoPtr
, nItem
, nVirtualKey
== VK_SPACE
);
10048 * [I] infoPtr : valid pointer to the listview structure
10053 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
10057 /* drop any left over scroll amount */
10058 infoPtr
->cWheelRemainder
= 0;
10060 /* if we did not have the focus, there's nothing more to do */
10061 if (!infoPtr
->bFocus
) return 0;
10063 /* send NM_KILLFOCUS notification */
10064 if (!notify(infoPtr
, NM_KILLFOCUS
)) return 0;
10066 /* if we have a focus rectangle, get rid of it */
10067 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
10069 /* if have a marquee selection, stop it */
10070 if (infoPtr
->bMarqueeSelect
)
10072 /* Remove the marquee rectangle and release our mouse capture */
10073 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeRect
);
10076 SetRectEmpty(&infoPtr
->marqueeRect
);
10078 infoPtr
->bMarqueeSelect
= FALSE
;
10079 infoPtr
->bScrolling
= FALSE
;
10080 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
);
10083 /* set window focus flag */
10084 infoPtr
->bFocus
= FALSE
;
10086 /* invalidate the selected items before resetting focus flag */
10087 LISTVIEW_InvalidateSelectedItems(infoPtr
);
10094 * Processes double click messages (left mouse button).
10097 * [I] infoPtr : valid pointer to the listview structure
10098 * [I] wKey : key flag
10099 * [I] x,y : mouse coordinate
10104 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10106 LVHITTESTINFO htInfo
;
10108 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey
, x
, y
);
10110 /* Cancel the item edition if any */
10111 if (infoPtr
->itemEdit
.fEnabled
)
10113 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
)&infoPtr
->itemEdit
);
10114 infoPtr
->itemEdit
.fEnabled
= FALSE
;
10117 /* send NM_RELEASEDCAPTURE notification */
10118 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10123 /* send NM_DBLCLK notification */
10124 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
10125 if (!notify_click(infoPtr
, NM_DBLCLK
, &htInfo
)) return 0;
10127 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
10128 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
10133 static LRESULT
LISTVIEW_TrackMouse(const LISTVIEW_INFO
*infoPtr
, POINT pt
)
10138 r
.top
= r
.bottom
= pt
.y
;
10139 r
.left
= r
.right
= pt
.x
;
10141 InflateRect(&r
, GetSystemMetrics(SM_CXDRAG
), GetSystemMetrics(SM_CYDRAG
));
10143 SetCapture(infoPtr
->hwndSelf
);
10147 if (PeekMessageW(&msg
, 0, 0, 0, PM_REMOVE
| PM_NOYIELD
))
10149 if (msg
.message
== WM_MOUSEMOVE
)
10151 pt
.x
= (short)LOWORD(msg
.lParam
);
10152 pt
.y
= (short)HIWORD(msg
.lParam
);
10153 if (PtInRect(&r
, pt
))
10161 else if (msg
.message
>= WM_LBUTTONDOWN
&&
10162 msg
.message
<= WM_RBUTTONDBLCLK
)
10167 DispatchMessageW(&msg
);
10170 if (GetCapture() != infoPtr
->hwndSelf
)
10181 * Processes mouse down messages (left mouse button).
10184 * infoPtr [I ] valid pointer to the listview structure
10185 * wKey [I ] key flag
10186 * x,y [I ] mouse coordinate
10191 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10193 LVHITTESTINFO lvHitTestInfo
;
10194 static BOOL bGroupSelect
= TRUE
;
10195 POINT pt
= { x
, y
};
10198 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey
, x
, y
);
10200 /* send NM_RELEASEDCAPTURE notification */
10201 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10203 /* set left button down flag and record the click position */
10204 infoPtr
->bLButtonDown
= TRUE
;
10205 infoPtr
->ptClickPos
= pt
;
10206 infoPtr
->bDragging
= FALSE
;
10207 infoPtr
->bMarqueeSelect
= FALSE
;
10208 infoPtr
->bScrolling
= FALSE
;
10210 lvHitTestInfo
.pt
.x
= x
;
10211 lvHitTestInfo
.pt
.y
= y
;
10213 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
10214 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt
), nItem
);
10215 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
10217 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
10219 toggle_checkbox_state(infoPtr
, nItem
);
10223 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
10225 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
10226 infoPtr
->nEditLabelItem
= nItem
;
10228 LISTVIEW_SetSelection(infoPtr
, nItem
);
10232 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
10236 if (!LISTVIEW_AddGroupSelection(infoPtr
, nItem
)) return 0;
10237 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
10238 infoPtr
->nSelectionMark
= nItem
;
10244 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
10245 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
10247 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
10248 infoPtr
->nSelectionMark
= nItem
;
10251 else if (wKey
& MK_CONTROL
)
10255 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
10257 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
10258 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
10259 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
10260 infoPtr
->nSelectionMark
= nItem
;
10262 else if (wKey
& MK_SHIFT
)
10264 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
10268 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
10270 infoPtr
->nEditLabelItem
= nItem
;
10271 infoPtr
->nLButtonDownItem
= nItem
;
10273 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
10276 /* set selection (clears other pre-existing selections) */
10277 LISTVIEW_SetSelection(infoPtr
, nItem
);
10281 if (!infoPtr
->bFocus
)
10282 SetFocus(infoPtr
->hwndSelf
);
10284 if (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
)
10285 if(lvHitTestInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&lvHitTestInfo
);
10289 if (!infoPtr
->bFocus
)
10290 SetFocus(infoPtr
->hwndSelf
);
10292 /* remove all selections */
10293 if (!(wKey
& MK_CONTROL
) && !(wKey
& MK_SHIFT
))
10294 LISTVIEW_DeselectAll(infoPtr
);
10303 * Processes mouse up messages (left mouse button).
10306 * infoPtr [I ] valid pointer to the listview structure
10307 * wKey [I ] key flag
10308 * x,y [I ] mouse coordinate
10313 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10315 LVHITTESTINFO lvHitTestInfo
;
10317 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey
, x
, y
);
10319 if (!infoPtr
->bLButtonDown
) return 0;
10321 lvHitTestInfo
.pt
.x
= x
;
10322 lvHitTestInfo
.pt
.y
= y
;
10324 /* send NM_CLICK notification */
10325 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
10326 if (!notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
)) return 0;
10328 /* set left button flag */
10329 infoPtr
->bLButtonDown
= FALSE
;
10331 /* set a single selection, reset others */
10332 if(lvHitTestInfo
.iItem
== infoPtr
->nLButtonDownItem
&& lvHitTestInfo
.iItem
!= -1)
10333 LISTVIEW_SetSelection(infoPtr
, infoPtr
->nLButtonDownItem
);
10334 infoPtr
->nLButtonDownItem
= -1;
10336 if (infoPtr
->bDragging
|| infoPtr
->bMarqueeSelect
)
10338 /* Remove the marquee rectangle and release our mouse capture */
10339 if (infoPtr
->bMarqueeSelect
)
10341 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeDrawRect
);
10345 SetRectEmpty(&infoPtr
->marqueeRect
);
10346 SetRectEmpty(&infoPtr
->marqueeDrawRect
);
10348 infoPtr
->bDragging
= FALSE
;
10349 infoPtr
->bMarqueeSelect
= FALSE
;
10350 infoPtr
->bScrolling
= FALSE
;
10352 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
);
10356 /* if we clicked on a selected item, edit the label */
10357 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
10359 /* we want to make sure the user doesn't want to do a double click. So we will
10360 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
10362 infoPtr
->itemEdit
.fEnabled
= TRUE
;
10363 infoPtr
->itemEdit
.iItem
= lvHitTestInfo
.iItem
;
10364 SetTimer(infoPtr
->hwndSelf
,
10365 (UINT_PTR
)&infoPtr
->itemEdit
,
10366 GetDoubleClickTime(),
10367 LISTVIEW_DelayedEditItem
);
10375 * Destroys the listview control (called after WM_DESTROY).
10378 * [I] infoPtr : valid pointer to the listview structure
10383 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
10389 /* destroy data structure */
10390 DPA_Destroy(infoPtr
->hdpaItems
);
10391 DPA_Destroy(infoPtr
->hdpaItemIds
);
10392 DPA_Destroy(infoPtr
->hdpaPosX
);
10393 DPA_Destroy(infoPtr
->hdpaPosY
);
10395 for (i
= 0; i
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); i
++)
10396 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, i
));
10397 DPA_Destroy(infoPtr
->hdpaColumns
);
10398 ranges_destroy(infoPtr
->selectionRanges
);
10400 /* destroy image lists */
10401 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
10403 ImageList_Destroy(infoPtr
->himlNormal
);
10404 ImageList_Destroy(infoPtr
->himlSmall
);
10405 ImageList_Destroy(infoPtr
->himlState
);
10408 /* destroy font, bkgnd brush */
10409 infoPtr
->hFont
= 0;
10410 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
10411 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
10413 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
10415 /* free listview info pointer*/
10423 * Handles notifications.
10426 * [I] infoPtr : valid pointer to the listview structure
10427 * [I] lpnmhdr : notification information
10432 static LRESULT
LISTVIEW_Notify(LISTVIEW_INFO
*infoPtr
, NMHDR
*lpnmhdr
)
10436 TRACE("(lpnmhdr=%p)\n", lpnmhdr
);
10438 if (!lpnmhdr
|| lpnmhdr
->hwndFrom
!= infoPtr
->hwndHeader
) return 0;
10440 /* remember: HDN_LAST < HDN_FIRST */
10441 if (lpnmhdr
->code
> HDN_FIRST
|| lpnmhdr
->code
< HDN_LAST
) return 0;
10442 lpnmh
= (NMHEADERW
*)lpnmhdr
;
10444 if (lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
10446 switch (lpnmhdr
->code
)
10451 COLUMN_INFO
*lpColumnInfo
;
10455 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
10458 /* remove the old line (if any) */
10459 LISTVIEW_DrawTrackLine(infoPtr
);
10461 /* compute & draw the new line */
10462 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
10463 x
= lpColumnInfo
->rcHeader
.left
+ lpnmh
->pitem
->cxy
;
10464 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
10465 infoPtr
->xTrackLine
= x
+ ptOrigin
.x
;
10466 LISTVIEW_DrawTrackLine(infoPtr
);
10467 return notify_forward_header(infoPtr
, lpnmh
);
10470 case HDN_ENDTRACKA
:
10471 case HDN_ENDTRACKW
:
10472 /* remove the track line (if any) */
10473 LISTVIEW_DrawTrackLine(infoPtr
);
10474 infoPtr
->xTrackLine
= -1;
10475 return notify_forward_header(infoPtr
, lpnmh
);
10477 case HDN_BEGINDRAG
:
10478 if ((infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
) == 0) return 1;
10479 return notify_forward_header(infoPtr
, lpnmh
);
10482 infoPtr
->colRectsDirty
= TRUE
;
10483 LISTVIEW_InvalidateList(infoPtr
);
10484 return notify_forward_header(infoPtr
, lpnmh
);
10486 case HDN_ITEMCHANGEDW
:
10487 case HDN_ITEMCHANGEDA
:
10489 COLUMN_INFO
*lpColumnInfo
;
10493 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
10495 hdi
.mask
= HDI_WIDTH
;
10496 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, lpnmh
->iItem
, (LPARAM
)&hdi
)) return 0;
10500 cxy
= lpnmh
->pitem
->cxy
;
10502 /* determine how much we change since the last know position */
10503 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
10504 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
10507 lpColumnInfo
->rcHeader
.right
+= dx
;
10509 hdi
.mask
= HDI_ORDER
;
10510 SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, lpnmh
->iItem
, (LPARAM
)&hdi
);
10512 /* not the rightmost one */
10513 if (hdi
.iOrder
+ 1 < DPA_GetPtrCount(infoPtr
->hdpaColumns
))
10515 INT nIndex
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
10516 hdi
.iOrder
+ 1, 0);
10517 LISTVIEW_ScrollColumns(infoPtr
, nIndex
, dx
);
10521 /* only needs to update the scrolls */
10522 infoPtr
->nItemWidth
+= dx
;
10523 LISTVIEW_UpdateScroll(infoPtr
);
10525 LISTVIEW_UpdateItemSize(infoPtr
);
10526 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& is_redrawing(infoPtr
))
10529 RECT rcCol
= lpColumnInfo
->rcHeader
;
10531 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
10532 OffsetRect(&rcCol
, ptOrigin
.x
, 0);
10534 rcCol
.top
= infoPtr
->rcList
.top
;
10535 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
10537 /* resizing left-aligned columns leaves most of the left side untouched */
10538 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
10540 INT nMaxDirty
= infoPtr
->nEllipsisWidth
+ infoPtr
->ntmMaxCharWidth
;
10543 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- nMaxDirty
);
10546 /* when shrinking the last column clear the now unused field */
10547 if (hdi
.iOrder
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
10553 /* deal with right from rightmost column area */
10554 right
.left
= rcCol
.right
;
10555 right
.top
= rcCol
.top
;
10556 right
.bottom
= rcCol
.bottom
;
10557 right
.right
= infoPtr
->rcList
.right
;
10559 LISTVIEW_InvalidateRect(infoPtr
, &right
);
10562 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
10568 case HDN_ITEMCLICKW
:
10569 case HDN_ITEMCLICKA
:
10571 /* Handle sorting by Header Column */
10574 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
10576 nmlv
.iSubItem
= lpnmh
->iItem
;
10577 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
10578 return notify_forward_header(infoPtr
, lpnmh
);
10581 case HDN_DIVIDERDBLCLICKW
:
10582 case HDN_DIVIDERDBLCLICKA
:
10583 /* FIXME: for LVS_EX_HEADERINALLVIEWS and not LV_VIEW_DETAILS
10584 we should use LVSCW_AUTOSIZE_USEHEADER, helper rework or
10585 split needed for that */
10586 LISTVIEW_SetColumnWidth(infoPtr
, lpnmh
->iItem
, LVSCW_AUTOSIZE
);
10587 return notify_forward_header(infoPtr
, lpnmh
);
10594 * Paint non-client area of control.
10597 * [I] infoPtr : valid pointer to the listview structureof the sender
10598 * [I] region : update region
10601 * TRUE - frame was painted
10602 * FALSE - call default window proc
10604 static BOOL
LISTVIEW_NCPaint(const LISTVIEW_INFO
*infoPtr
, HRGN region
)
10606 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
10610 int cxEdge
= GetSystemMetrics (SM_CXEDGE
),
10611 cyEdge
= GetSystemMetrics (SM_CYEDGE
);
10614 return DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)region
, 0);
10616 GetWindowRect(infoPtr
->hwndSelf
, &r
);
10618 cliprgn
= CreateRectRgn (r
.left
+ cxEdge
, r
.top
+ cyEdge
,
10619 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
10620 if (region
!= (HRGN
)1)
10621 CombineRgn (cliprgn
, cliprgn
, region
, RGN_AND
);
10622 OffsetRect(&r
, -r
.left
, -r
.top
);
10624 dc
= GetDCEx(infoPtr
->hwndSelf
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
10625 OffsetRect(&r
, -r
.left
, -r
.top
);
10627 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
10628 DrawThemeParentBackground(infoPtr
->hwndSelf
, dc
, &r
);
10629 DrawThemeBackground (theme
, dc
, 0, 0, &r
, 0);
10630 ReleaseDC(infoPtr
->hwndSelf
, dc
);
10632 /* Call default proc to get the scrollbars etc. painted */
10633 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)cliprgn
, 0);
10640 * Determines the type of structure to use.
10643 * [I] infoPtr : valid pointer to the listview structureof the sender
10644 * [I] hwndFrom : listview window handle
10645 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
10650 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
10652 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
10654 if (nCommand
== NF_REQUERY
)
10655 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
10657 return infoPtr
->notifyFormat
;
10662 * Paints/Repaints the listview control. Internal use.
10665 * [I] infoPtr : valid pointer to the listview structure
10666 * [I] hdc : device context handle
10671 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
10673 TRACE("(hdc=%p)\n", hdc
);
10675 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
10677 infoPtr
->bNoItemMetrics
= FALSE
;
10678 LISTVIEW_UpdateItemSize(infoPtr
);
10679 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)
10680 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
10681 LISTVIEW_UpdateScroll(infoPtr
);
10684 if (infoPtr
->hwndHeader
) UpdateWindow(infoPtr
->hwndHeader
);
10687 LISTVIEW_Refresh(infoPtr
, hdc
, NULL
);
10692 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
10693 if (!hdc
) return 1;
10694 LISTVIEW_Refresh(infoPtr
, hdc
, ps
.fErase
? &ps
.rcPaint
: NULL
);
10695 EndPaint(infoPtr
->hwndSelf
, &ps
);
10703 * Paints/Repaints the listview control, WM_PAINT handler.
10706 * [I] infoPtr : valid pointer to the listview structure
10707 * [I] hdc : device context handle
10712 static inline LRESULT
LISTVIEW_WMPaint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
10714 TRACE("(hdc=%p)\n", hdc
);
10716 if (!is_redrawing(infoPtr
))
10717 return DefWindowProcW (infoPtr
->hwndSelf
, WM_PAINT
, (WPARAM
)hdc
, 0);
10719 return LISTVIEW_Paint(infoPtr
, hdc
);
10724 * Paints/Repaints the listview control.
10727 * [I] infoPtr : valid pointer to the listview structure
10728 * [I] hdc : device context handle
10729 * [I] options : drawing options
10734 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
10736 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
10739 if (options
& ~(PRF_ERASEBKGND
|PRF_CLIENT
))
10740 FIXME("(hdc=%p options=0x%08x) partial stub\n", hdc
, options
);
10742 if (options
& PRF_ERASEBKGND
)
10743 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
10745 if (options
& PRF_CLIENT
)
10746 LISTVIEW_Paint(infoPtr
, hdc
);
10754 * Processes double click messages (right mouse button).
10757 * [I] infoPtr : valid pointer to the listview structure
10758 * [I] wKey : key flag
10759 * [I] x,y : mouse coordinate
10764 static LRESULT
LISTVIEW_RButtonDblClk(const LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10766 LVHITTESTINFO lvHitTestInfo
;
10768 TRACE("(key=%hu,X=%u,Y=%u)\n", wKey
, x
, y
);
10770 /* send NM_RELEASEDCAPTURE notification */
10771 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10773 /* send NM_RDBLCLK notification */
10774 lvHitTestInfo
.pt
.x
= x
;
10775 lvHitTestInfo
.pt
.y
= y
;
10776 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
10777 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
10784 * Processes WM_RBUTTONDOWN message and corresponding drag operation.
10787 * [I] infoPtr : valid pointer to the listview structure
10788 * [I] wKey : key flag
10789 * [I] x, y : mouse coordinate
10794 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10799 TRACE("(key=%hu, x=%d, y=%d)\n", wKey
, x
, y
);
10801 /* send NM_RELEASEDCAPTURE notification */
10802 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10804 /* determine the index of the selected item */
10807 item
= LISTVIEW_HitTest(infoPtr
, &ht
, TRUE
, TRUE
);
10809 /* make sure the listview control window has the focus */
10810 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
10812 if ((item
>= 0) && (item
< infoPtr
->nItemCount
))
10814 LISTVIEW_SetItemFocus(infoPtr
, item
);
10815 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
10816 !LISTVIEW_GetItemState(infoPtr
, item
, LVIS_SELECTED
))
10817 LISTVIEW_SetSelection(infoPtr
, item
);
10820 LISTVIEW_DeselectAll(infoPtr
);
10822 if (LISTVIEW_TrackMouse(infoPtr
, ht
.pt
))
10824 if (ht
.iItem
!= -1)
10828 memset(&nmlv
, 0, sizeof(nmlv
));
10829 nmlv
.iItem
= ht
.iItem
;
10830 nmlv
.ptAction
= ht
.pt
;
10832 notify_listview(infoPtr
, LVN_BEGINRDRAG
, &nmlv
);
10837 SetFocus(infoPtr
->hwndSelf
);
10841 LISTVIEW_HitTest(infoPtr
, &ht
, TRUE
, FALSE
);
10843 if (notify_click(infoPtr
, NM_RCLICK
, &ht
))
10845 /* Send a WM_CONTEXTMENU message in response to the WM_RBUTTONUP */
10846 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
10847 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)GetMessagePos());
10859 * [I] infoPtr : valid pointer to the listview structure
10860 * [I] hwnd : window handle of window containing the cursor
10861 * [I] nHittest : hit-test code
10862 * [I] wMouseMsg : ideintifier of the mouse message
10865 * TRUE if cursor is set
10868 static BOOL
LISTVIEW_SetCursor(const LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
10870 LVHITTESTINFO lvHitTestInfo
;
10872 if (!LISTVIEW_IsHotTracking(infoPtr
)) goto forward
;
10874 if (!infoPtr
->hHotCursor
) goto forward
;
10876 GetCursorPos(&lvHitTestInfo
.pt
);
10877 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) goto forward
;
10879 SetCursor(infoPtr
->hHotCursor
);
10885 return DefWindowProcW(infoPtr
->hwndSelf
, WM_SETCURSOR
, wParam
, lParam
);
10893 * [I] infoPtr : valid pointer to the listview structure
10894 * [I] hwndLoseFocus : handle of previously focused window
10899 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
10901 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
10903 /* if we have the focus already, there's nothing to do */
10904 if (infoPtr
->bFocus
) return 0;
10906 /* send NM_SETFOCUS notification */
10907 if (!notify(infoPtr
, NM_SETFOCUS
)) return 0;
10909 /* set window focus flag */
10910 infoPtr
->bFocus
= TRUE
;
10912 /* put the focus rect back on */
10913 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
10915 /* redraw all visible selected items */
10916 LISTVIEW_InvalidateSelectedItems(infoPtr
);
10926 * [I] infoPtr : valid pointer to the listview structure
10927 * [I] fRedraw : font handle
10928 * [I] fRedraw : redraw flag
10933 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
10935 HFONT oldFont
= infoPtr
->hFont
;
10936 INT oldHeight
= infoPtr
->nItemHeight
;
10938 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
10940 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
10941 if (infoPtr
->hFont
== oldFont
) return 0;
10943 LISTVIEW_SaveTextMetrics(infoPtr
);
10945 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
10947 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
10949 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
10950 LISTVIEW_UpdateSize(infoPtr
);
10951 LISTVIEW_UpdateScroll(infoPtr
);
10953 else if (infoPtr
->nItemHeight
!= oldHeight
)
10954 LISTVIEW_UpdateScroll(infoPtr
);
10956 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
10963 * Message handling for WM_SETREDRAW.
10964 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
10967 * [I] infoPtr : valid pointer to the listview structure
10968 * [I] redraw: state of redraw flag
10973 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL redraw
)
10975 TRACE("old=%d, new=%d\n", infoPtr
->redraw
, redraw
);
10977 if (infoPtr
->redraw
== !!redraw
)
10980 if (!(infoPtr
->redraw
= !!redraw
))
10983 if (is_autoarrange(infoPtr
))
10984 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
10985 LISTVIEW_UpdateScroll(infoPtr
);
10987 /* despite what the WM_SETREDRAW docs says, apps expect us
10988 * to invalidate the listview here... stupid! */
10989 LISTVIEW_InvalidateList(infoPtr
);
10996 * Resizes the listview control. This function processes WM_SIZE
10997 * messages. At this time, the width and height are not used.
11000 * [I] infoPtr : valid pointer to the listview structure
11001 * [I] Width : new width
11002 * [I] Height : new height
11007 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
11009 RECT rcOld
= infoPtr
->rcList
;
11011 TRACE("(width=%d, height=%d)\n", Width
, Height
);
11013 LISTVIEW_UpdateSize(infoPtr
);
11014 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
11016 /* do not bother with display related stuff if we're not redrawing */
11017 if (!is_redrawing(infoPtr
)) return 0;
11019 if (is_autoarrange(infoPtr
))
11020 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
11022 LISTVIEW_UpdateScroll(infoPtr
);
11024 /* refresh all only for lists whose height changed significantly */
11025 if ((infoPtr
->uView
== LV_VIEW_LIST
) &&
11026 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
11027 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
11028 LISTVIEW_InvalidateList(infoPtr
);
11035 * Sets the size information.
11038 * [I] infoPtr : valid pointer to the listview structure
11043 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
11045 TRACE("uView=%d, rcList(old)=%s\n", infoPtr
->uView
, wine_dbgstr_rect(&infoPtr
->rcList
));
11047 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
11049 if (infoPtr
->uView
== LV_VIEW_LIST
)
11051 /* Apparently the "LIST" style is supposed to have the same
11052 * number of items in a column even if there is no scroll bar.
11053 * Since if a scroll bar already exists then the bottom is already
11054 * reduced, only reduce if the scroll bar does not currently exist.
11055 * The "2" is there to mimic the native control. I think it may be
11056 * related to either padding or edges. (GLA 7/2002)
11058 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_HSCROLL
))
11059 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
11060 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
11063 /* if control created invisible header isn't created */
11064 if (infoPtr
->hwndHeader
)
11069 hl
.prc
= &infoPtr
->rcList
;
11071 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
11072 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp
.flags
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
11074 if (LISTVIEW_IsHeaderEnabled(infoPtr
))
11075 wp
.flags
|= SWP_SHOWWINDOW
;
11078 wp
.flags
|= SWP_HIDEWINDOW
;
11082 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
11083 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
11085 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
11087 /* extra padding for grid */
11088 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
11089 infoPtr
->rcList
.top
+= 2;
11091 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr
->rcList
));
11096 * Processes WM_STYLECHANGED messages.
11099 * [I] infoPtr : valid pointer to the listview structure
11100 * [I] wStyleType : window style type (normal or extended)
11101 * [I] lpss : window style information
11106 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
11107 const STYLESTRUCT
*lpss
)
11109 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
11110 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
11113 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
11114 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
11116 if (wStyleType
!= GWL_STYLE
) return 0;
11118 infoPtr
->dwStyle
= lpss
->styleNew
;
11120 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
11121 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
11122 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
11124 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
11125 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
11126 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
11128 if (uNewView
!= uOldView
)
11132 /* LVM_SETVIEW doesn't change window style bits within LVS_TYPEMASK,
11133 changing style updates current view only when view bits change. */
11134 map_style_view(infoPtr
);
11135 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
11136 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
11138 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
11139 SetRectEmpty(&infoPtr
->rcFocus
);
11141 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
11142 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
11144 if (uNewView
== LVS_REPORT
)
11149 LISTVIEW_CreateHeader( infoPtr
);
11151 hl
.prc
= &infoPtr
->rcList
;
11153 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
11154 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
11155 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
11156 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
11159 LISTVIEW_UpdateItemSize(infoPtr
);
11162 if (uNewView
== LVS_REPORT
|| infoPtr
->dwLvExStyle
& LVS_EX_HEADERINALLVIEWS
)
11164 if ((lpss
->styleOld
^ lpss
->styleNew
) & LVS_NOCOLUMNHEADER
)
11166 if (lpss
->styleNew
& LVS_NOCOLUMNHEADER
)
11168 /* Turn off the header control */
11169 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
11170 TRACE("Hide header control, was 0x%08x\n", style
);
11171 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
| HDS_HIDDEN
);
11173 /* Turn on the header control */
11174 if ((style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
)) & HDS_HIDDEN
)
11176 TRACE("Show header control, was 0x%08x\n", style
);
11177 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, (style
& ~HDS_HIDDEN
) | WS_VISIBLE
);
11183 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
11184 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
11185 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
11187 /* update the size of the client area */
11188 LISTVIEW_UpdateSize(infoPtr
);
11190 /* add scrollbars if needed */
11191 LISTVIEW_UpdateScroll(infoPtr
);
11193 /* invalidate client area + erase background */
11194 LISTVIEW_InvalidateList(infoPtr
);
11201 * Processes WM_STYLECHANGING messages.
11204 * [I] wStyleType : window style type (normal or extended)
11205 * [I0] lpss : window style information
11210 static INT
LISTVIEW_StyleChanging(WPARAM wStyleType
,
11213 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
11214 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
11216 /* don't forward LVS_OWNERDATA only if not already set to */
11217 if ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_OWNERDATA
)
11219 if (lpss
->styleOld
& LVS_OWNERDATA
)
11220 lpss
->styleNew
|= LVS_OWNERDATA
;
11222 lpss
->styleNew
&= ~LVS_OWNERDATA
;
11230 * Processes WM_SHOWWINDOW messages.
11233 * [I] infoPtr : valid pointer to the listview structure
11234 * [I] bShown : window is being shown (FALSE when hidden)
11235 * [I] iStatus : window show status
11240 static LRESULT
LISTVIEW_ShowWindow(LISTVIEW_INFO
*infoPtr
, WPARAM bShown
, LPARAM iStatus
)
11242 /* header delayed creation */
11243 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && bShown
)
11245 LISTVIEW_CreateHeader(infoPtr
);
11247 if (!(LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
))
11248 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
11251 return DefWindowProcW(infoPtr
->hwndSelf
, WM_SHOWWINDOW
, bShown
, iStatus
);
11256 * Processes CCM_GETVERSION messages.
11259 * [I] infoPtr : valid pointer to the listview structure
11264 static inline LRESULT
LISTVIEW_GetVersion(const LISTVIEW_INFO
*infoPtr
)
11266 return infoPtr
->iVersion
;
11271 * Processes CCM_SETVERSION messages.
11274 * [I] infoPtr : valid pointer to the listview structure
11275 * [I] iVersion : version to be set
11278 * -1 when requested version is greater than DLL version;
11279 * previous version otherwise
11281 static LRESULT
LISTVIEW_SetVersion(LISTVIEW_INFO
*infoPtr
, DWORD iVersion
)
11283 INT iOldVersion
= infoPtr
->iVersion
;
11285 if (iVersion
> COMCTL32_VERSION
)
11288 infoPtr
->iVersion
= iVersion
;
11290 TRACE("new version %d\n", iVersion
);
11292 return iOldVersion
;
11297 * Window procedure of the listview control.
11300 static LRESULT WINAPI
11301 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
11303 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
11305 TRACE("(hwnd=%p uMsg=%x wParam=%lx lParam=%lx)\n", hwnd
, uMsg
, wParam
, lParam
);
11307 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
11308 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11312 case LVM_APPROXIMATEVIEWRECT
:
11313 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
11314 LOWORD(lParam
), HIWORD(lParam
));
11316 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
11318 case LVM_CANCELEDITLABEL
:
11319 return LISTVIEW_CancelEditLabel(infoPtr
);
11321 case LVM_CREATEDRAGIMAGE
:
11322 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
11324 case LVM_DELETEALLITEMS
:
11325 return LISTVIEW_DeleteAllItems(infoPtr
, FALSE
);
11327 case LVM_DELETECOLUMN
:
11328 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
11330 case LVM_DELETEITEM
:
11331 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
11333 case LVM_EDITLABELA
:
11334 case LVM_EDITLABELW
:
11335 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
,
11336 uMsg
== LVM_EDITLABELW
);
11337 /* case LVM_ENABLEGROUPVIEW: */
11339 case LVM_ENSUREVISIBLE
:
11340 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
11342 case LVM_FINDITEMW
:
11343 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
11345 case LVM_FINDITEMA
:
11346 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
11348 case LVM_GETBKCOLOR
:
11349 return infoPtr
->clrBk
;
11351 /* case LVM_GETBKIMAGE: */
11353 case LVM_GETCALLBACKMASK
:
11354 return infoPtr
->uCallbackMask
;
11356 case LVM_GETCOLUMNA
:
11357 case LVM_GETCOLUMNW
:
11358 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
,
11359 uMsg
== LVM_GETCOLUMNW
);
11361 case LVM_GETCOLUMNORDERARRAY
:
11362 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
11364 case LVM_GETCOLUMNWIDTH
:
11365 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
11367 case LVM_GETCOUNTPERPAGE
:
11368 return LISTVIEW_GetCountPerPage(infoPtr
);
11370 case LVM_GETEDITCONTROL
:
11371 return (LRESULT
)infoPtr
->hwndEdit
;
11373 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
11374 return infoPtr
->dwLvExStyle
;
11376 /* case LVM_GETGROUPINFO: */
11378 /* case LVM_GETGROUPMETRICS: */
11380 case LVM_GETHEADER
:
11381 return (LRESULT
)infoPtr
->hwndHeader
;
11383 case LVM_GETHOTCURSOR
:
11384 return (LRESULT
)infoPtr
->hHotCursor
;
11386 case LVM_GETHOTITEM
:
11387 return infoPtr
->nHotItem
;
11389 case LVM_GETHOVERTIME
:
11390 return infoPtr
->dwHoverTime
;
11392 case LVM_GETIMAGELIST
:
11393 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
11395 /* case LVM_GETINSERTMARK: */
11397 /* case LVM_GETINSERTMARKCOLOR: */
11399 /* case LVM_GETINSERTMARKRECT: */
11401 case LVM_GETISEARCHSTRINGA
:
11402 case LVM_GETISEARCHSTRINGW
:
11403 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
11408 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, uMsg
== LVM_GETITEMW
);
11410 case LVM_GETITEMCOUNT
:
11411 return infoPtr
->nItemCount
;
11413 case LVM_GETITEMPOSITION
:
11414 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
11416 case LVM_GETITEMRECT
:
11417 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
11419 case LVM_GETITEMSPACING
:
11420 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
11422 case LVM_GETITEMSTATE
:
11423 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
11425 case LVM_GETITEMTEXTA
:
11426 case LVM_GETITEMTEXTW
:
11427 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
,
11428 uMsg
== LVM_GETITEMTEXTW
);
11430 case LVM_GETNEXTITEM
:
11431 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
11433 case LVM_GETNUMBEROFWORKAREAS
:
11434 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
11437 case LVM_GETORIGIN
:
11438 if (!lParam
) return FALSE
;
11439 if (infoPtr
->uView
== LV_VIEW_DETAILS
||
11440 infoPtr
->uView
== LV_VIEW_LIST
) return FALSE
;
11441 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
11444 /* case LVM_GETOUTLINECOLOR: */
11446 /* case LVM_GETSELECTEDCOLUMN: */
11448 case LVM_GETSELECTEDCOUNT
:
11449 return LISTVIEW_GetSelectedCount(infoPtr
);
11451 case LVM_GETSELECTIONMARK
:
11452 return infoPtr
->nSelectionMark
;
11454 case LVM_GETSTRINGWIDTHA
:
11455 case LVM_GETSTRINGWIDTHW
:
11456 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
,
11457 uMsg
== LVM_GETSTRINGWIDTHW
);
11459 case LVM_GETSUBITEMRECT
:
11460 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
11462 case LVM_GETTEXTBKCOLOR
:
11463 return infoPtr
->clrTextBk
;
11465 case LVM_GETTEXTCOLOR
:
11466 return infoPtr
->clrText
;
11468 /* case LVM_GETTILEINFO: */
11470 /* case LVM_GETTILEVIEWINFO: */
11472 case LVM_GETTOOLTIPS
:
11473 if( !infoPtr
->hwndToolTip
)
11474 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
11475 return (LRESULT
)infoPtr
->hwndToolTip
;
11477 case LVM_GETTOPINDEX
:
11478 return LISTVIEW_GetTopIndex(infoPtr
);
11480 case LVM_GETUNICODEFORMAT
:
11481 return (infoPtr
->notifyFormat
== NFR_UNICODE
);
11484 return infoPtr
->uView
;
11486 case LVM_GETVIEWRECT
:
11487 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
11489 case LVM_GETWORKAREAS
:
11490 FIXME("LVM_GETWORKAREAS: unimplemented\n");
11493 /* case LVM_HASGROUP: */
11496 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, TRUE
);
11498 case LVM_INSERTCOLUMNA
:
11499 case LVM_INSERTCOLUMNW
:
11500 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
,
11501 uMsg
== LVM_INSERTCOLUMNW
);
11503 /* case LVM_INSERTGROUP: */
11505 /* case LVM_INSERTGROUPSORTED: */
11507 case LVM_INSERTITEMA
:
11508 case LVM_INSERTITEMW
:
11509 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, uMsg
== LVM_INSERTITEMW
);
11511 /* case LVM_INSERTMARKHITTEST: */
11513 /* case LVM_ISGROUPVIEWENABLED: */
11515 case LVM_ISITEMVISIBLE
:
11516 return LISTVIEW_IsItemVisible(infoPtr
, (INT
)wParam
);
11518 case LVM_MAPIDTOINDEX
:
11519 return LISTVIEW_MapIdToIndex(infoPtr
, (UINT
)wParam
);
11521 case LVM_MAPINDEXTOID
:
11522 return LISTVIEW_MapIndexToId(infoPtr
, (INT
)wParam
);
11524 /* case LVM_MOVEGROUP: */
11526 /* case LVM_MOVEITEMTOGROUP: */
11528 case LVM_REDRAWITEMS
:
11529 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
11531 /* case LVM_REMOVEALLGROUPS: */
11533 /* case LVM_REMOVEGROUP: */
11536 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
11538 case LVM_SETBKCOLOR
:
11539 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
11541 /* case LVM_SETBKIMAGE: */
11543 case LVM_SETCALLBACKMASK
:
11544 infoPtr
->uCallbackMask
= (UINT
)wParam
;
11547 case LVM_SETCOLUMNA
:
11548 case LVM_SETCOLUMNW
:
11549 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
,
11550 uMsg
== LVM_SETCOLUMNW
);
11552 case LVM_SETCOLUMNORDERARRAY
:
11553 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
11555 case LVM_SETCOLUMNWIDTH
:
11556 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
11558 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
11559 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
11561 /* case LVM_SETGROUPINFO: */
11563 /* case LVM_SETGROUPMETRICS: */
11565 case LVM_SETHOTCURSOR
:
11566 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
11568 case LVM_SETHOTITEM
:
11569 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
11571 case LVM_SETHOVERTIME
:
11572 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)lParam
);
11574 case LVM_SETICONSPACING
:
11576 return LISTVIEW_SetIconSpacing(infoPtr
, -1, -1);
11577 return LISTVIEW_SetIconSpacing(infoPtr
, LOWORD(lParam
), HIWORD(lParam
));
11579 case LVM_SETIMAGELIST
:
11580 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
11582 /* case LVM_SETINFOTIP: */
11584 /* case LVM_SETINSERTMARK: */
11586 /* case LVM_SETINSERTMARKCOLOR: */
11591 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
11592 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, (uMsg
== LVM_SETITEMW
));
11595 case LVM_SETITEMCOUNT
:
11596 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
11598 case LVM_SETITEMPOSITION
:
11601 pt
.x
= (short)LOWORD(lParam
);
11602 pt
.y
= (short)HIWORD(lParam
);
11603 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, &pt
);
11606 case LVM_SETITEMPOSITION32
:
11607 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, (POINT
*)lParam
);
11609 case LVM_SETITEMSTATE
:
11610 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
11612 case LVM_SETITEMTEXTA
:
11613 case LVM_SETITEMTEXTW
:
11614 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
,
11615 uMsg
== LVM_SETITEMTEXTW
);
11617 /* case LVM_SETOUTLINECOLOR: */
11619 /* case LVM_SETSELECTEDCOLUMN: */
11621 case LVM_SETSELECTIONMARK
:
11622 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
11624 case LVM_SETTEXTBKCOLOR
:
11625 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
11627 case LVM_SETTEXTCOLOR
:
11628 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
11630 /* case LVM_SETTILEINFO: */
11632 /* case LVM_SETTILEVIEWINFO: */
11634 /* case LVM_SETTILEWIDTH: */
11636 case LVM_SETTOOLTIPS
:
11637 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
11639 case LVM_SETUNICODEFORMAT
:
11640 return LISTVIEW_SetUnicodeFormat(infoPtr
, wParam
);
11643 return LISTVIEW_SetView(infoPtr
, wParam
);
11645 /* case LVM_SETWORKAREAS: */
11647 /* case LVM_SORTGROUPS: */
11649 case LVM_SORTITEMS
:
11650 case LVM_SORTITEMSEX
:
11651 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, wParam
,
11652 uMsg
== LVM_SORTITEMSEX
);
11653 case LVM_SUBITEMHITTEST
:
11654 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
11657 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
11659 case CCM_GETVERSION
:
11660 return LISTVIEW_GetVersion(infoPtr
);
11662 case CCM_SETVERSION
:
11663 return LISTVIEW_SetVersion(infoPtr
, wParam
);
11666 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
11669 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
11672 return LISTVIEW_NCCreate(hwnd
, wParam
, (LPCREATESTRUCTW
)lParam
);
11675 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
11678 return LISTVIEW_Destroy(infoPtr
);
11681 return LISTVIEW_Enable(infoPtr
);
11683 case WM_ERASEBKGND
:
11684 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
11686 case WM_GETDLGCODE
:
11687 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
11690 return (LRESULT
)infoPtr
->hFont
;
11693 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0);
11696 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
11699 return LISTVIEW_KillFocus(infoPtr
);
11701 case WM_LBUTTONDBLCLK
:
11702 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11704 case WM_LBUTTONDOWN
:
11705 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11708 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11711 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11713 case WM_MOUSEHOVER
:
11714 return LISTVIEW_MouseHover(infoPtr
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11717 return LISTVIEW_NCDestroy(infoPtr
);
11720 return LISTVIEW_NCPaint(infoPtr
, (HRGN
)wParam
);
11723 return LISTVIEW_Notify(infoPtr
, (LPNMHDR
)lParam
);
11725 case WM_NOTIFYFORMAT
:
11726 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
11728 case WM_PRINTCLIENT
:
11729 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
11732 return LISTVIEW_WMPaint(infoPtr
, (HDC
)wParam
);
11734 case WM_RBUTTONDBLCLK
:
11735 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11737 case WM_RBUTTONDOWN
:
11738 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11741 return LISTVIEW_SetCursor(infoPtr
, wParam
, lParam
);
11744 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
11747 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
11750 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
11752 case WM_SHOWWINDOW
:
11753 return LISTVIEW_ShowWindow(infoPtr
, wParam
, lParam
);
11755 case WM_STYLECHANGED
:
11756 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
11758 case WM_STYLECHANGING
:
11759 return LISTVIEW_StyleChanging(wParam
, (LPSTYLESTRUCT
)lParam
);
11761 case WM_SYSCOLORCHANGE
:
11762 COMCTL32_RefreshSysColors();
11765 /* case WM_TIMER: */
11766 case WM_THEMECHANGED
:
11767 return LISTVIEW_ThemeChanged(infoPtr
);
11770 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0);
11772 case WM_MOUSEWHEEL
:
11773 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
11774 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11775 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
11777 case WM_WINDOWPOSCHANGED
:
11778 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
11780 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
11781 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
11783 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
11785 if (notify_measureitem(infoPtr
)) LISTVIEW_InvalidateList(infoPtr
);
11787 LISTVIEW_Size(infoPtr
, ((WINDOWPOS
*)lParam
)->cx
, ((WINDOWPOS
*)lParam
)->cy
);
11789 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11791 /* case WM_WININICHANGE: */
11794 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
) && !COMCTL32_IsReflectedMessage(uMsg
))
11795 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg
, wParam
, lParam
);
11797 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11804 * Registers the window class.
11812 void LISTVIEW_Register(void)
11814 WNDCLASSW wndClass
;
11816 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
11817 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
11818 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
11819 wndClass
.cbClsExtra
= 0;
11820 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
11821 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
11822 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
11823 wndClass
.lpszClassName
= WC_LISTVIEWW
;
11824 RegisterClassW(&wndClass
);
11829 * Unregisters the window class.
11837 void LISTVIEW_Unregister(void)
11839 UnregisterClassW(WC_LISTVIEWW
, NULL
);
11844 * Handle any WM_COMMAND messages
11847 * [I] infoPtr : valid pointer to the listview structure
11848 * [I] wParam : the first message parameter
11849 * [I] lParam : the second message parameter
11854 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
11857 TRACE("(%p %x %x %lx)\n", infoPtr
, HIWORD(wParam
), LOWORD(wParam
), lParam
);
11859 if (!infoPtr
->hwndEdit
) return 0;
11861 switch (HIWORD(wParam
))
11866 * Adjust the edit window size
11868 WCHAR buffer
[1024];
11869 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
11870 HFONT hFont
, hOldFont
= 0;
11874 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
11875 GetWindowTextW(infoPtr
->hwndEdit
, buffer
, ARRAY_SIZE(buffer
));
11876 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
11878 /* Select font to get the right dimension of the string */
11879 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
11882 hOldFont
= SelectObject(hdc
, hFont
);
11885 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
11887 TEXTMETRICW textMetric
;
11889 /* Add Extra spacing for the next character */
11890 GetTextMetricsW(hdc
, &textMetric
);
11891 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
11893 SetWindowPos(infoPtr
->hwndEdit
, NULL
, 0, 0, sz
.cx
,
11894 rect
.bottom
- rect
.top
, SWP_DRAWFRAME
| SWP_NOMOVE
| SWP_NOZORDER
);
11897 SelectObject(hdc
, hOldFont
);
11899 ReleaseDC(infoPtr
->hwndEdit
, hdc
);
11905 LISTVIEW_CancelEditLabel(infoPtr
);
11910 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);