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 enum notification_mask
216 NOTIFY_MASK_ITEM_CHANGE
= 0x1,
217 NOTIFY_MASK_END_LABEL_EDIT
= 0x2,
218 NOTIFY_MASK_UNMASK_ALL
= 0xffffffff
221 typedef struct tagLISTVIEW_INFO
225 RECT rcList
; /* This rectangle is really the window
226 * client rectangle possibly reduced by the
227 * horizontal scroll bar and/or header - see
228 * LISTVIEW_UpdateSize. This rectangle offset
229 * by the LISTVIEW_GetOrigin value is in
230 * client coordinates */
232 /* notification window */
242 INT nItemCount
; /* the number of items in the list */
243 HDPA hdpaItems
; /* array ITEM_INFO pointers */
244 HDPA hdpaItemIds
; /* array of ITEM_ID pointers */
245 HDPA hdpaPosX
; /* maintains the (X, Y) coordinates of the */
246 HDPA hdpaPosY
; /* items in LVS_ICON, and LVS_SMALLICON modes */
247 RANGES selectionRanges
;
248 INT nSelectionMark
; /* item to start next multiselection from */
252 HDPA hdpaColumns
; /* array of COLUMN_INFO pointers */
253 BOOL colRectsDirty
; /* trigger column rectangles requery from header */
256 BOOL bNoItemMetrics
; /* flags if item metrics are not yet computed */
261 PFNLVCOMPARE pfnCompare
; /* sorting callback pointer */
265 DWORD dwStyle
; /* the cached window GWL_STYLE */
266 DWORD dwLvExStyle
; /* extended listview style */
267 DWORD uView
; /* current view available through LVM_[G,S]ETVIEW */
273 DELAYED_ITEM_EDIT itemEdit
; /* Pointer to this structure will be the timer ID */
276 HIMAGELIST himlNormal
;
277 HIMAGELIST himlSmall
;
278 HIMAGELIST himlState
;
283 POINT currIconPos
; /* this is the position next icon will be placed */
287 INT xTrackLine
; /* The x coefficient of the track line or -1 if none */
289 /* marquee selection */
290 BOOL bMarqueeSelect
; /* marquee selection/highlight underway */
292 RECT marqueeRect
; /* absolute coordinates of marquee selection */
293 RECT marqueeDrawRect
; /* relative coordinates for drawing marquee */
294 POINT marqueeOrigin
; /* absolute coordinates of marquee click origin */
297 BOOL bFocus
; /* control has focus */
299 RECT rcFocus
; /* focus bounds */
310 INT ntmHeight
; /* Some cached metrics of the font used */
311 INT ntmMaxCharWidth
; /* by the listview to draw items */
314 /* mouse operation */
317 POINT ptClickPos
; /* point where the user clicked */
318 INT nLButtonDownItem
; /* tracks item to reset multiselection on WM_LBUTTONUP */
323 /* keyboard operation */
324 DWORD lastKeyPressTimestamp
;
326 INT nSearchParamLength
;
327 WCHAR szSearchParam
[ MAX_PATH
];
330 BOOL bIsDrawing
; /* Drawing in progress */
331 INT nMeasureItemHeight
; /* WM_MEASUREITEM result */
332 BOOL redraw
; /* WM_SETREDRAW switch */
335 DWORD iVersion
; /* CCM_[G,S]ETVERSION */
341 /* How many we debug buffer to allocate */
342 #define DEBUG_BUFFERS 20
343 /* The size of a single debug buffer */
344 #define DEBUG_BUFFER_SIZE 256
346 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
347 #define SB_INTERNAL -1
349 /* maximum size of a label */
350 #define DISP_TEXT_SIZE 260
352 /* padding for items in list and small icon display modes */
353 #define WIDTH_PADDING 12
355 /* padding for items in list, report and small icon display modes */
356 #define HEIGHT_PADDING 1
358 /* offset of items in report display mode */
359 #define REPORT_MARGINX 2
361 /* padding for icon in large icon display mode
362 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
363 * that HITTEST will see.
364 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
365 * ICON_TOP_PADDING - sum of the two above.
366 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
367 * LABEL_HOR_PADDING - between text and sides of box
368 * LABEL_VERT_PADDING - between bottom of text and end of box
370 * ICON_LR_PADDING - additional width above icon size.
371 * ICON_LR_HALF - half of the above value
373 #define ICON_TOP_PADDING_NOTHITABLE 2
374 #define ICON_TOP_PADDING_HITABLE 2
375 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
376 #define ICON_BOTTOM_PADDING 4
377 #define LABEL_HOR_PADDING 5
378 #define LABEL_VERT_PADDING 7
379 #define ICON_LR_PADDING 16
380 #define ICON_LR_HALF (ICON_LR_PADDING/2)
382 /* default label width for items in list and small icon display modes */
383 #define DEFAULT_LABEL_WIDTH 40
384 /* maximum select rectangle width for empty text item in LV_VIEW_DETAILS */
385 #define MAX_EMPTYTEXT_SELECT_WIDTH 80
387 /* default column width for items in list display mode */
388 #define DEFAULT_COLUMN_WIDTH 128
390 /* Size of "line" scroll for V & H scrolls */
391 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
393 /* Padding between image and label */
394 #define IMAGE_PADDING 2
396 /* Padding behind the label */
397 #define TRAILING_LABEL_PADDING 12
398 #define TRAILING_HEADER_PADDING 11
400 /* Border for the icon caption */
401 #define CAPTION_BORDER 2
403 /* Standard DrawText flags */
404 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
405 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
406 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
408 /* Image index from state */
409 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
411 /* The time in milliseconds to reset the search in the list */
412 #define KEY_DELAY 450
414 /* Dump the LISTVIEW_INFO structure to the debug channel */
415 #define LISTVIEW_DUMP(iP) do { \
416 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
417 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
418 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
419 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
420 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
421 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
422 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
423 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
424 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
425 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
428 static const WCHAR themeClass
[] = {'L','i','s','t','V','i','e','w',0};
431 * forward declarations
433 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
434 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*, INT
, LPRECT
);
435 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*, INT
, LPPOINT
);
436 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*, INT
, LPPOINT
);
437 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*, INT
, LPRECT
);
438 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*, LPPOINT
);
439 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*, LPRECT
);
440 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
441 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*, WPARAM
, LPARAM
);
442 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
443 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
, BOOL
);
444 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*, INT
, UINT
);
445 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, const LVITEMW
*);
446 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
);
447 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
);
448 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
449 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*, INT
, HIMAGELIST
);
450 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*, LPLVHITTESTINFO
, BOOL
, BOOL
);
451 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*, BOOL
, BOOL
);
452 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*, INT
, INT
);
454 /******** Text handling functions *************************************/
456 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
457 * text string. The string may be ANSI or Unicode, in which case
458 * the boolean isW tells us the type of the string.
460 * The name of the function tell what type of strings it expects:
461 * W: Unicode, T: ANSI/Unicode - function of isW
464 static inline BOOL
is_text(LPCWSTR text
)
466 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
469 static inline int textlenT(LPCWSTR text
, BOOL isW
)
471 return !is_text(text
) ? 0 :
472 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
475 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
478 if (isSrcW
) lstrcpynW(dest
, src
, max
);
479 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
481 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
482 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
485 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
487 LPWSTR wstr
= (LPWSTR
)text
;
489 if (!isW
&& is_text(text
))
491 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
492 wstr
= Alloc(len
* sizeof(WCHAR
));
493 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
495 TRACE(" wstr=%s\n", text
== LPSTR_TEXTCALLBACKW
? "(callback)" : debugstr_w(wstr
));
499 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
501 if (!isW
&& is_text(wstr
)) Free (wstr
);
505 * dest is a pointer to a Unicode string
506 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
508 static BOOL
textsetptrT(LPWSTR
*dest
, LPCWSTR src
, BOOL isW
)
512 if (src
== LPSTR_TEXTCALLBACKW
)
514 if (is_text(*dest
)) Free(*dest
);
515 *dest
= LPSTR_TEXTCALLBACKW
;
519 LPWSTR pszText
= textdupTtoW(src
, isW
);
520 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
521 bResult
= Str_SetPtrW(dest
, pszText
);
522 textfreeT(pszText
, isW
);
528 * compares a Unicode to a Unicode/ANSI text string
530 static inline int textcmpWT(LPCWSTR aw
, LPCWSTR bt
, BOOL isW
)
532 if (!aw
) return bt
? -1 : 0;
534 if (aw
== LPSTR_TEXTCALLBACKW
)
535 return bt
== LPSTR_TEXTCALLBACKW
? 1 : -1;
536 if (bt
!= LPSTR_TEXTCALLBACKW
)
538 LPWSTR bw
= textdupTtoW(bt
, isW
);
539 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
547 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
549 n
= min(min(n
, lstrlenW(s1
)), lstrlenW(s2
));
550 return CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
) - CSTR_EQUAL
;
553 /******** Debugging functions *****************************************/
555 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
557 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
558 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
561 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
563 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
564 n
= min(textlenT(text
, isW
), n
);
565 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
568 static char* debug_getbuf(void)
570 static int index
= 0;
571 static char buffers
[DEBUG_BUFFERS
][DEBUG_BUFFER_SIZE
];
572 return buffers
[index
++ % DEBUG_BUFFERS
];
575 static inline const char* debugrange(const RANGE
*lprng
)
577 if (!lprng
) return "(null)";
578 return wine_dbg_sprintf("[%d, %d]", lprng
->lower
, lprng
->upper
);
581 static const char* debugscrollinfo(const SCROLLINFO
*pScrollInfo
)
583 char* buf
= debug_getbuf(), *text
= buf
;
584 int len
, size
= DEBUG_BUFFER_SIZE
;
586 if (pScrollInfo
== NULL
) return "(null)";
587 len
= snprintf(buf
, size
, "{cbSize=%u, ", pScrollInfo
->cbSize
);
588 if (len
== -1) goto end
;
589 buf
+= len
; size
-= len
;
590 if (pScrollInfo
->fMask
& SIF_RANGE
)
591 len
= snprintf(buf
, size
, "nMin=%d, nMax=%d, ", pScrollInfo
->nMin
, pScrollInfo
->nMax
);
593 if (len
== -1) goto end
;
594 buf
+= len
; size
-= len
;
595 if (pScrollInfo
->fMask
& SIF_PAGE
)
596 len
= snprintf(buf
, size
, "nPage=%u, ", pScrollInfo
->nPage
);
598 if (len
== -1) goto end
;
599 buf
+= len
; size
-= len
;
600 if (pScrollInfo
->fMask
& SIF_POS
)
601 len
= snprintf(buf
, size
, "nPos=%d, ", pScrollInfo
->nPos
);
603 if (len
== -1) goto end
;
604 buf
+= len
; size
-= len
;
605 if (pScrollInfo
->fMask
& SIF_TRACKPOS
)
606 len
= snprintf(buf
, size
, "nTrackPos=%d, ", pScrollInfo
->nTrackPos
);
608 if (len
== -1) goto end
;
612 buf
= text
+ strlen(text
);
614 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
618 static const char* debugnmlistview(const NMLISTVIEW
*plvnm
)
620 if (!plvnm
) return "(null)";
621 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
622 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
623 plvnm
->iItem
, plvnm
->iSubItem
, plvnm
->uNewState
, plvnm
->uOldState
,
624 plvnm
->uChanged
, wine_dbgstr_point(&plvnm
->ptAction
), plvnm
->lParam
);
627 static const char* debuglvitem_t(const LVITEMW
*lpLVItem
, BOOL isW
)
629 char* buf
= debug_getbuf(), *text
= buf
;
630 int len
, size
= DEBUG_BUFFER_SIZE
;
632 if (lpLVItem
== NULL
) return "(null)";
633 len
= snprintf(buf
, size
, "{iItem=%d, iSubItem=%d, ", lpLVItem
->iItem
, lpLVItem
->iSubItem
);
634 if (len
== -1) goto end
;
635 buf
+= len
; size
-= len
;
636 if (lpLVItem
->mask
& LVIF_STATE
)
637 len
= snprintf(buf
, size
, "state=%x, stateMask=%x, ", lpLVItem
->state
, lpLVItem
->stateMask
);
639 if (len
== -1) goto end
;
640 buf
+= len
; size
-= len
;
641 if (lpLVItem
->mask
& LVIF_TEXT
)
642 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem
->pszText
, isW
, 80), lpLVItem
->cchTextMax
);
644 if (len
== -1) goto end
;
645 buf
+= len
; size
-= len
;
646 if (lpLVItem
->mask
& LVIF_IMAGE
)
647 len
= snprintf(buf
, size
, "iImage=%d, ", lpLVItem
->iImage
);
649 if (len
== -1) goto end
;
650 buf
+= len
; size
-= len
;
651 if (lpLVItem
->mask
& LVIF_PARAM
)
652 len
= snprintf(buf
, size
, "lParam=%lx, ", lpLVItem
->lParam
);
654 if (len
== -1) goto end
;
655 buf
+= len
; size
-= len
;
656 if (lpLVItem
->mask
& LVIF_INDENT
)
657 len
= snprintf(buf
, size
, "iIndent=%d, ", lpLVItem
->iIndent
);
659 if (len
== -1) goto end
;
663 buf
= text
+ strlen(text
);
665 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
669 static const char* debuglvcolumn_t(const LVCOLUMNW
*lpColumn
, BOOL isW
)
671 char* buf
= debug_getbuf(), *text
= buf
;
672 int len
, size
= DEBUG_BUFFER_SIZE
;
674 if (lpColumn
== NULL
) return "(null)";
675 len
= snprintf(buf
, size
, "{");
676 if (len
== -1) goto end
;
677 buf
+= len
; size
-= len
;
678 if (lpColumn
->mask
& LVCF_SUBITEM
)
679 len
= snprintf(buf
, size
, "iSubItem=%d, ", lpColumn
->iSubItem
);
681 if (len
== -1) goto end
;
682 buf
+= len
; size
-= len
;
683 if (lpColumn
->mask
& LVCF_FMT
)
684 len
= snprintf(buf
, size
, "fmt=%x, ", lpColumn
->fmt
);
686 if (len
== -1) goto end
;
687 buf
+= len
; size
-= len
;
688 if (lpColumn
->mask
& LVCF_WIDTH
)
689 len
= snprintf(buf
, size
, "cx=%d, ", lpColumn
->cx
);
691 if (len
== -1) goto end
;
692 buf
+= len
; size
-= len
;
693 if (lpColumn
->mask
& LVCF_TEXT
)
694 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn
->pszText
, isW
, 80), lpColumn
->cchTextMax
);
696 if (len
== -1) goto end
;
697 buf
+= len
; size
-= len
;
698 if (lpColumn
->mask
& LVCF_IMAGE
)
699 len
= snprintf(buf
, size
, "iImage=%d, ", lpColumn
->iImage
);
701 if (len
== -1) goto end
;
702 buf
+= len
; size
-= len
;
703 if (lpColumn
->mask
& LVCF_ORDER
)
704 len
= snprintf(buf
, size
, "iOrder=%d, ", lpColumn
->iOrder
);
706 if (len
== -1) goto end
;
710 buf
= text
+ strlen(text
);
712 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
716 static const char* debuglvhittestinfo(const LVHITTESTINFO
*lpht
)
718 if (!lpht
) return "(null)";
720 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
721 wine_dbgstr_point(&lpht
->pt
), lpht
->flags
, lpht
->iItem
, lpht
->iSubItem
);
724 /* Return the corresponding text for a given scroll value */
725 static inline LPCSTR
debugscrollcode(int nScrollCode
)
729 case SB_LINELEFT
: return "SB_LINELEFT";
730 case SB_LINERIGHT
: return "SB_LINERIGHT";
731 case SB_PAGELEFT
: return "SB_PAGELEFT";
732 case SB_PAGERIGHT
: return "SB_PAGERIGHT";
733 case SB_THUMBPOSITION
: return "SB_THUMBPOSITION";
734 case SB_THUMBTRACK
: return "SB_THUMBTRACK";
735 case SB_ENDSCROLL
: return "SB_ENDSCROLL";
736 case SB_INTERNAL
: return "SB_INTERNAL";
737 default: return "unknown";
742 /******** Notification functions ************************************/
744 static int get_ansi_notification(UINT unicodeNotificationCode
)
746 switch (unicodeNotificationCode
)
748 case LVN_BEGINLABELEDITA
:
749 case LVN_BEGINLABELEDITW
: return LVN_BEGINLABELEDITA
;
750 case LVN_ENDLABELEDITA
:
751 case LVN_ENDLABELEDITW
: return LVN_ENDLABELEDITA
;
752 case LVN_GETDISPINFOA
:
753 case LVN_GETDISPINFOW
: return LVN_GETDISPINFOA
;
754 case LVN_SETDISPINFOA
:
755 case LVN_SETDISPINFOW
: return LVN_SETDISPINFOA
;
756 case LVN_ODFINDITEMA
:
757 case LVN_ODFINDITEMW
: return LVN_ODFINDITEMA
;
758 case LVN_GETINFOTIPA
:
759 case LVN_GETINFOTIPW
: return LVN_GETINFOTIPA
;
760 /* header forwards */
762 case HDN_TRACKW
: return HDN_TRACKA
;
764 case HDN_ENDTRACKW
: return HDN_ENDTRACKA
;
765 case HDN_BEGINDRAG
: return HDN_BEGINDRAG
;
766 case HDN_ENDDRAG
: return HDN_ENDDRAG
;
767 case HDN_ITEMCHANGINGA
:
768 case HDN_ITEMCHANGINGW
: return HDN_ITEMCHANGINGA
;
769 case HDN_ITEMCHANGEDA
:
770 case HDN_ITEMCHANGEDW
: return HDN_ITEMCHANGEDA
;
772 case HDN_ITEMCLICKW
: return HDN_ITEMCLICKA
;
773 case HDN_DIVIDERDBLCLICKA
:
774 case HDN_DIVIDERDBLCLICKW
: return HDN_DIVIDERDBLCLICKA
;
777 FIXME("unknown notification %x\n", unicodeNotificationCode
);
778 return unicodeNotificationCode
;
781 /* forwards header notifications to listview parent */
782 static LRESULT
notify_forward_header(const LISTVIEW_INFO
*infoPtr
, NMHEADERW
*lpnmhW
)
784 LPCWSTR text
= NULL
, filter
= NULL
;
786 NMHEADERA
*lpnmh
= (NMHEADERA
*) lpnmhW
;
788 /* on unicode format exit earlier */
789 if (infoPtr
->notifyFormat
== NFR_UNICODE
)
790 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, lpnmh
->hdr
.idFrom
,
793 /* header always supplies unicode notifications,
794 all we have to do is to convert strings to ANSI */
797 /* convert item text */
798 if (lpnmh
->pitem
->mask
& HDI_TEXT
)
800 text
= (LPCWSTR
)lpnmh
->pitem
->pszText
;
801 lpnmh
->pitem
->pszText
= NULL
;
802 Str_SetPtrWtoA(&lpnmh
->pitem
->pszText
, text
);
804 /* convert filter text */
805 if ((lpnmh
->pitem
->mask
& HDI_FILTER
) && (lpnmh
->pitem
->type
== HDFT_ISSTRING
) &&
806 lpnmh
->pitem
->pvFilter
)
808 filter
= (LPCWSTR
)((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
;
809 ((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
= NULL
;
810 Str_SetPtrWtoA(&((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
, filter
);
813 lpnmh
->hdr
.code
= get_ansi_notification(lpnmh
->hdr
.code
);
815 ret
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, lpnmh
->hdr
.idFrom
,
821 Free(lpnmh
->pitem
->pszText
);
822 lpnmh
->pitem
->pszText
= (LPSTR
)text
;
826 Free(((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
);
827 ((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
= (LPSTR
)filter
;
833 static LRESULT
notify_hdr(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
837 TRACE("(code=%d)\n", code
);
839 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
840 pnmh
->idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
842 result
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, pnmh
->idFrom
, (LPARAM
)pnmh
);
844 TRACE(" <= %ld\n", result
);
849 static inline BOOL
notify(const LISTVIEW_INFO
*infoPtr
, INT code
)
852 HWND hwnd
= infoPtr
->hwndSelf
;
853 notify_hdr(infoPtr
, code
, &nmh
);
854 return IsWindow(hwnd
);
857 static inline void notify_itemactivate(const LISTVIEW_INFO
*infoPtr
, const LVHITTESTINFO
*htInfo
)
867 item
.mask
= LVIF_PARAM
|LVIF_STATE
;
868 item
.iItem
= htInfo
->iItem
;
870 item
.stateMask
= (UINT
)-1;
871 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) {
872 nmia
.lParam
= item
.lParam
;
873 nmia
.uOldState
= item
.state
;
874 nmia
.uNewState
= item
.state
| LVIS_ACTIVATING
;
875 nmia
.uChanged
= LVIF_STATE
;
878 nmia
.iItem
= htInfo
->iItem
;
879 nmia
.iSubItem
= htInfo
->iSubItem
;
880 nmia
.ptAction
= htInfo
->pt
;
882 if (GetKeyState(VK_SHIFT
) & 0x8000) nmia
.uKeyFlags
|= LVKF_SHIFT
;
883 if (GetKeyState(VK_CONTROL
) & 0x8000) nmia
.uKeyFlags
|= LVKF_CONTROL
;
884 if (GetKeyState(VK_MENU
) & 0x8000) nmia
.uKeyFlags
|= LVKF_ALT
;
886 notify_hdr(infoPtr
, LVN_ITEMACTIVATE
, (LPNMHDR
)&nmia
);
889 static inline LRESULT
notify_listview(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
891 TRACE("(code=%d, plvnm=%s)\n", code
, debugnmlistview(plvnm
));
892 return notify_hdr(infoPtr
, code
, (LPNMHDR
)plvnm
);
895 /* Handles NM_DBLCLK, NM_CLICK, NM_RDBLCLK, NM_RCLICK. Only NM_RCLICK return value is used. */
896 static BOOL
notify_click(const LISTVIEW_INFO
*infoPtr
, INT code
, const LVHITTESTINFO
*lvht
)
900 HWND hwnd
= infoPtr
->hwndSelf
;
903 TRACE("code=%d, lvht=%s\n", code
, debuglvhittestinfo(lvht
));
904 ZeroMemory(&nmia
, sizeof(nmia
));
905 nmia
.iItem
= lvht
->iItem
;
906 nmia
.iSubItem
= lvht
->iSubItem
;
907 nmia
.ptAction
= lvht
->pt
;
908 item
.mask
= LVIF_PARAM
;
909 item
.iItem
= lvht
->iItem
;
911 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmia
.lParam
= item
.lParam
;
912 ret
= notify_hdr(infoPtr
, code
, (NMHDR
*)&nmia
);
913 return IsWindow(hwnd
) && (code
== NM_RCLICK
? !ret
: TRUE
);
916 static BOOL
notify_deleteitem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
920 HWND hwnd
= infoPtr
->hwndSelf
;
922 ZeroMemory(&nmlv
, sizeof (NMLISTVIEW
));
924 item
.mask
= LVIF_PARAM
;
927 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
928 notify_listview(infoPtr
, LVN_DELETEITEM
, &nmlv
);
929 return IsWindow(hwnd
);
933 Send notification. depends on dispinfoW having same
934 structure as dispinfoA.
935 infoPtr : listview struct
936 code : *Unicode* notification code
937 pdi : dispinfo structure (can be unicode or ansi)
938 isW : TRUE if dispinfo is Unicode
940 static BOOL
notify_dispinfoT(const LISTVIEW_INFO
*infoPtr
, UINT code
, LPNMLVDISPINFOW pdi
, BOOL isW
)
942 INT length
= 0, ret_length
;
943 LPWSTR buffer
= NULL
, ret_text
;
944 BOOL return_ansi
= FALSE
;
945 BOOL return_unicode
= FALSE
;
948 if ((pdi
->item
.mask
& LVIF_TEXT
) && is_text(pdi
->item
.pszText
))
950 return_unicode
= ( isW
&& infoPtr
->notifyFormat
== NFR_ANSI
);
951 return_ansi
= (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
);
954 ret_length
= pdi
->item
.cchTextMax
;
955 ret_text
= pdi
->item
.pszText
;
957 if (return_unicode
|| return_ansi
)
959 if (code
!= LVN_GETDISPINFOW
)
961 length
= return_ansi
?
962 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
963 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
967 length
= pdi
->item
.cchTextMax
;
968 *pdi
->item
.pszText
= 0; /* make sure we don't process garbage */
971 buffer
= Alloc( (return_ansi
? sizeof(WCHAR
) : sizeof(CHAR
)) * length
);
972 if (!buffer
) return FALSE
;
975 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
978 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) buffer
,
981 pdi
->item
.pszText
= buffer
;
982 pdi
->item
.cchTextMax
= length
;
985 if (infoPtr
->notifyFormat
== NFR_ANSI
)
986 code
= get_ansi_notification(code
);
988 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi
->item
, infoPtr
->notifyFormat
!= NFR_ANSI
));
989 ret
= notify_hdr(infoPtr
, code
, &pdi
->hdr
);
990 TRACE(" resulting code=%d\n", pdi
->hdr
.code
);
992 if (return_ansi
|| return_unicode
)
994 if (return_ansi
&& (pdi
->hdr
.code
== LVN_GETDISPINFOA
))
996 strcpy((char*)ret_text
, (char*)pdi
->item
.pszText
);
998 else if (return_unicode
&& (pdi
->hdr
.code
== LVN_GETDISPINFOW
))
1000 strcpyW(ret_text
, pdi
->item
.pszText
);
1002 else if (return_ansi
) /* note : pointer can be changed by app ! */
1004 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) ret_text
,
1005 ret_length
, NULL
, NULL
);
1008 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
1009 ret_text
, ret_length
);
1011 pdi
->item
.pszText
= ret_text
; /* restores our buffer */
1012 pdi
->item
.cchTextMax
= ret_length
;
1018 /* if dispinfo holder changed notification code then convert */
1019 if (!isW
&& (pdi
->hdr
.code
== LVN_GETDISPINFOW
) && (pdi
->item
.mask
& LVIF_TEXT
))
1021 length
= WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
1023 buffer
= Alloc(length
* sizeof(CHAR
));
1024 if (!buffer
) return FALSE
;
1026 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) buffer
,
1027 ret_length
, NULL
, NULL
);
1029 strcpy((LPSTR
)pdi
->item
.pszText
, (LPSTR
)buffer
);
1036 static void customdraw_fill(NMLVCUSTOMDRAW
*lpnmlvcd
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
,
1037 const RECT
*rcBounds
, const LVITEMW
*lplvItem
)
1039 ZeroMemory(lpnmlvcd
, sizeof(NMLVCUSTOMDRAW
));
1040 lpnmlvcd
->nmcd
.hdc
= hdc
;
1041 lpnmlvcd
->nmcd
.rc
= *rcBounds
;
1042 lpnmlvcd
->clrTextBk
= infoPtr
->clrTextBk
;
1043 lpnmlvcd
->clrText
= infoPtr
->clrText
;
1044 if (!lplvItem
) return;
1045 lpnmlvcd
->nmcd
.dwItemSpec
= lplvItem
->iItem
+ 1;
1046 lpnmlvcd
->iSubItem
= lplvItem
->iSubItem
;
1047 if (lplvItem
->state
& LVIS_SELECTED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_SELECTED
;
1048 if (lplvItem
->state
& LVIS_FOCUSED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_FOCUS
;
1049 if (lplvItem
->iItem
== infoPtr
->nHotItem
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_HOT
;
1050 lpnmlvcd
->nmcd
.lItemlParam
= lplvItem
->lParam
;
1053 static inline DWORD
notify_customdraw (const LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, NMLVCUSTOMDRAW
*lpnmlvcd
)
1055 BOOL isForItem
= (lpnmlvcd
->nmcd
.dwItemSpec
!= 0);
1058 lpnmlvcd
->nmcd
.dwDrawStage
= dwDrawStage
;
1059 if (isForItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_ITEM
;
1060 if (lpnmlvcd
->iSubItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_SUBITEM
;
1061 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
--;
1062 result
= notify_hdr(infoPtr
, NM_CUSTOMDRAW
, &lpnmlvcd
->nmcd
.hdr
);
1063 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
++;
1067 static void prepaint_setup (const LISTVIEW_INFO
*infoPtr
, HDC hdc
, NMLVCUSTOMDRAW
*lpnmlvcd
, BOOL SubItem
)
1069 COLORREF backcolor
, textcolor
;
1071 /* apparently, for selected items, we have to override the returned values */
1074 if (lpnmlvcd
->nmcd
.uItemState
& CDIS_SELECTED
)
1076 if (infoPtr
->bFocus
)
1078 lpnmlvcd
->clrTextBk
= comctl32_color
.clrHighlight
;
1079 lpnmlvcd
->clrText
= comctl32_color
.clrHighlightText
;
1081 else if (infoPtr
->dwStyle
& LVS_SHOWSELALWAYS
)
1083 lpnmlvcd
->clrTextBk
= comctl32_color
.clr3dFace
;
1084 lpnmlvcd
->clrText
= comctl32_color
.clrBtnText
;
1089 backcolor
= lpnmlvcd
->clrTextBk
;
1090 textcolor
= lpnmlvcd
->clrText
;
1092 if (backcolor
== CLR_DEFAULT
)
1093 backcolor
= comctl32_color
.clrWindow
;
1094 if (textcolor
== CLR_DEFAULT
)
1095 textcolor
= comctl32_color
.clrWindowText
;
1097 /* Set the text attributes */
1098 if (backcolor
!= CLR_NONE
)
1100 SetBkMode(hdc
, OPAQUE
);
1101 SetBkColor(hdc
, backcolor
);
1104 SetBkMode(hdc
, TRANSPARENT
);
1105 SetTextColor(hdc
, textcolor
);
1108 static inline DWORD
notify_postpaint (const LISTVIEW_INFO
*infoPtr
, NMLVCUSTOMDRAW
*lpnmlvcd
)
1110 return notify_customdraw(infoPtr
, CDDS_POSTPAINT
, lpnmlvcd
);
1113 /* returns TRUE when repaint needed, FALSE otherwise */
1114 static BOOL
notify_measureitem(LISTVIEW_INFO
*infoPtr
)
1116 MEASUREITEMSTRUCT mis
;
1117 mis
.CtlType
= ODT_LISTVIEW
;
1118 mis
.CtlID
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1122 mis
.itemHeight
= infoPtr
->nItemHeight
;
1123 SendMessageW(infoPtr
->hwndNotify
, WM_MEASUREITEM
, mis
.CtlID
, (LPARAM
)&mis
);
1124 if (infoPtr
->nItemHeight
!= max(mis
.itemHeight
, 1))
1126 infoPtr
->nMeasureItemHeight
= infoPtr
->nItemHeight
= max(mis
.itemHeight
, 1);
1132 /******** Item iterator functions **********************************/
1134 static RANGES
ranges_create(int count
);
1135 static void ranges_destroy(RANGES ranges
);
1136 static BOOL
ranges_add(RANGES ranges
, RANGE range
);
1137 static BOOL
ranges_del(RANGES ranges
, RANGE range
);
1138 static void ranges_dump(RANGES ranges
);
1140 static inline BOOL
ranges_additem(RANGES ranges
, INT nItem
)
1142 RANGE range
= { nItem
, nItem
+ 1 };
1144 return ranges_add(ranges
, range
);
1147 static inline BOOL
ranges_delitem(RANGES ranges
, INT nItem
)
1149 RANGE range
= { nItem
, nItem
+ 1 };
1151 return ranges_del(ranges
, range
);
1155 * ITERATOR DOCUMENTATION
1157 * The iterator functions allow for easy, and convenient iteration
1158 * over items of interest in the list. Typically, you create an
1159 * iterator, use it, and destroy it, as such:
1162 * iterator_xxxitems(&i, ...);
1163 * while (iterator_{prev,next}(&i)
1165 * //code which uses i.nItem
1167 * iterator_destroy(&i);
1169 * where xxx is either: framed, or visible.
1170 * Note that it is important that the code destroys the iterator
1171 * after it's done with it, as the creation of the iterator may
1172 * allocate memory, which thus needs to be freed.
1174 * You can iterate both forwards, and backwards through the list,
1175 * by using iterator_next or iterator_prev respectively.
1177 * Lower numbered items are draw on top of higher number items in
1178 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1179 * items may overlap). So, to test items, you should use
1181 * which lists the items top to bottom (in Z-order).
1182 * For drawing items, you should use
1184 * which lists the items bottom to top (in Z-order).
1185 * If you keep iterating over the items after the end-of-items
1186 * marker (-1) is returned, the iterator will start from the
1187 * beginning. Typically, you don't need to test for -1,
1188 * because iterator_{next,prev} will return TRUE if more items
1189 * are to be iterated over, or FALSE otherwise.
1191 * Note: the iterator is defined to be bidirectional. That is,
1192 * any number of prev followed by any number of next, or
1193 * five versa, should leave the iterator at the same item:
1194 * prev * n, next * n = next * n, prev * n
1196 * The iterator has a notion of an out-of-order, special item,
1197 * which sits at the start of the list. This is used in
1198 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1199 * which needs to be first, as it may overlap other items.
1201 * The code is a bit messy because we have:
1202 * - a special item to deal with
1203 * - simple range, or composite range
1205 * If you find bugs, or want to add features, please make sure you
1206 * always check/modify *both* iterator_prev, and iterator_next.
1210 * This function iterates through the items in increasing order,
1211 * but prefixed by the special item, then -1. That is:
1212 * special, 1, 2, 3, ..., n, -1.
1213 * Each item is listed only once.
1215 static inline BOOL
iterator_next(ITERATOR
* i
)
1219 i
->nItem
= i
->nSpecial
;
1220 if (i
->nItem
!= -1) return TRUE
;
1222 if (i
->nItem
== i
->nSpecial
)
1224 if (i
->ranges
) i
->index
= 0;
1230 if (i
->nItem
== i
->nSpecial
) i
->nItem
++;
1231 if (i
->nItem
< i
->range
.upper
) return TRUE
;
1236 if (i
->index
< DPA_GetPtrCount(i
->ranges
->hdpa
))
1237 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, i
->index
++);
1240 else if (i
->nItem
>= i
->range
.upper
) goto end
;
1242 i
->nItem
= i
->range
.lower
;
1243 if (i
->nItem
>= 0) goto testitem
;
1250 * This function iterates through the items in decreasing order,
1251 * followed by the special item, then -1. That is:
1252 * n, n-1, ..., 3, 2, 1, special, -1.
1253 * Each item is listed only once.
1255 static inline BOOL
iterator_prev(ITERATOR
* i
)
1262 if (i
->ranges
) i
->index
= DPA_GetPtrCount(i
->ranges
->hdpa
);
1265 if (i
->nItem
== i
->nSpecial
)
1273 if (i
->nItem
== i
->nSpecial
) i
->nItem
--;
1274 if (i
->nItem
>= i
->range
.lower
) return TRUE
;
1280 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, --i
->index
);
1283 else if (!start
&& i
->nItem
< i
->range
.lower
) goto end
;
1285 i
->nItem
= i
->range
.upper
;
1286 if (i
->nItem
> 0) goto testitem
;
1288 return (i
->nItem
= i
->nSpecial
) != -1;
1291 static RANGE
iterator_range(const ITERATOR
*i
)
1295 if (!i
->ranges
) return i
->range
;
1297 if (DPA_GetPtrCount(i
->ranges
->hdpa
) > 0)
1299 range
.lower
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, 0)).lower
;
1300 range
.upper
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, DPA_GetPtrCount(i
->ranges
->hdpa
) - 1)).upper
;
1302 else range
.lower
= range
.upper
= 0;
1308 * Releases resources associated with this iterator.
1310 static inline void iterator_destroy(const ITERATOR
*i
)
1312 ranges_destroy(i
->ranges
);
1316 * Create an empty iterator.
1318 static inline void iterator_empty(ITERATOR
* i
)
1320 ZeroMemory(i
, sizeof(*i
));
1321 i
->nItem
= i
->nSpecial
= i
->range
.lower
= i
->range
.upper
= -1;
1325 * Create an iterator over a range.
1327 static inline void iterator_rangeitems(ITERATOR
* i
, RANGE range
)
1334 * Create an iterator over a bunch of ranges.
1335 * Please note that the iterator will take ownership of the ranges,
1336 * and will free them upon destruction.
1338 static inline void iterator_rangesitems(ITERATOR
* i
, RANGES ranges
)
1345 * Creates an iterator over the items which intersect frame.
1346 * Uses absolute coordinates rather than compensating for the current offset.
1348 static BOOL
iterator_frameditems_absolute(ITERATOR
* i
, const LISTVIEW_INFO
* infoPtr
, const RECT
*frame
)
1350 RECT rcItem
, rcTemp
;
1353 TRACE("(frame=%s)\n", wine_dbgstr_rect(frame
));
1355 /* in case we fail, we want to return an empty iterator */
1358 if (infoPtr
->nItemCount
== 0)
1361 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)
1365 if (infoPtr
->uView
== LV_VIEW_ICON
&& infoPtr
->nFocusedItem
!= -1)
1367 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcItem
);
1368 if (IntersectRect(&rcTemp
, &rcItem
, frame
))
1369 i
->nSpecial
= infoPtr
->nFocusedItem
;
1371 if (!(ranges
= ranges_create(50))) return FALSE
;
1372 iterator_rangesitems(i
, ranges
);
1373 /* to do better here, we need to have PosX, and PosY sorted */
1374 TRACE("building icon ranges:\n");
1375 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
1377 rcItem
.left
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1378 rcItem
.top
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1379 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1380 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1381 if (IntersectRect(&rcTemp
, &rcItem
, frame
))
1382 ranges_additem(i
->ranges
, nItem
);
1386 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
1390 if (frame
->left
>= infoPtr
->nItemWidth
) return TRUE
;
1391 if (frame
->top
>= infoPtr
->nItemHeight
* infoPtr
->nItemCount
) return TRUE
;
1393 range
.lower
= max(frame
->top
/ infoPtr
->nItemHeight
, 0);
1394 range
.upper
= min((frame
->bottom
- 1) / infoPtr
->nItemHeight
, infoPtr
->nItemCount
- 1) + 1;
1395 if (range
.upper
<= range
.lower
) return TRUE
;
1396 iterator_rangeitems(i
, range
);
1397 TRACE(" report=%s\n", debugrange(&i
->range
));
1401 INT nPerCol
= max((infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
, 1);
1402 INT nFirstRow
= max(frame
->top
/ infoPtr
->nItemHeight
, 0);
1403 INT nLastRow
= min((frame
->bottom
- 1) / infoPtr
->nItemHeight
, nPerCol
- 1);
1410 if (infoPtr
->nItemWidth
)
1412 nFirstCol
= max(frame
->left
/ infoPtr
->nItemWidth
, 0);
1413 nLastCol
= min((frame
->right
- 1) / infoPtr
->nItemWidth
, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1417 nFirstCol
= max(frame
->left
, 0);
1418 nLastCol
= min(frame
->right
- 1, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1421 lower
= nFirstCol
* nPerCol
+ nFirstRow
;
1423 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1424 nPerCol
, nFirstRow
, nLastRow
, nFirstCol
, nLastCol
, lower
);
1426 if (nLastCol
< nFirstCol
|| nLastRow
< nFirstRow
) return TRUE
;
1428 if (!(ranges
= ranges_create(nLastCol
- nFirstCol
+ 1))) return FALSE
;
1429 iterator_rangesitems(i
, ranges
);
1430 TRACE("building list ranges:\n");
1431 for (nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++)
1433 item_range
.lower
= nCol
* nPerCol
+ nFirstRow
;
1434 if(item_range
.lower
>= infoPtr
->nItemCount
) break;
1435 item_range
.upper
= min(nCol
* nPerCol
+ nLastRow
+ 1, infoPtr
->nItemCount
);
1436 TRACE(" list=%s\n", debugrange(&item_range
));
1437 ranges_add(i
->ranges
, item_range
);
1445 * Creates an iterator over the items which intersect lprc.
1447 static BOOL
iterator_frameditems(ITERATOR
* i
, const LISTVIEW_INFO
* infoPtr
, const RECT
*lprc
)
1452 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc
));
1454 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1455 OffsetRect(&frame
, -Origin
.x
, -Origin
.y
);
1457 return iterator_frameditems_absolute(i
, infoPtr
, &frame
);
1461 * Creates an iterator over the items which intersect the visible region of hdc.
1463 static BOOL
iterator_visibleitems(ITERATOR
*i
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1465 POINT Origin
, Position
;
1466 RECT rcItem
, rcClip
;
1469 rgntype
= GetClipBox(hdc
, &rcClip
);
1470 if (rgntype
== NULLREGION
)
1475 if (!iterator_frameditems(i
, infoPtr
, &rcClip
)) return FALSE
;
1476 if (rgntype
== SIMPLEREGION
) return TRUE
;
1478 /* first deal with the special item */
1479 if (i
->nSpecial
!= -1)
1481 LISTVIEW_GetItemBox(infoPtr
, i
->nSpecial
, &rcItem
);
1482 if (!RectVisible(hdc
, &rcItem
)) i
->nSpecial
= -1;
1485 /* if we can't deal with the region, we'll just go with the simple range */
1486 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1487 TRACE("building visible range:\n");
1488 if (!i
->ranges
&& i
->range
.lower
< i
->range
.upper
)
1490 if (!(i
->ranges
= ranges_create(50))) return TRUE
;
1491 if (!ranges_add(i
->ranges
, i
->range
))
1493 ranges_destroy(i
->ranges
);
1499 /* now delete the invisible items from the list */
1500 while(iterator_next(i
))
1502 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
1503 rcItem
.left
= (infoPtr
->uView
== LV_VIEW_DETAILS
) ? Origin
.x
: Position
.x
+ Origin
.x
;
1504 rcItem
.top
= Position
.y
+ Origin
.y
;
1505 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1506 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1507 if (!RectVisible(hdc
, &rcItem
))
1508 ranges_delitem(i
->ranges
, i
->nItem
);
1510 /* the iterator should restart on the next iterator_next */
1516 /* Remove common elements from two iterators */
1517 /* Passed iterators have to point on the first elements */
1518 static BOOL
iterator_remove_common_items(ITERATOR
*iter1
, ITERATOR
*iter2
)
1520 if(!iter1
->ranges
|| !iter2
->ranges
) {
1523 if(iter1
->ranges
|| iter2
->ranges
||
1524 (iter1
->range
.lower
<iter2
->range
.lower
&& iter1
->range
.upper
>iter2
->range
.upper
) ||
1525 (iter1
->range
.lower
>iter2
->range
.lower
&& iter1
->range
.upper
<iter2
->range
.upper
)) {
1526 ERR("result is not a one range iterator\n");
1530 if(iter1
->range
.lower
==-1 || iter2
->range
.lower
==-1)
1533 lower
= iter1
->range
.lower
;
1534 upper
= iter1
->range
.upper
;
1536 if(lower
< iter2
->range
.lower
)
1537 iter1
->range
.upper
= iter2
->range
.lower
;
1538 else if(upper
> iter2
->range
.upper
)
1539 iter1
->range
.lower
= iter2
->range
.upper
;
1541 iter1
->range
.lower
= iter1
->range
.upper
= -1;
1543 if(iter2
->range
.lower
< lower
)
1544 iter2
->range
.upper
= lower
;
1545 else if(iter2
->range
.upper
> upper
)
1546 iter2
->range
.lower
= upper
;
1548 iter2
->range
.lower
= iter2
->range
.upper
= -1;
1553 iterator_next(iter1
);
1554 iterator_next(iter2
);
1557 if(iter1
->nItem
==-1 || iter2
->nItem
==-1)
1560 if(iter1
->nItem
== iter2
->nItem
) {
1561 int delete = iter1
->nItem
;
1563 iterator_prev(iter1
);
1564 iterator_prev(iter2
);
1565 ranges_delitem(iter1
->ranges
, delete);
1566 ranges_delitem(iter2
->ranges
, delete);
1567 iterator_next(iter1
);
1568 iterator_next(iter2
);
1569 } else if(iter1
->nItem
> iter2
->nItem
)
1570 iterator_next(iter2
);
1572 iterator_next(iter1
);
1575 iter1
->nItem
= iter1
->range
.lower
= iter1
->range
.upper
= -1;
1576 iter2
->nItem
= iter2
->range
.lower
= iter2
->range
.upper
= -1;
1580 /******** Misc helper functions ************************************/
1582 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
1583 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
1585 if (isW
) return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
1586 else return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
1589 static inline BOOL
is_autoarrange(const LISTVIEW_INFO
*infoPtr
)
1591 return (infoPtr
->dwStyle
& LVS_AUTOARRANGE
) &&
1592 (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
);
1595 static void toggle_checkbox_state(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1597 DWORD state
= STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_STATEIMAGEMASK
));
1598 if(state
== 1 || state
== 2)
1602 lvitem
.state
= INDEXTOSTATEIMAGEMASK(state
);
1603 lvitem
.stateMask
= LVIS_STATEIMAGEMASK
;
1604 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvitem
);
1608 /* this should be called after window style got updated,
1609 it used to reset view state to match current window style */
1610 static inline void map_style_view(LISTVIEW_INFO
*infoPtr
)
1612 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
1615 infoPtr
->uView
= LV_VIEW_ICON
;
1618 infoPtr
->uView
= LV_VIEW_DETAILS
;
1621 infoPtr
->uView
= LV_VIEW_SMALLICON
;
1624 infoPtr
->uView
= LV_VIEW_LIST
;
1628 /* computes next item id value */
1629 static DWORD
get_next_itemid(const LISTVIEW_INFO
*infoPtr
)
1631 INT count
= DPA_GetPtrCount(infoPtr
->hdpaItemIds
);
1635 ITEM_ID
*lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, count
- 1);
1636 return lpID
->id
+ 1;
1641 /******** Internal API functions ************************************/
1643 static inline COLUMN_INFO
* LISTVIEW_GetColumnInfo(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
)
1645 static COLUMN_INFO mainItem
;
1647 if (nSubItem
== 0 && DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0) return &mainItem
;
1648 assert (nSubItem
>= 0 && nSubItem
< DPA_GetPtrCount(infoPtr
->hdpaColumns
));
1650 /* update cached column rectangles */
1651 if (infoPtr
->colRectsDirty
)
1654 LISTVIEW_INFO
*Ptr
= (LISTVIEW_INFO
*)infoPtr
;
1657 for (i
= 0; i
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); i
++) {
1658 info
= DPA_GetPtr(infoPtr
->hdpaColumns
, i
);
1659 SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMRECT
, i
, (LPARAM
)&info
->rcHeader
);
1661 Ptr
->colRectsDirty
= FALSE
;
1664 return DPA_GetPtr(infoPtr
->hdpaColumns
, nSubItem
);
1667 static INT
LISTVIEW_CreateHeader(LISTVIEW_INFO
*infoPtr
)
1669 DWORD dFlags
= WS_CHILD
| HDS_HORZ
| HDS_FULLDRAG
| HDS_DRAGDROP
;
1672 if (infoPtr
->hwndHeader
) return 0;
1674 TRACE("Creating header for list %p\n", infoPtr
->hwndSelf
);
1676 /* setup creation flags */
1677 dFlags
|= (LVS_NOSORTHEADER
& infoPtr
->dwStyle
) ? 0 : HDS_BUTTONS
;
1678 dFlags
|= (LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
) ? HDS_HIDDEN
: 0;
1680 hInst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
1683 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
, dFlags
,
1684 0, 0, 0, 0, infoPtr
->hwndSelf
, NULL
, hInst
, NULL
);
1685 if (!infoPtr
->hwndHeader
) return -1;
1687 /* set header unicode format */
1688 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, TRUE
, 0);
1690 /* set header font */
1691 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, TRUE
);
1693 /* set header image list */
1694 if (infoPtr
->himlSmall
)
1695 SendMessageW(infoPtr
->hwndHeader
, HDM_SETIMAGELIST
, 0, (LPARAM
)infoPtr
->himlSmall
);
1697 LISTVIEW_UpdateSize(infoPtr
);
1702 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
, LPRECT lprc
)
1704 *lprc
= LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->rcHeader
;
1707 static inline BOOL
LISTVIEW_IsHeaderEnabled(const LISTVIEW_INFO
*infoPtr
)
1709 return (infoPtr
->uView
== LV_VIEW_DETAILS
||
1710 infoPtr
->dwLvExStyle
& LVS_EX_HEADERINALLVIEWS
) &&
1711 !(infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
);
1714 static inline BOOL
LISTVIEW_GetItemW(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
1716 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
1719 /* used to handle collapse main item column case */
1720 static inline BOOL
LISTVIEW_DrawFocusRect(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1722 return (infoPtr
->rcFocus
.left
< infoPtr
->rcFocus
.right
) ?
1723 DrawFocusRect(hdc
, &infoPtr
->rcFocus
) : FALSE
;
1726 /* Listview invalidation functions: use _only_ these functions to invalidate */
1728 static inline BOOL
is_redrawing(const LISTVIEW_INFO
*infoPtr
)
1730 return infoPtr
->redraw
;
1733 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO
*infoPtr
, const RECT
* rect
)
1735 if(!is_redrawing(infoPtr
)) return;
1736 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect
));
1737 InvalidateRect(infoPtr
->hwndSelf
, rect
, TRUE
);
1740 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
1744 if (!is_redrawing(infoPtr
) || nItem
< 0 || nItem
>= infoPtr
->nItemCount
)
1747 LISTVIEW_GetItemBox(infoPtr
, nItem
, &rcBox
);
1748 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1751 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
)
1753 POINT Origin
, Position
;
1756 if(!is_redrawing(infoPtr
)) return;
1757 assert (infoPtr
->uView
== LV_VIEW_DETAILS
);
1758 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1759 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
1760 LISTVIEW_GetHeaderRect(infoPtr
, nSubItem
, &rcBox
);
1762 rcBox
.bottom
= infoPtr
->nItemHeight
;
1763 OffsetRect(&rcBox
, Origin
.x
, Origin
.y
+ Position
.y
);
1764 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1767 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO
*infoPtr
)
1769 LISTVIEW_InvalidateRect(infoPtr
, NULL
);
1772 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
1776 if(!is_redrawing(infoPtr
)) return;
1777 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
1778 rcCol
.top
= infoPtr
->rcList
.top
;
1779 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
1780 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
1785 * Retrieves the number of items that can fit vertically in the client area.
1788 * [I] infoPtr : valid pointer to the listview structure
1791 * Number of items per row.
1793 static inline INT
LISTVIEW_GetCountPerRow(const LISTVIEW_INFO
*infoPtr
)
1795 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1797 return max(nListWidth
/(infoPtr
->nItemWidth
? infoPtr
->nItemWidth
: 1), 1);
1802 * Retrieves the number of items that can fit horizontally in the client
1806 * [I] infoPtr : valid pointer to the listview structure
1809 * Number of items per column.
1811 static inline INT
LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO
*infoPtr
)
1813 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1815 return infoPtr
->nItemHeight
? max(nListHeight
/ infoPtr
->nItemHeight
, 1) : 0;
1819 /*************************************************************************
1820 * LISTVIEW_ProcessLetterKeys
1822 * Processes keyboard messages generated by pressing the letter keys
1824 * What this does is perform a case insensitive search from the
1825 * current position with the following quirks:
1826 * - If two chars or more are pressed in quick succession we search
1827 * for the corresponding string (e.g. 'abc').
1828 * - If there is a delay we wipe away the current search string and
1829 * restart with just that char.
1830 * - If the user keeps pressing the same character, whether slowly or
1831 * fast, so that the search string is entirely composed of this
1832 * character ('aaaaa' for instance), then we search for first item
1833 * that starting with that character.
1834 * - If the user types the above character in quick succession, then
1835 * we must also search for the corresponding string ('aaaaa'), and
1836 * go to that string if there is a match.
1839 * [I] hwnd : handle to the window
1840 * [I] charCode : the character code, the actual character
1841 * [I] keyData : key data
1849 * - The current implementation has a list of characters it will
1850 * accept and it ignores everything else. In particular it will
1851 * ignore accentuated characters which seems to match what
1852 * Windows does. But I'm not sure it makes sense to follow
1854 * - We don't sound a beep when the search fails.
1858 * TREEVIEW_ProcessLetterKeys
1860 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
1862 WCHAR buffer
[MAX_PATH
];
1869 /* simple parameter checking */
1870 if (!charCode
|| !keyData
|| infoPtr
->nItemCount
== 0) return 0;
1872 /* only allow the valid WM_CHARs through */
1873 if (!isalnumW(charCode
) &&
1874 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
1875 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
1876 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
1877 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
1878 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
1879 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
1880 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
1881 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
1882 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
1885 /* update the search parameters */
1886 prevTime
= infoPtr
->lastKeyPressTimestamp
;
1887 infoPtr
->lastKeyPressTimestamp
= GetTickCount();
1888 diff
= infoPtr
->lastKeyPressTimestamp
- prevTime
;
1890 if (diff
>= 0 && diff
< KEY_DELAY
)
1892 if (infoPtr
->nSearchParamLength
< MAX_PATH
- 1)
1893 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++] = charCode
;
1895 if (infoPtr
->charCode
!= charCode
)
1896 infoPtr
->charCode
= charCode
= 0;
1900 infoPtr
->charCode
= charCode
;
1901 infoPtr
->szSearchParam
[0] = charCode
;
1902 infoPtr
->nSearchParamLength
= 1;
1905 /* should start from next after focused item, so next item that matches
1906 will be selected, if there isn't any and focused matches it will be selected
1907 on second search stage from beginning of the list */
1908 if (infoPtr
->nFocusedItem
>= 0 && infoPtr
->nItemCount
> 1)
1910 /* with some accumulated search data available start with current focus, otherwise
1911 it's excluded from search */
1912 startidx
= infoPtr
->nSearchParamLength
> 1 ? infoPtr
->nFocusedItem
: infoPtr
->nFocusedItem
+ 1;
1913 if (startidx
== infoPtr
->nItemCount
) startidx
= 0;
1918 /* let application handle this for virtual listview */
1919 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
1923 memset(&nmlv
.lvfi
, 0, sizeof(nmlv
.lvfi
));
1924 nmlv
.lvfi
.flags
= (LVFI_WRAP
| LVFI_PARTIAL
);
1925 nmlv
.lvfi
.psz
= infoPtr
->szSearchParam
;
1926 nmlv
.iStart
= startidx
;
1928 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
] = 0;
1930 nItem
= notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
1934 int i
= startidx
, endidx
;
1936 /* and search from the current position */
1938 endidx
= infoPtr
->nItemCount
;
1940 /* first search in [startidx, endidx), on failure continue in [0, startidx) */
1943 /* start from first item if not found with >= startidx */
1944 if (i
== infoPtr
->nItemCount
&& startidx
> 0)
1950 for (i
= startidx
; i
< endidx
; i
++)
1953 item
.mask
= LVIF_TEXT
;
1956 item
.pszText
= buffer
;
1957 item
.cchTextMax
= MAX_PATH
;
1958 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
1960 if (!lstrncmpiW(item
.pszText
, infoPtr
->szSearchParam
, infoPtr
->nSearchParamLength
))
1965 /* this is used to find first char match when search string is not available yet,
1966 otherwise every WM_CHAR will search to next item by first char, ignoring that we're
1967 already waiting for user to complete a string */
1968 else if (nItem
== -1 && infoPtr
->nSearchParamLength
== 1 && !lstrncmpiW(item
.pszText
, infoPtr
->szSearchParam
, 1))
1970 /* this would work but we must keep looking for a longer match */
1975 if ( nItem
!= -1 || /* found something */
1976 endidx
!= infoPtr
->nItemCount
|| /* second search done */
1977 (startidx
== 0 && endidx
== infoPtr
->nItemCount
) /* full range for first search */ )
1983 LISTVIEW_KeySelection(infoPtr
, nItem
, FALSE
);
1988 /*************************************************************************
1989 * LISTVIEW_UpdateHeaderSize [Internal]
1991 * Function to resize the header control
1994 * [I] hwnd : handle to a window
1995 * [I] nNewScrollPos : scroll pos to set
2000 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
2005 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
2007 if (!infoPtr
->hwndHeader
) return;
2009 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
2010 point
[0].x
= winRect
.left
;
2011 point
[0].y
= winRect
.top
;
2012 point
[1].x
= winRect
.right
;
2013 point
[1].y
= winRect
.bottom
;
2015 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
2016 point
[0].x
= -nNewScrollPos
;
2017 point
[1].x
+= nNewScrollPos
;
2019 SetWindowPos(infoPtr
->hwndHeader
,0,
2020 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
2021 (infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
|
2022 SWP_NOZORDER
| SWP_NOACTIVATE
);
2025 static INT
LISTVIEW_UpdateHScroll(LISTVIEW_INFO
*infoPtr
)
2027 SCROLLINFO horzInfo
;
2030 ZeroMemory(&horzInfo
, sizeof(SCROLLINFO
));
2031 horzInfo
.cbSize
= sizeof(SCROLLINFO
);
2032 horzInfo
.nPage
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2034 /* for now, we'll set info.nMax to the _count_, and adjust it later */
2035 if (infoPtr
->uView
== LV_VIEW_LIST
)
2037 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
2038 horzInfo
.nMax
= (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
;
2040 /* scroll by at least one column per page */
2041 if(horzInfo
.nPage
< infoPtr
->nItemWidth
)
2042 horzInfo
.nPage
= infoPtr
->nItemWidth
;
2044 if (infoPtr
->nItemWidth
)
2045 horzInfo
.nPage
/= infoPtr
->nItemWidth
;
2047 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2049 horzInfo
.nMax
= infoPtr
->nItemWidth
;
2051 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2055 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) horzInfo
.nMax
= rcView
.right
- rcView
.left
;
2058 if (LISTVIEW_IsHeaderEnabled(infoPtr
))
2060 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
))
2065 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
2066 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
2068 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcHeader
);
2069 horzInfo
.nMax
= rcHeader
.right
;
2070 TRACE("horzInfo.nMax=%d\n", horzInfo
.nMax
);
2074 horzInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
2075 horzInfo
.nMax
= max(horzInfo
.nMax
- 1, 0);
2076 dx
= GetScrollPos(infoPtr
->hwndSelf
, SB_HORZ
);
2077 dx
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
, TRUE
);
2078 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo
));
2080 /* Update the Header Control */
2081 if (infoPtr
->hwndHeader
)
2083 horzInfo
.fMask
= SIF_POS
;
2084 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
);
2085 LISTVIEW_UpdateHeaderSize(infoPtr
, horzInfo
.nPos
);
2088 LISTVIEW_UpdateSize(infoPtr
);
2092 static INT
LISTVIEW_UpdateVScroll(LISTVIEW_INFO
*infoPtr
)
2094 SCROLLINFO vertInfo
;
2097 ZeroMemory(&vertInfo
, sizeof(SCROLLINFO
));
2098 vertInfo
.cbSize
= sizeof(SCROLLINFO
);
2099 vertInfo
.nPage
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2101 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2103 vertInfo
.nMax
= infoPtr
->nItemCount
;
2105 /* scroll by at least one page */
2106 if(vertInfo
.nPage
< infoPtr
->nItemHeight
)
2107 vertInfo
.nPage
= infoPtr
->nItemHeight
;
2109 if (infoPtr
->nItemHeight
> 0)
2110 vertInfo
.nPage
/= infoPtr
->nItemHeight
;
2112 else if (infoPtr
->uView
!= LV_VIEW_LIST
) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2116 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) vertInfo
.nMax
= rcView
.bottom
- rcView
.top
;
2119 vertInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
2120 vertInfo
.nMax
= max(vertInfo
.nMax
- 1, 0);
2121 dy
= GetScrollPos(infoPtr
->hwndSelf
, SB_VERT
);
2122 dy
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &vertInfo
, TRUE
);
2123 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo
));
2125 LISTVIEW_UpdateSize(infoPtr
);
2131 * Update the scrollbars. This function should be called whenever
2132 * the content, size or view changes.
2135 * [I] infoPtr : valid pointer to the listview structure
2140 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO
*infoPtr
)
2144 if ((infoPtr
->dwStyle
& LVS_NOSCROLL
) || !is_redrawing(infoPtr
)) return;
2146 /* Setting the horizontal scroll can change the listview size
2147 * (and potentially everything else) so we need to recompute
2148 * everything again for the vertical scroll and vice-versa
2150 for (dx
= 0, dy
= 0, pass
= 0; pass
<= 1; pass
++)
2152 dx
+= LISTVIEW_UpdateHScroll(infoPtr
);
2153 dy
+= LISTVIEW_UpdateVScroll(infoPtr
);
2156 /* Change of the range may have changed the scroll pos. If so move the content */
2157 if (dx
!= 0 || dy
!= 0)
2160 listRect
= infoPtr
->rcList
;
2161 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &listRect
, &listRect
, 0, 0,
2162 SW_ERASE
| SW_INVALIDATE
);
2169 * Shows/hides the focus rectangle.
2172 * [I] infoPtr : valid pointer to the listview structure
2173 * [I] fShow : TRUE to show the focus, FALSE to hide it.
2178 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO
*infoPtr
, BOOL fShow
)
2182 TRACE("fShow=%d, nItem=%d\n", fShow
, infoPtr
->nFocusedItem
);
2184 if (infoPtr
->nFocusedItem
< 0) return;
2186 /* we need some gymnastics in ICON mode to handle large items */
2187 if (infoPtr
->uView
== LV_VIEW_ICON
)
2191 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcBox
);
2192 if ((rcBox
.bottom
- rcBox
.top
) > infoPtr
->nItemHeight
)
2194 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
2199 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) return;
2201 /* for some reason, owner draw should work only in report mode */
2202 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
2207 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2208 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2210 item
.iItem
= infoPtr
->nFocusedItem
;
2212 item
.mask
= LVIF_PARAM
;
2213 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto done
;
2215 ZeroMemory(&dis
, sizeof(dis
));
2216 dis
.CtlType
= ODT_LISTVIEW
;
2217 dis
.CtlID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
2218 dis
.itemID
= item
.iItem
;
2219 dis
.itemAction
= ODA_FOCUS
;
2220 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
2221 dis
.hwndItem
= infoPtr
->hwndSelf
;
2223 LISTVIEW_GetItemBox(infoPtr
, dis
.itemID
, &dis
.rcItem
);
2224 dis
.itemData
= item
.lParam
;
2226 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
2228 SelectObject(hdc
, hOldFont
);
2231 LISTVIEW_InvalidateItem(infoPtr
, infoPtr
->nFocusedItem
);
2234 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2238 * Invalidates all visible selected items.
2240 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO
*infoPtr
)
2244 iterator_frameditems(&i
, infoPtr
, &infoPtr
->rcList
);
2245 while(iterator_next(&i
))
2247 if (LISTVIEW_GetItemState(infoPtr
, i
.nItem
, LVIS_SELECTED
))
2248 LISTVIEW_InvalidateItem(infoPtr
, i
.nItem
);
2250 iterator_destroy(&i
);
2255 * DESCRIPTION: [INTERNAL]
2256 * Computes an item's (left,top) corner, relative to rcView.
2257 * That is, the position has NOT been made relative to the Origin.
2258 * This is deliberate, to avoid computing the Origin over, and
2259 * over again, when this function is called in a loop. Instead,
2260 * one can factor the computation of the Origin before the loop,
2261 * and offset the value returned by this function, on every iteration.
2264 * [I] infoPtr : valid pointer to the listview structure
2265 * [I] nItem : item number
2266 * [O] lpptOrig : item top, left corner
2271 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
2273 assert(nItem
>= 0 && nItem
< infoPtr
->nItemCount
);
2275 if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
2277 lpptPosition
->x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2278 lpptPosition
->y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2280 else if (infoPtr
->uView
== LV_VIEW_LIST
)
2282 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
2283 lpptPosition
->x
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
2284 lpptPosition
->y
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
2286 else /* LV_VIEW_DETAILS */
2288 lpptPosition
->x
= REPORT_MARGINX
;
2289 /* item is always at zero indexed column */
2290 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2291 lpptPosition
->x
+= LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
;
2292 lpptPosition
->y
= nItem
* infoPtr
->nItemHeight
;
2297 * DESCRIPTION: [INTERNAL]
2298 * Compute the rectangles of an item. This is to localize all
2299 * the computations in one place. If you are not interested in some
2300 * of these values, simply pass in a NULL -- the function is smart
2301 * enough to compute only what's necessary. The function computes
2302 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
2303 * one, the BOX rectangle. This rectangle is very cheap to compute,
2304 * and is guaranteed to contain all the other rectangles. Computing
2305 * the ICON rect is also cheap, but all the others are potentially
2306 * expensive. This gives an easy and effective optimization when
2307 * searching (like point inclusion, or rectangle intersection):
2308 * first test against the BOX, and if TRUE, test against the desired
2310 * If the function does not have all the necessary information
2311 * to computed the requested rectangles, will crash with a
2312 * failed assertion. This is done so we catch all programming
2313 * errors, given that the function is called only from our code.
2315 * We have the following 'special' meanings for a few fields:
2316 * * If LVIS_FOCUSED is set, we assume the item has the focus
2317 * This is important in ICON mode, where it might get a larger
2318 * then usual rectangle
2320 * Please note that subitem support works only in REPORT mode.
2323 * [I] infoPtr : valid pointer to the listview structure
2324 * [I] lpLVItem : item to compute the measures for
2325 * [O] lprcBox : ptr to Box rectangle
2326 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
2327 * [0] lprcSelectBox : ptr to select box rectangle
2328 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
2329 * [O] lprcIcon : ptr to Icon rectangle
2330 * Same as LVM_GETITEMRECT with LVIR_ICON
2331 * [O] lprcStateIcon: ptr to State Icon rectangle
2332 * [O] lprcLabel : ptr to Label rectangle
2333 * Same as LVM_GETITEMRECT with LVIR_LABEL
2338 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
,
2339 LPRECT lprcBox
, LPRECT lprcSelectBox
,
2340 LPRECT lprcIcon
, LPRECT lprcStateIcon
, LPRECT lprcLabel
)
2342 BOOL doSelectBox
= FALSE
, doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
2343 RECT Box
, SelectBox
, Icon
, Label
;
2344 COLUMN_INFO
*lpColumnInfo
= NULL
;
2345 SIZE labelSize
= { 0, 0 };
2347 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem
, TRUE
));
2349 /* Be smart and try to figure out the minimum we have to do */
2350 if (lpLVItem
->iSubItem
) assert(infoPtr
->uView
== LV_VIEW_DETAILS
);
2351 if (infoPtr
->uView
== LV_VIEW_ICON
&& (lprcBox
|| lprcLabel
))
2353 assert((lpLVItem
->mask
& LVIF_STATE
) && (lpLVItem
->stateMask
& LVIS_FOCUSED
));
2354 if (lpLVItem
->state
& LVIS_FOCUSED
) oversizedBox
= doLabel
= TRUE
;
2356 if (lprcSelectBox
) doSelectBox
= TRUE
;
2357 if (lprcLabel
) doLabel
= TRUE
;
2358 if (doLabel
|| lprcIcon
|| lprcStateIcon
) doIcon
= TRUE
;
2365 /************************************************************/
2366 /* compute the box rectangle (it should be cheap to do) */
2367 /************************************************************/
2368 if (lpLVItem
->iSubItem
|| infoPtr
->uView
== LV_VIEW_DETAILS
)
2369 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpLVItem
->iSubItem
);
2371 if (lpLVItem
->iSubItem
)
2373 Box
= lpColumnInfo
->rcHeader
;
2378 Box
.right
= infoPtr
->nItemWidth
;
2381 Box
.bottom
= infoPtr
->nItemHeight
;
2383 /******************************************************************/
2384 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2385 /******************************************************************/
2388 LONG state_width
= 0;
2390 if (infoPtr
->himlState
&& lpLVItem
->iSubItem
== 0)
2391 state_width
= infoPtr
->iconStateSize
.cx
;
2393 if (infoPtr
->uView
== LV_VIEW_ICON
)
2395 Icon
.left
= Box
.left
+ state_width
;
2396 if (infoPtr
->himlNormal
)
2397 Icon
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
- state_width
) / 2;
2398 Icon
.top
= Box
.top
+ ICON_TOP_PADDING
;
2399 Icon
.right
= Icon
.left
;
2400 Icon
.bottom
= Icon
.top
;
2401 if (infoPtr
->himlNormal
)
2403 Icon
.right
+= infoPtr
->iconSize
.cx
;
2404 Icon
.bottom
+= infoPtr
->iconSize
.cy
;
2407 else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2409 Icon
.left
= Box
.left
+ state_width
;
2411 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& lpLVItem
->iSubItem
== 0)
2413 /* we need the indent in report mode */
2414 assert(lpLVItem
->mask
& LVIF_INDENT
);
2415 Icon
.left
+= infoPtr
->iconSize
.cx
* lpLVItem
->iIndent
+ REPORT_MARGINX
;
2419 Icon
.right
= Icon
.left
;
2420 if (infoPtr
->himlSmall
&&
2421 (!lpColumnInfo
|| lpLVItem
->iSubItem
== 0 ||
2422 ((infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
) && lpLVItem
->iImage
!= I_IMAGECALLBACK
)))
2423 Icon
.right
+= infoPtr
->iconSize
.cx
;
2424 Icon
.bottom
= Icon
.top
+ infoPtr
->iconSize
.cy
;
2426 if(lprcIcon
) *lprcIcon
= Icon
;
2427 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon
));
2429 /* TODO: is this correct? */
2432 lprcStateIcon
->left
= Icon
.left
- state_width
;
2433 lprcStateIcon
->right
= Icon
.left
;
2434 lprcStateIcon
->top
= Icon
.top
;
2435 lprcStateIcon
->bottom
= lprcStateIcon
->top
+ infoPtr
->iconSize
.cy
;
2436 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon
));
2439 else Icon
.right
= 0;
2441 /************************************************************/
2442 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2443 /************************************************************/
2446 /* calculate how far to the right can the label stretch */
2447 Label
.right
= Box
.right
;
2448 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2450 if (lpLVItem
->iSubItem
== 0)
2452 /* we need a zero based rect here */
2453 Label
= lpColumnInfo
->rcHeader
;
2454 OffsetRect(&Label
, -Label
.left
, 0);
2458 if (lpLVItem
->iSubItem
|| ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && infoPtr
->uView
== LV_VIEW_DETAILS
))
2460 labelSize
.cx
= infoPtr
->nItemWidth
;
2461 labelSize
.cy
= infoPtr
->nItemHeight
;
2465 /* we need the text in non owner draw mode */
2466 assert(lpLVItem
->mask
& LVIF_TEXT
);
2467 if (is_text(lpLVItem
->pszText
))
2469 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2470 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2471 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2475 /* compute rough rectangle where the label will go */
2476 SetRectEmpty(&rcText
);
2477 rcText
.right
= infoPtr
->nItemWidth
- TRAILING_LABEL_PADDING
;
2478 rcText
.bottom
= infoPtr
->nItemHeight
;
2479 if (infoPtr
->uView
== LV_VIEW_ICON
)
2480 rcText
.bottom
-= ICON_TOP_PADDING
+ infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2482 /* now figure out the flags */
2483 if (infoPtr
->uView
== LV_VIEW_ICON
)
2484 uFormat
= oversizedBox
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
;
2486 uFormat
= LV_SL_DT_FLAGS
;
2488 DrawTextW (hdc
, lpLVItem
->pszText
, -1, &rcText
, uFormat
| DT_CALCRECT
);
2490 if (rcText
.right
!= rcText
.left
)
2491 labelSize
.cx
= min(rcText
.right
- rcText
.left
+ TRAILING_LABEL_PADDING
, infoPtr
->nItemWidth
);
2493 labelSize
.cy
= rcText
.bottom
- rcText
.top
;
2495 SelectObject(hdc
, hOldFont
);
2496 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2500 if (infoPtr
->uView
== LV_VIEW_ICON
)
2502 Label
.left
= Box
.left
+ (infoPtr
->nItemWidth
- labelSize
.cx
) / 2;
2503 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
2504 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2505 Label
.right
= Label
.left
+ labelSize
.cx
;
2506 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2507 if (!oversizedBox
&& labelSize
.cy
> infoPtr
->ntmHeight
)
2509 labelSize
.cy
= min(Box
.bottom
- Label
.top
, labelSize
.cy
);
2510 labelSize
.cy
/= infoPtr
->ntmHeight
;
2511 labelSize
.cy
= max(labelSize
.cy
, 1);
2512 labelSize
.cy
*= infoPtr
->ntmHeight
;
2514 Label
.bottom
= Label
.top
+ labelSize
.cy
+ HEIGHT_PADDING
;
2516 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2518 Label
.left
= Icon
.right
;
2519 Label
.top
= Box
.top
;
2520 Label
.right
= lpLVItem
->iSubItem
? lpColumnInfo
->rcHeader
.right
:
2521 lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
2522 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2524 else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2526 Label
.left
= Icon
.right
;
2527 Label
.top
= Box
.top
;
2528 Label
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2529 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2532 if (lprcLabel
) *lprcLabel
= Label
;
2533 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label
));
2536 /************************************************************/
2537 /* compute SELECT bounding box */
2538 /************************************************************/
2541 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2543 SelectBox
.left
= Icon
.left
;
2544 SelectBox
.top
= Box
.top
;
2545 SelectBox
.bottom
= Box
.bottom
;
2548 SelectBox
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2550 SelectBox
.right
= min(Label
.left
+ MAX_EMPTYTEXT_SELECT_WIDTH
, Label
.right
);
2554 UnionRect(&SelectBox
, &Icon
, &Label
);
2556 if (lprcSelectBox
) *lprcSelectBox
= SelectBox
;
2557 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox
));
2560 /* Fix the Box if necessary */
2563 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
2564 else *lprcBox
= Box
;
2566 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box
));
2570 * DESCRIPTION: [INTERNAL]
2573 * [I] infoPtr : valid pointer to the listview structure
2574 * [I] nItem : item number
2575 * [O] lprcBox : ptr to Box rectangle
2580 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprcBox
)
2582 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2583 POINT Position
, Origin
;
2586 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
2587 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
2589 /* Be smart and try to figure out the minimum we have to do */
2591 if (infoPtr
->uView
== LV_VIEW_ICON
&& infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
2592 lvItem
.mask
|= LVIF_TEXT
;
2593 lvItem
.iItem
= nItem
;
2594 lvItem
.iSubItem
= 0;
2595 lvItem
.pszText
= szDispText
;
2596 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2597 if (lvItem
.mask
) LISTVIEW_GetItemW(infoPtr
, &lvItem
);
2598 if (infoPtr
->uView
== LV_VIEW_ICON
)
2600 lvItem
.mask
|= LVIF_STATE
;
2601 lvItem
.stateMask
= LVIS_FOCUSED
;
2602 lvItem
.state
= (lvItem
.mask
& LVIF_TEXT
? LVIS_FOCUSED
: 0);
2604 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprcBox
, 0, 0, 0, 0);
2606 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
&&
2607 SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, 0, 0))
2609 OffsetRect(lprcBox
, Origin
.x
, Position
.y
+ Origin
.y
);
2612 OffsetRect(lprcBox
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
2615 /* LISTVIEW_MapIdToIndex helper */
2616 static INT CALLBACK
MapIdSearchCompare(LPVOID p1
, LPVOID p2
, LPARAM lParam
)
2618 ITEM_ID
*id1
= (ITEM_ID
*)p1
;
2619 ITEM_ID
*id2
= (ITEM_ID
*)p2
;
2621 if (id1
->id
== id2
->id
) return 0;
2623 return (id1
->id
< id2
->id
) ? -1 : 1;
2628 * Returns the item index for id specified.
2631 * [I] infoPtr : valid pointer to the listview structure
2632 * [I] iID : item id to get index for
2635 * Item index, or -1 on failure.
2637 static INT
LISTVIEW_MapIdToIndex(const LISTVIEW_INFO
*infoPtr
, UINT iID
)
2642 TRACE("iID=%d\n", iID
);
2644 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return -1;
2645 if (infoPtr
->nItemCount
== 0) return -1;
2648 index
= DPA_Search(infoPtr
->hdpaItemIds
, &ID
, -1, MapIdSearchCompare
, 0, DPAS_SORTED
);
2652 ITEM_ID
*lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, index
);
2653 return DPA_GetPtrIndex(infoPtr
->hdpaItems
, lpID
->item
);
2661 * Returns the item id for index given.
2664 * [I] infoPtr : valid pointer to the listview structure
2665 * [I] iItem : item index to get id for
2670 static DWORD
LISTVIEW_MapIndexToId(const LISTVIEW_INFO
*infoPtr
, INT iItem
)
2675 TRACE("iItem=%d\n", iItem
);
2677 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return -1;
2678 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
) return -1;
2680 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, iItem
);
2681 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
2683 return lpItem
->id
->id
;
2688 * Returns the current icon position, and advances it along the top.
2689 * The returned position is not offset by Origin.
2692 * [I] infoPtr : valid pointer to the listview structure
2693 * [O] lpPos : will get the current icon position
2698 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2700 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2702 *lpPos
= infoPtr
->currIconPos
;
2704 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2705 if (infoPtr
->currIconPos
.x
+ infoPtr
->nItemWidth
<= nListWidth
) return;
2707 infoPtr
->currIconPos
.x
= 0;
2708 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2714 * Returns the current icon position, and advances it down the left edge.
2715 * The returned position is not offset by Origin.
2718 * [I] infoPtr : valid pointer to the listview structure
2719 * [O] lpPos : will get the current icon position
2724 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2726 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2728 *lpPos
= infoPtr
->currIconPos
;
2730 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2731 if (infoPtr
->currIconPos
.y
+ infoPtr
->nItemHeight
<= nListHeight
) return;
2733 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2734 infoPtr
->currIconPos
.y
= 0;
2740 * Moves an icon to the specified position.
2741 * It takes care of invalidating the item, etc.
2744 * [I] infoPtr : valid pointer to the listview structure
2745 * [I] nItem : the item to move
2746 * [I] lpPos : the new icon position
2747 * [I] isNew : flags the item as being new
2753 static BOOL
LISTVIEW_MoveIconTo(const LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*lppt
, BOOL isNew
)
2759 old
.x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2760 old
.y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2762 if (lppt
->x
== old
.x
&& lppt
->y
== old
.y
) return TRUE
;
2763 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2766 /* Allocating a POINTER for every item is too resource intensive,
2767 * so we'll keep the (x,y) in different arrays */
2768 if (!DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)(LONG_PTR
)lppt
->x
)) return FALSE
;
2769 if (!DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)(LONG_PTR
)lppt
->y
)) return FALSE
;
2771 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2778 * Arranges listview items in icon display mode.
2781 * [I] infoPtr : valid pointer to the listview structure
2782 * [I] nAlignCode : alignment code
2788 static BOOL
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
2790 void (*next_pos
)(LISTVIEW_INFO
*, LPPOINT
);
2794 if (infoPtr
->uView
!= LV_VIEW_ICON
&& infoPtr
->uView
!= LV_VIEW_SMALLICON
) return FALSE
;
2796 TRACE("nAlignCode=%d\n", nAlignCode
);
2798 if (nAlignCode
== LVA_DEFAULT
)
2800 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
) nAlignCode
= LVA_ALIGNLEFT
;
2801 else nAlignCode
= LVA_ALIGNTOP
;
2806 case LVA_ALIGNLEFT
: next_pos
= LISTVIEW_NextIconPosLeft
; break;
2807 case LVA_ALIGNTOP
: next_pos
= LISTVIEW_NextIconPosTop
; break;
2808 case LVA_SNAPTOGRID
: next_pos
= LISTVIEW_NextIconPosTop
; break; /* FIXME */
2809 default: return FALSE
;
2812 infoPtr
->currIconPos
.x
= infoPtr
->currIconPos
.y
= 0;
2813 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2815 next_pos(infoPtr
, &pos
);
2816 LISTVIEW_MoveIconTo(infoPtr
, i
, &pos
, FALSE
);
2824 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2825 * For LVS_REPORT always returns empty rectangle.
2828 * [I] infoPtr : valid pointer to the listview structure
2829 * [O] lprcView : bounding rectangle
2835 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2839 SetRectEmpty(lprcView
);
2841 switch (infoPtr
->uView
)
2844 case LV_VIEW_SMALLICON
:
2845 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2847 x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, i
);
2848 y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, i
);
2849 lprcView
->right
= max(lprcView
->right
, x
);
2850 lprcView
->bottom
= max(lprcView
->bottom
, y
);
2852 if (infoPtr
->nItemCount
> 0)
2854 lprcView
->right
+= infoPtr
->nItemWidth
;
2855 lprcView
->bottom
+= infoPtr
->nItemHeight
;
2860 y
= LISTVIEW_GetCountPerColumn(infoPtr
);
2861 x
= infoPtr
->nItemCount
/ y
;
2862 if (infoPtr
->nItemCount
% y
) x
++;
2863 lprcView
->right
= x
* infoPtr
->nItemWidth
;
2864 lprcView
->bottom
= y
* infoPtr
->nItemHeight
;
2871 * Retrieves the bounding rectangle of all the items.
2874 * [I] infoPtr : valid pointer to the listview structure
2875 * [O] lprcView : bounding rectangle
2881 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2885 TRACE("(lprcView=%p)\n", lprcView
);
2887 if (!lprcView
) return FALSE
;
2889 LISTVIEW_GetAreaRect(infoPtr
, lprcView
);
2891 if (infoPtr
->uView
!= LV_VIEW_DETAILS
)
2893 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
2894 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
2897 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView
));
2904 * Retrieves the subitem pointer associated with the subitem index.
2907 * [I] hdpaSubItems : DPA handle for a specific item
2908 * [I] nSubItem : index of subitem
2911 * SUCCESS : subitem pointer
2914 static SUBITEM_INFO
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
, INT nSubItem
)
2916 SUBITEM_INFO
*lpSubItem
;
2919 /* we should binary search here if need be */
2920 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
2922 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
2923 if (lpSubItem
->iSubItem
== nSubItem
)
2933 * Calculates the desired item width.
2936 * [I] infoPtr : valid pointer to the listview structure
2939 * The desired item width.
2941 static INT
LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO
*infoPtr
)
2945 TRACE("uView=%d\n", infoPtr
->uView
);
2947 if (infoPtr
->uView
== LV_VIEW_ICON
)
2948 nItemWidth
= infoPtr
->iconSpacing
.cx
;
2949 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2951 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2956 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
2957 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
2959 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcHeader
);
2960 nItemWidth
= rcHeader
.right
;
2963 else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
2965 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2969 lvItem
.mask
= LVIF_TEXT
;
2970 lvItem
.iSubItem
= 0;
2972 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2975 lvItem
.pszText
= szDispText
;
2976 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2977 if (LISTVIEW_GetItemW(infoPtr
, &lvItem
))
2978 nItemWidth
= max(LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
),
2982 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
2983 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
2985 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
+ WIDTH_PADDING
);
2993 * Calculates the desired item height.
2996 * [I] infoPtr : valid pointer to the listview structure
2999 * The desired item height.
3001 static INT
LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO
*infoPtr
)
3005 TRACE("uView=%d\n", infoPtr
->uView
);
3007 if (infoPtr
->uView
== LV_VIEW_ICON
)
3008 nItemHeight
= infoPtr
->iconSpacing
.cy
;
3011 nItemHeight
= infoPtr
->ntmHeight
;
3012 if (infoPtr
->himlState
)
3013 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
3014 if (infoPtr
->himlSmall
)
3015 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
3016 nItemHeight
+= HEIGHT_PADDING
;
3017 if (infoPtr
->nMeasureItemHeight
> 0)
3018 nItemHeight
= infoPtr
->nMeasureItemHeight
;
3021 return max(nItemHeight
, 1);
3026 * Updates the width, and height of an item.
3029 * [I] infoPtr : valid pointer to the listview structure
3034 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO
*infoPtr
)
3036 infoPtr
->nItemWidth
= LISTVIEW_CalculateItemWidth(infoPtr
);
3037 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
3043 * Retrieves and saves important text metrics info for the current
3047 * [I] infoPtr : valid pointer to the listview structure
3050 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
3052 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
3053 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
3054 HFONT hOldFont
= SelectObject(hdc
, hFont
);
3058 if (GetTextMetricsW(hdc
, &tm
))
3060 infoPtr
->ntmHeight
= tm
.tmHeight
;
3061 infoPtr
->ntmMaxCharWidth
= tm
.tmMaxCharWidth
;
3064 if (GetTextExtentPoint32A(hdc
, "...", 3, &sz
))
3065 infoPtr
->nEllipsisWidth
= sz
.cx
;
3067 SelectObject(hdc
, hOldFont
);
3068 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
3070 TRACE("tmHeight=%d\n", infoPtr
->ntmHeight
);
3075 * A compare function for ranges
3078 * [I] range1 : pointer to range 1;
3079 * [I] range2 : pointer to range 2;
3083 * > 0 : if range 1 > range 2
3084 * < 0 : if range 2 > range 1
3085 * = 0 : if range intersects range 2
3087 static INT CALLBACK
ranges_cmp(LPVOID range1
, LPVOID range2
, LPARAM flags
)
3091 if (((RANGE
*)range1
)->upper
<= ((RANGE
*)range2
)->lower
)
3093 else if (((RANGE
*)range2
)->upper
<= ((RANGE
*)range1
)->lower
)
3098 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1
), debugrange(range2
), cmp
);
3103 #define ranges_check(ranges, desc) if (TRACE_ON(listview)) ranges_assert(ranges, desc, __FILE__, __LINE__)
3105 static void ranges_assert(RANGES ranges
, LPCSTR desc
, const char *file
, int line
)
3110 TRACE("*** Checking %s:%d:%s ***\n", file
, line
, desc
);
3112 assert (DPA_GetPtrCount(ranges
->hdpa
) >= 0);
3113 ranges_dump(ranges
);
3114 if (DPA_GetPtrCount(ranges
->hdpa
) > 0)
3116 prev
= DPA_GetPtr(ranges
->hdpa
, 0);
3117 assert (prev
->lower
>= 0 && prev
->lower
< prev
->upper
);
3118 for (i
= 1; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3120 curr
= DPA_GetPtr(ranges
->hdpa
, i
);
3121 assert (prev
->upper
<= curr
->lower
);
3122 assert (curr
->lower
< curr
->upper
);
3126 TRACE("--- Done checking---\n");
3129 static RANGES
ranges_create(int count
)
3131 RANGES ranges
= Alloc(sizeof(struct tagRANGES
));
3132 if (!ranges
) return NULL
;
3133 ranges
->hdpa
= DPA_Create(count
);
3134 if (ranges
->hdpa
) return ranges
;
3139 static void ranges_clear(RANGES ranges
)
3143 for(i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3144 Free(DPA_GetPtr(ranges
->hdpa
, i
));
3145 DPA_DeleteAllPtrs(ranges
->hdpa
);
3149 static void ranges_destroy(RANGES ranges
)
3151 if (!ranges
) return;
3152 ranges_clear(ranges
);
3153 DPA_Destroy(ranges
->hdpa
);
3157 static RANGES
ranges_clone(RANGES ranges
)
3162 if (!(clone
= ranges_create(DPA_GetPtrCount(ranges
->hdpa
)))) goto fail
;
3164 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3166 RANGE
*newrng
= Alloc(sizeof(RANGE
));
3167 if (!newrng
) goto fail
;
3168 *newrng
= *((RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
));
3169 if (!DPA_SetPtr(clone
->hdpa
, i
, newrng
))
3178 TRACE ("clone failed\n");
3179 ranges_destroy(clone
);
3183 static RANGES
ranges_diff(RANGES ranges
, RANGES sub
)
3187 for (i
= 0; i
< DPA_GetPtrCount(sub
->hdpa
); i
++)
3188 ranges_del(ranges
, *((RANGE
*)DPA_GetPtr(sub
->hdpa
, i
)));
3193 static void ranges_dump(RANGES ranges
)
3197 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3198 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges
->hdpa
, i
)));
3201 static inline BOOL
ranges_contain(RANGES ranges
, INT nItem
)
3203 RANGE srchrng
= { nItem
, nItem
+ 1 };
3205 TRACE("(nItem=%d)\n", nItem
);
3206 ranges_check(ranges
, "before contain");
3207 return DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
) != -1;
3210 static INT
ranges_itemcount(RANGES ranges
)
3214 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3216 RANGE
*sel
= DPA_GetPtr(ranges
->hdpa
, i
);
3217 count
+= sel
->upper
- sel
->lower
;
3223 static BOOL
ranges_shift(RANGES ranges
, INT nItem
, INT delta
, INT nUpper
)
3225 RANGE srchrng
= { nItem
, nItem
+ 1 }, *chkrng
;
3228 index
= DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
3229 if (index
== -1) return TRUE
;
3231 for (; index
< DPA_GetPtrCount(ranges
->hdpa
); index
++)
3233 chkrng
= DPA_GetPtr(ranges
->hdpa
, index
);
3234 if (chkrng
->lower
>= nItem
)
3235 chkrng
->lower
= max(min(chkrng
->lower
+ delta
, nUpper
- 1), 0);
3236 if (chkrng
->upper
> nItem
)
3237 chkrng
->upper
= max(min(chkrng
->upper
+ delta
, nUpper
), 0);
3242 static BOOL
ranges_add(RANGES ranges
, RANGE range
)
3247 TRACE("(%s)\n", debugrange(&range
));
3248 ranges_check(ranges
, "before add");
3250 /* try find overlapping regions first */
3251 srchrgn
.lower
= range
.lower
- 1;
3252 srchrgn
.upper
= range
.upper
+ 1;
3253 index
= DPA_Search(ranges
->hdpa
, &srchrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
);
3259 TRACE("Adding new range\n");
3261 /* create the brand new range to insert */
3262 newrgn
= Alloc(sizeof(RANGE
));
3263 if(!newrgn
) goto fail
;
3266 /* figure out where to insert it */
3267 index
= DPA_Search(ranges
->hdpa
, newrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
3268 TRACE("index=%d\n", index
);
3269 if (index
== -1) index
= 0;
3271 /* and get it over with */
3272 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
3280 RANGE
*chkrgn
, *mrgrgn
;
3281 INT fromindex
, mergeindex
;
3283 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
3284 TRACE("Merge with %s @%d\n", debugrange(chkrgn
), index
);
3286 chkrgn
->lower
= min(range
.lower
, chkrgn
->lower
);
3287 chkrgn
->upper
= max(range
.upper
, chkrgn
->upper
);
3289 TRACE("New range %s @%d\n", debugrange(chkrgn
), index
);
3291 /* merge now common ranges */
3293 srchrgn
.lower
= chkrgn
->lower
- 1;
3294 srchrgn
.upper
= chkrgn
->upper
+ 1;
3298 mergeindex
= DPA_Search(ranges
->hdpa
, &srchrgn
, fromindex
, ranges_cmp
, 0, 0);
3299 if (mergeindex
== -1) break;
3300 if (mergeindex
== index
)
3302 fromindex
= index
+ 1;
3306 TRACE("Merge with index %i\n", mergeindex
);
3308 mrgrgn
= DPA_GetPtr(ranges
->hdpa
, mergeindex
);
3309 chkrgn
->lower
= min(chkrgn
->lower
, mrgrgn
->lower
);
3310 chkrgn
->upper
= max(chkrgn
->upper
, mrgrgn
->upper
);
3312 DPA_DeletePtr(ranges
->hdpa
, mergeindex
);
3313 if (mergeindex
< index
) index
--;
3317 ranges_check(ranges
, "after add");
3321 ranges_check(ranges
, "failed add");
3325 static BOOL
ranges_del(RANGES ranges
, RANGE range
)
3330 TRACE("(%s)\n", debugrange(&range
));
3331 ranges_check(ranges
, "before del");
3333 /* we don't use DPAS_SORTED here, since we need *
3334 * to find the first overlapping range */
3335 index
= DPA_Search(ranges
->hdpa
, &range
, 0, ranges_cmp
, 0, 0);
3338 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
3340 TRACE("Matches range %s @%d\n", debugrange(chkrgn
), index
);
3342 /* case 1: Same range */
3343 if ( (chkrgn
->upper
== range
.upper
) &&
3344 (chkrgn
->lower
== range
.lower
) )
3346 DPA_DeletePtr(ranges
->hdpa
, index
);
3350 /* case 2: engulf */
3351 else if ( (chkrgn
->upper
<= range
.upper
) &&
3352 (chkrgn
->lower
>= range
.lower
) )
3354 DPA_DeletePtr(ranges
->hdpa
, index
);
3357 /* case 3: overlap upper */
3358 else if ( (chkrgn
->upper
<= range
.upper
) &&
3359 (chkrgn
->lower
< range
.lower
) )
3361 chkrgn
->upper
= range
.lower
;
3363 /* case 4: overlap lower */
3364 else if ( (chkrgn
->upper
> range
.upper
) &&
3365 (chkrgn
->lower
>= range
.lower
) )
3367 chkrgn
->lower
= range
.upper
;
3370 /* case 5: fully internal */
3375 if (!(newrgn
= Alloc(sizeof(RANGE
)))) goto fail
;
3376 newrgn
->lower
= chkrgn
->lower
;
3377 newrgn
->upper
= range
.lower
;
3378 chkrgn
->lower
= range
.upper
;
3379 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
3387 index
= DPA_Search(ranges
->hdpa
, &range
, index
, ranges_cmp
, 0, 0);
3390 ranges_check(ranges
, "after del");
3394 ranges_check(ranges
, "failed del");
3400 * Removes all selection ranges
3403 * [I] infoPtr : valid pointer to the listview structure
3404 * [I] toSkip : item range to skip removing the selection
3410 static BOOL
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO
*infoPtr
, RANGES toSkip
)
3419 lvItem
.stateMask
= LVIS_SELECTED
;
3421 /* need to clone the DPA because callbacks can change it */
3422 if (!(clone
= ranges_clone(infoPtr
->selectionRanges
))) return FALSE
;
3423 iterator_rangesitems(&i
, ranges_diff(clone
, toSkip
));
3424 while(iterator_next(&i
))
3425 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &lvItem
);
3426 /* note that the iterator destructor will free the cloned range */
3427 iterator_destroy(&i
);
3432 static inline BOOL
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3436 if (!(toSkip
= ranges_create(1))) return FALSE
;
3437 if (nItem
!= -1) ranges_additem(toSkip
, nItem
);
3438 LISTVIEW_DeselectAllSkipItems(infoPtr
, toSkip
);
3439 ranges_destroy(toSkip
);
3443 static inline BOOL
LISTVIEW_DeselectAll(LISTVIEW_INFO
*infoPtr
)
3445 return LISTVIEW_DeselectAllSkipItem(infoPtr
, -1);
3450 * Retrieves the number of items that are marked as selected.
3453 * [I] infoPtr : valid pointer to the listview structure
3456 * Number of items selected.
3458 static INT
LISTVIEW_GetSelectedCount(const LISTVIEW_INFO
*infoPtr
)
3460 INT nSelectedCount
= 0;
3462 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3465 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
3467 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
3472 nSelectedCount
= ranges_itemcount(infoPtr
->selectionRanges
);
3474 TRACE("nSelectedCount=%d\n", nSelectedCount
);
3475 return nSelectedCount
;
3480 * Manages the item focus.
3483 * [I] infoPtr : valid pointer to the listview structure
3484 * [I] nItem : item index
3487 * TRUE : focused item changed
3488 * FALSE : focused item has NOT changed
3490 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3492 INT oldFocus
= infoPtr
->nFocusedItem
;
3495 if (nItem
== infoPtr
->nFocusedItem
) return FALSE
;
3497 lvItem
.state
= nItem
== -1 ? 0 : LVIS_FOCUSED
;
3498 lvItem
.stateMask
= LVIS_FOCUSED
;
3499 LISTVIEW_SetItemState(infoPtr
, nItem
== -1 ? infoPtr
->nFocusedItem
: nItem
, &lvItem
);
3501 return oldFocus
!= infoPtr
->nFocusedItem
;
3504 static INT
shift_item(const LISTVIEW_INFO
*infoPtr
, INT nShiftItem
, INT nItem
, INT direction
)
3506 if (nShiftItem
< nItem
) return nShiftItem
;
3508 if (nShiftItem
> nItem
) return nShiftItem
+ direction
;
3510 if (direction
> 0) return nShiftItem
+ direction
;
3512 return min(nShiftItem
, infoPtr
->nItemCount
- 1);
3515 /* This function updates focus index.
3518 focus : current focus index
3519 item : index of item to be added/removed
3520 direction : add/remove flag
3522 static void LISTVIEW_ShiftFocus(LISTVIEW_INFO
*infoPtr
, INT focus
, INT item
, INT direction
)
3524 DWORD old_mask
= infoPtr
->notify_mask
& NOTIFY_MASK_ITEM_CHANGE
;
3526 infoPtr
->notify_mask
&= ~NOTIFY_MASK_ITEM_CHANGE
;
3527 focus
= shift_item(infoPtr
, focus
, item
, direction
);
3528 if (focus
!= infoPtr
->nFocusedItem
)
3529 LISTVIEW_SetItemFocus(infoPtr
, focus
);
3530 infoPtr
->notify_mask
|= old_mask
;
3535 * Updates the various indices after an item has been inserted or deleted.
3538 * [I] infoPtr : valid pointer to the listview structure
3539 * [I] nItem : item index
3540 * [I] direction : Direction of shift, +1 or -1.
3545 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
3547 TRACE("Shifting %i, %i steps\n", nItem
, direction
);
3549 ranges_shift(infoPtr
->selectionRanges
, nItem
, direction
, infoPtr
->nItemCount
);
3550 assert(abs(direction
) == 1);
3551 infoPtr
->nSelectionMark
= shift_item(infoPtr
, infoPtr
->nSelectionMark
, nItem
, direction
);
3553 /* But we are not supposed to modify nHotItem! */
3558 * Adds a block of selections.
3561 * [I] infoPtr : valid pointer to the listview structure
3562 * [I] nItem : item index
3565 * Whether the window is still valid.
3567 static BOOL
LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3569 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
3570 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
3571 HWND hwndSelf
= infoPtr
->hwndSelf
;
3572 NMLVODSTATECHANGE nmlv
;
3577 /* Temporarily disable change notification
3578 * If the control is LVS_OWNERDATA, we need to send
3579 * only one LVN_ODSTATECHANGED notification.
3580 * See MSDN documentation for LVN_ITEMCHANGED.
3582 old_mask
= infoPtr
->notify_mask
& NOTIFY_MASK_ITEM_CHANGE
;
3583 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
3584 infoPtr
->notify_mask
&= ~NOTIFY_MASK_ITEM_CHANGE
;
3586 if (nFirst
== -1) nFirst
= nItem
;
3588 item
.state
= LVIS_SELECTED
;
3589 item
.stateMask
= LVIS_SELECTED
;
3591 for (i
= nFirst
; i
<= nLast
; i
++)
3592 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
3594 ZeroMemory(&nmlv
, sizeof(nmlv
));
3595 nmlv
.iFrom
= nFirst
;
3598 nmlv
.uNewState
= item
.state
;
3600 notify_hdr(infoPtr
, LVN_ODSTATECHANGED
, (LPNMHDR
)&nmlv
);
3601 if (!IsWindow(hwndSelf
))
3603 infoPtr
->notify_mask
|= old_mask
;
3610 * Sets a single group selection.
3613 * [I] infoPtr : valid pointer to the listview structure
3614 * [I] nItem : item index
3619 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3626 if (!(selection
= ranges_create(100))) return;
3628 item
.state
= LVIS_SELECTED
;
3629 item
.stateMask
= LVIS_SELECTED
;
3631 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
3633 if (infoPtr
->nSelectionMark
== -1)
3635 infoPtr
->nSelectionMark
= nItem
;
3636 ranges_additem(selection
, nItem
);
3642 sel
.lower
= min(infoPtr
->nSelectionMark
, nItem
);
3643 sel
.upper
= max(infoPtr
->nSelectionMark
, nItem
) + 1;
3644 ranges_add(selection
, sel
);
3649 RECT rcItem
, rcSel
, rcSelMark
;
3652 rcItem
.left
= LVIR_BOUNDS
;
3653 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) {
3654 ranges_destroy (selection
);
3657 rcSelMark
.left
= LVIR_BOUNDS
;
3658 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) {
3659 ranges_destroy (selection
);
3662 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
3663 iterator_frameditems(&i
, infoPtr
, &rcSel
);
3664 while(iterator_next(&i
))
3666 LISTVIEW_GetItemPosition(infoPtr
, i
.nItem
, &ptItem
);
3667 if (PtInRect(&rcSel
, ptItem
)) ranges_additem(selection
, i
.nItem
);
3669 iterator_destroy(&i
);
3672 /* disable per item notifications on LVS_OWNERDATA style
3673 FIXME: single LVN_ODSTATECHANGED should be used */
3674 old_mask
= infoPtr
->notify_mask
& NOTIFY_MASK_ITEM_CHANGE
;
3675 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
3676 infoPtr
->notify_mask
&= ~NOTIFY_MASK_ITEM_CHANGE
;
3678 LISTVIEW_DeselectAllSkipItems(infoPtr
, selection
);
3681 iterator_rangesitems(&i
, selection
);
3682 while(iterator_next(&i
))
3683 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &item
);
3684 /* this will also destroy the selection */
3685 iterator_destroy(&i
);
3687 infoPtr
->notify_mask
|= old_mask
;
3688 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3693 * Sets a single selection.
3696 * [I] infoPtr : valid pointer to the listview structure
3697 * [I] nItem : item index
3702 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3706 TRACE("nItem=%d\n", nItem
);
3708 LISTVIEW_DeselectAllSkipItem(infoPtr
, nItem
);
3710 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3711 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3712 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3714 infoPtr
->nSelectionMark
= nItem
;
3719 * Set selection(s) with keyboard.
3722 * [I] infoPtr : valid pointer to the listview structure
3723 * [I] nItem : item index
3724 * [I] space : VK_SPACE code sent
3727 * SUCCESS : TRUE (needs to be repainted)
3728 * FAILURE : FALSE (nothing has changed)
3730 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL space
)
3732 /* FIXME: pass in the state */
3733 WORD wShift
= GetKeyState(VK_SHIFT
) & 0x8000;
3734 WORD wCtrl
= GetKeyState(VK_CONTROL
) & 0x8000;
3735 BOOL bResult
= FALSE
;
3737 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem
, wShift
, wCtrl
);
3738 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3742 if (infoPtr
->dwStyle
& LVS_SINGLESEL
|| (wShift
== 0 && wCtrl
== 0))
3743 LISTVIEW_SetSelection(infoPtr
, nItem
);
3747 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3751 lvItem
.state
= ~LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3752 lvItem
.stateMask
= LVIS_SELECTED
;
3755 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3756 if (lvItem
.state
& LVIS_SELECTED
)
3757 infoPtr
->nSelectionMark
= nItem
;
3759 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3762 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3765 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3769 static BOOL
LISTVIEW_GetItemAtPt(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, POINT pt
)
3771 LVHITTESTINFO lvHitTestInfo
;
3773 ZeroMemory(&lvHitTestInfo
, sizeof(lvHitTestInfo
));
3774 lvHitTestInfo
.pt
.x
= pt
.x
;
3775 lvHitTestInfo
.pt
.y
= pt
.y
;
3777 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
3779 lpLVItem
->mask
= LVIF_PARAM
;
3780 lpLVItem
->iItem
= lvHitTestInfo
.iItem
;
3781 lpLVItem
->iSubItem
= 0;
3783 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
3786 static inline BOOL
LISTVIEW_IsHotTracking(const LISTVIEW_INFO
*infoPtr
)
3788 return ((infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) ||
3789 (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
) ||
3790 (infoPtr
->dwLvExStyle
& LVS_EX_TWOCLICKACTIVATE
));
3795 * Called when the mouse is being actively tracked and has hovered for a specified
3799 * [I] infoPtr : valid pointer to the listview structure
3800 * [I] fwKeys : key indicator
3801 * [I] x,y : mouse position
3804 * 0 if the message was processed, non-zero if there was an error
3807 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3808 * over the item for a certain period of time.
3811 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, INT x
, INT y
)
3815 if (notify_hdr(infoPtr
, NM_HOVER
, &hdr
)) return 0;
3817 if (LISTVIEW_IsHotTracking(infoPtr
))
3825 if (LISTVIEW_GetItemAtPt(infoPtr
, &item
, pt
))
3826 LISTVIEW_SetSelection(infoPtr
, item
.iItem
);
3828 SetFocus(infoPtr
->hwndSelf
);
3834 #define SCROLL_LEFT 0x1
3835 #define SCROLL_RIGHT 0x2
3836 #define SCROLL_UP 0x4
3837 #define SCROLL_DOWN 0x8
3841 * Utility routine to draw and highlight items within a marquee selection rectangle.
3844 * [I] infoPtr : valid pointer to the listview structure
3845 * [I] coords_orig : original co-ordinates of the cursor
3846 * [I] coords_offs : offsetted coordinates of the cursor
3847 * [I] offset : offset amount
3848 * [I] scroll : Bitmask of which directions we should scroll, if at all
3853 static void LISTVIEW_MarqueeHighlight(LISTVIEW_INFO
*infoPtr
, const POINT
*coords_orig
,
3856 BOOL controlDown
= FALSE
;
3858 ITERATOR old_elems
, new_elems
;
3860 POINT coords_offs
, offset
;
3862 /* Ensure coordinates are within client bounds */
3863 coords_offs
.x
= max(min(coords_orig
->x
, infoPtr
->rcList
.right
), 0);
3864 coords_offs
.y
= max(min(coords_orig
->y
, infoPtr
->rcList
.bottom
), 0);
3867 LISTVIEW_GetOrigin(infoPtr
, &offset
);
3869 /* Offset coordinates by the appropriate amount */
3870 coords_offs
.x
-= offset
.x
;
3871 coords_offs
.y
-= offset
.y
;
3873 if (coords_offs
.x
> infoPtr
->marqueeOrigin
.x
)
3875 rect
.left
= infoPtr
->marqueeOrigin
.x
;
3876 rect
.right
= coords_offs
.x
;
3880 rect
.left
= coords_offs
.x
;
3881 rect
.right
= infoPtr
->marqueeOrigin
.x
;
3884 if (coords_offs
.y
> infoPtr
->marqueeOrigin
.y
)
3886 rect
.top
= infoPtr
->marqueeOrigin
.y
;
3887 rect
.bottom
= coords_offs
.y
;
3891 rect
.top
= coords_offs
.y
;
3892 rect
.bottom
= infoPtr
->marqueeOrigin
.y
;
3895 /* Cancel out the old marquee rectangle and draw the new one */
3896 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeDrawRect
);
3898 /* Scroll by the appropriate distance if applicable - speed up scrolling as
3899 the cursor is further away */
3901 if ((scroll
& SCROLL_LEFT
) && (coords_orig
->x
<= 0))
3902 LISTVIEW_Scroll(infoPtr
, coords_orig
->x
, 0);
3904 if ((scroll
& SCROLL_RIGHT
) && (coords_orig
->x
>= infoPtr
->rcList
.right
))
3905 LISTVIEW_Scroll(infoPtr
, (coords_orig
->x
- infoPtr
->rcList
.right
), 0);
3907 if ((scroll
& SCROLL_UP
) && (coords_orig
->y
<= 0))
3908 LISTVIEW_Scroll(infoPtr
, 0, coords_orig
->y
);
3910 if ((scroll
& SCROLL_DOWN
) && (coords_orig
->y
>= infoPtr
->rcList
.bottom
))
3911 LISTVIEW_Scroll(infoPtr
, 0, (coords_orig
->y
- infoPtr
->rcList
.bottom
));
3913 iterator_frameditems_absolute(&old_elems
, infoPtr
, &infoPtr
->marqueeRect
);
3915 infoPtr
->marqueeRect
= rect
;
3916 infoPtr
->marqueeDrawRect
= rect
;
3917 OffsetRect(&infoPtr
->marqueeDrawRect
, offset
.x
, offset
.y
);
3919 iterator_frameditems_absolute(&new_elems
, infoPtr
, &infoPtr
->marqueeRect
);
3920 iterator_remove_common_items(&old_elems
, &new_elems
);
3922 /* Iterate over no longer selected items */
3923 while (iterator_next(&old_elems
))
3925 if (old_elems
.nItem
> -1)
3927 if (LISTVIEW_GetItemState(infoPtr
, old_elems
.nItem
, LVIS_SELECTED
) == LVIS_SELECTED
)
3930 item
.state
= LVIS_SELECTED
;
3932 item
.stateMask
= LVIS_SELECTED
;
3934 LISTVIEW_SetItemState(infoPtr
, old_elems
.nItem
, &item
);
3937 iterator_destroy(&old_elems
);
3940 /* Iterate over newly selected items */
3941 if (GetKeyState(VK_CONTROL
) & 0x8000)
3944 while (iterator_next(&new_elems
))
3946 if (new_elems
.nItem
> -1)
3948 /* If CTRL is pressed, invert. If not, always select the item. */
3949 if ((controlDown
) && (LISTVIEW_GetItemState(infoPtr
, new_elems
.nItem
, LVIS_SELECTED
)))
3952 item
.state
= LVIS_SELECTED
;
3954 item
.stateMask
= LVIS_SELECTED
;
3956 LISTVIEW_SetItemState(infoPtr
, new_elems
.nItem
, &item
);
3959 iterator_destroy(&new_elems
);
3961 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeDrawRect
);
3966 * Called when we are in a marquee selection that involves scrolling the listview (ie,
3967 * the cursor is outside the bounds of the client area). This is a TIMERPROC.
3970 * [I] hwnd : Handle to the listview
3971 * [I] uMsg : WM_TIMER (ignored)
3972 * [I] idEvent : The timer ID interpreted as a pointer to a LISTVIEW_INFO struct
3973 * [I] dwTimer : The elapsed time (ignored)
3978 static VOID CALLBACK
LISTVIEW_ScrollTimer(HWND hWnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
3980 LISTVIEW_INFO
*infoPtr
;
3981 SCROLLINFO scrollInfo
;
3985 infoPtr
= (LISTVIEW_INFO
*) idEvent
;
3990 /* Get the current cursor position and convert to client coordinates */
3991 GetCursorPos(&coords
);
3992 ScreenToClient(hWnd
, &coords
);
3994 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3995 scrollInfo
.fMask
= SIF_ALL
;
3997 /* Work out in which directions we can scroll */
3998 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
4000 if (scrollInfo
.nPos
!= scrollInfo
.nMin
)
4001 scroll
|= SCROLL_UP
;
4003 if (((scrollInfo
.nPage
+ scrollInfo
.nPos
) - 1) != scrollInfo
.nMax
)
4004 scroll
|= SCROLL_DOWN
;
4007 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
4009 if (scrollInfo
.nPos
!= scrollInfo
.nMin
)
4010 scroll
|= SCROLL_LEFT
;
4012 if (((scrollInfo
.nPage
+ scrollInfo
.nPos
) - 1) != scrollInfo
.nMax
)
4013 scroll
|= SCROLL_RIGHT
;
4016 if (((coords
.x
<= 0) && (scroll
& SCROLL_LEFT
)) ||
4017 ((coords
.y
<= 0) && (scroll
& SCROLL_UP
)) ||
4018 ((coords
.x
>= infoPtr
->rcList
.right
) && (scroll
& SCROLL_RIGHT
)) ||
4019 ((coords
.y
>= infoPtr
->rcList
.bottom
) && (scroll
& SCROLL_DOWN
)))
4021 LISTVIEW_MarqueeHighlight(infoPtr
, &coords
, scroll
);
4027 * Called whenever WM_MOUSEMOVE is received.
4030 * [I] infoPtr : valid pointer to the listview structure
4031 * [I] fwKeys : key indicator
4032 * [I] x,y : mouse position
4035 * 0 if the message is processed, non-zero if there was an error
4037 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
4046 if (!(fwKeys
& MK_LBUTTON
))
4047 infoPtr
->bLButtonDown
= FALSE
;
4049 if (infoPtr
->bLButtonDown
)
4051 rect
.left
= rect
.right
= infoPtr
->ptClickPos
.x
;
4052 rect
.top
= rect
.bottom
= infoPtr
->ptClickPos
.y
;
4054 InflateRect(&rect
, GetSystemMetrics(SM_CXDRAG
), GetSystemMetrics(SM_CYDRAG
));
4056 if (infoPtr
->bMarqueeSelect
)
4058 /* Enable the timer if we're going outside our bounds, in case the user doesn't
4059 move the mouse again */
4061 if ((x
<= 0) || (y
<= 0) || (x
>= infoPtr
->rcList
.right
) ||
4062 (y
>= infoPtr
->rcList
.bottom
))
4064 if (!infoPtr
->bScrolling
)
4066 infoPtr
->bScrolling
= TRUE
;
4067 SetTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
, 1, LISTVIEW_ScrollTimer
);
4072 infoPtr
->bScrolling
= FALSE
;
4073 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
);
4076 LISTVIEW_MarqueeHighlight(infoPtr
, &pt
, 0);
4081 LISTVIEW_HitTest(infoPtr
, &ht
, TRUE
, TRUE
);
4083 /* reset item marker */
4084 if (infoPtr
->nLButtonDownItem
!= ht
.iItem
)
4085 infoPtr
->nLButtonDownItem
= -1;
4087 if (!PtInRect(&rect
, pt
))
4089 /* this path covers the following:
4090 1. WM_LBUTTONDOWN over selected item (sets focus on it)
4091 2. change focus with keys
4092 3. move mouse over item from step 1 selects it and moves focus on it */
4093 if (infoPtr
->nLButtonDownItem
!= -1 &&
4094 !LISTVIEW_GetItemState(infoPtr
, infoPtr
->nLButtonDownItem
, LVIS_SELECTED
))
4098 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
4099 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
4101 LISTVIEW_SetItemState(infoPtr
, infoPtr
->nLButtonDownItem
, &lvItem
);
4102 infoPtr
->nLButtonDownItem
= -1;
4105 if (!infoPtr
->bDragging
)
4107 ht
.pt
= infoPtr
->ptClickPos
;
4108 LISTVIEW_HitTest(infoPtr
, &ht
, TRUE
, TRUE
);
4110 /* If the click is outside the range of an item, begin a
4111 highlight. If not, begin an item drag. */
4116 /* If we're allowing multiple selections, send notification.
4117 If return value is non-zero, cancel. */
4118 if (!(infoPtr
->dwStyle
& LVS_SINGLESEL
) && (notify_hdr(infoPtr
, LVN_MARQUEEBEGIN
, &hdr
) == 0))
4120 /* Store the absolute coordinates of the click */
4122 LISTVIEW_GetOrigin(infoPtr
, &offset
);
4124 infoPtr
->marqueeOrigin
.x
= infoPtr
->ptClickPos
.x
- offset
.x
;
4125 infoPtr
->marqueeOrigin
.y
= infoPtr
->ptClickPos
.y
- offset
.y
;
4127 /* Begin selection and capture mouse */
4128 infoPtr
->bMarqueeSelect
= TRUE
;
4129 SetCapture(infoPtr
->hwndSelf
);
4136 ZeroMemory(&nmlv
, sizeof(nmlv
));
4137 nmlv
.iItem
= ht
.iItem
;
4138 nmlv
.ptAction
= infoPtr
->ptClickPos
;
4140 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
4141 infoPtr
->bDragging
= TRUE
;
4149 /* see if we are supposed to be tracking mouse hovering */
4150 if (LISTVIEW_IsHotTracking(infoPtr
)) {
4151 TRACKMOUSEEVENT trackinfo
;
4154 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
4155 trackinfo
.dwFlags
= TME_QUERY
;
4157 /* see if we are already tracking this hwnd */
4158 _TrackMouseEvent(&trackinfo
);
4161 if(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)
4164 if((trackinfo
.dwFlags
& flags
) != flags
|| trackinfo
.hwndTrack
!= infoPtr
->hwndSelf
) {
4165 trackinfo
.dwFlags
= flags
;
4166 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
4167 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
4169 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
4170 _TrackMouseEvent(&trackinfo
);
4179 * Tests whether the item is assignable to a list with style lStyle
4181 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
4183 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
4184 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
4185 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
4193 * Helper for LISTVIEW_SetItemT and LISTVIEW_InsertItemT: sets item attributes.
4196 * [I] infoPtr : valid pointer to the listview structure
4197 * [I] lpLVItem : valid pointer to new item attributes
4198 * [I] isNew : the item being set is being inserted
4199 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4200 * [O] bChanged : will be set to TRUE if the item really changed
4206 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
4212 /* stateMask is ignored for LVM_INSERTITEM */
4213 UINT stateMask
= isNew
? ~0 : lpLVItem
->stateMask
;
4217 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
4219 if (lpLVItem
->mask
== 0) return TRUE
;
4221 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4223 /* a virtual listview only stores selection and focus */
4224 if (lpLVItem
->mask
& ~LVIF_STATE
)
4230 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
4231 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
4235 /* we need to get the lParam and state of the item */
4236 item
.iItem
= lpLVItem
->iItem
;
4237 item
.iSubItem
= lpLVItem
->iSubItem
;
4238 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
4239 item
.stateMask
= (infoPtr
->dwStyle
& LVS_OWNERDATA
) ? LVIS_FOCUSED
| LVIS_SELECTED
: ~0;
4243 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
4245 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
4246 /* determine what fields will change */
4247 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & stateMask
& ~infoPtr
->uCallbackMask
))
4248 uChanged
|= LVIF_STATE
;
4250 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
4251 uChanged
|= LVIF_IMAGE
;
4253 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
4254 uChanged
|= LVIF_PARAM
;
4256 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
4257 uChanged
|= LVIF_INDENT
;
4259 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
4260 uChanged
|= LVIF_TEXT
;
4262 TRACE("change mask=0x%x\n", uChanged
);
4264 memset(&nmlv
, 0, sizeof(NMLISTVIEW
));
4265 nmlv
.iItem
= lpLVItem
->iItem
;
4266 nmlv
.uNewState
= (item
.state
& ~stateMask
) | (lpLVItem
->state
& stateMask
);
4267 nmlv
.uOldState
= item
.state
;
4268 nmlv
.uChanged
= uChanged
? uChanged
: lpLVItem
->mask
;
4269 nmlv
.lParam
= item
.lParam
;
4271 /* Send LVN_ITEMCHANGING notification, if the item is not being inserted
4272 and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications
4273 are enabled. Even nothing really changed we still need to send this,
4274 in this case uChanged mask is just set to passed item mask. */
4275 if (lpItem
&& !isNew
&& (infoPtr
->notify_mask
& NOTIFY_MASK_ITEM_CHANGE
))
4277 HWND hwndSelf
= infoPtr
->hwndSelf
;
4279 if (notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
4281 if (!IsWindow(hwndSelf
))
4285 /* When item is inserted we need to shift existing focus index if new item has lower index. */
4286 if (isNew
&& (stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
) &&
4287 /* this means we won't hit a focus change path later */
4288 ((uChanged
& LVIF_STATE
) == 0 || (!(lpLVItem
->state
& LVIS_FOCUSED
) && (infoPtr
->nFocusedItem
!= lpLVItem
->iItem
))))
4290 if (infoPtr
->nFocusedItem
!= -1 && (lpLVItem
->iItem
<= infoPtr
->nFocusedItem
))
4291 infoPtr
->nFocusedItem
++;
4294 if (!uChanged
) return TRUE
;
4297 /* copy information */
4298 if (lpLVItem
->mask
& LVIF_TEXT
)
4299 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
4301 if (lpLVItem
->mask
& LVIF_IMAGE
)
4302 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
4304 if (lpLVItem
->mask
& LVIF_PARAM
)
4305 lpItem
->lParam
= lpLVItem
->lParam
;
4307 if (lpLVItem
->mask
& LVIF_INDENT
)
4308 lpItem
->iIndent
= lpLVItem
->iIndent
;
4310 if (uChanged
& LVIF_STATE
)
4312 if (lpItem
&& (stateMask
& ~infoPtr
->uCallbackMask
))
4314 lpItem
->state
&= ~stateMask
;
4315 lpItem
->state
|= (lpLVItem
->state
& stateMask
);
4317 if (lpLVItem
->state
& stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
4319 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
4320 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
4322 else if (stateMask
& LVIS_SELECTED
)
4324 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
4326 /* If we are asked to change focus, and we manage it, do it.
4327 It's important to have all new item data stored at this point,
4328 because changing existing focus could result in a redrawing operation,
4329 which in turn could ask for disp data, application should see all data
4330 for inserted item when processing LVN_GETDISPINFO.
4332 The way this works application will see nested item change notifications -
4333 changed item notifications interrupted by ones from item losing focus. */
4334 if (stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
4336 if (lpLVItem
->state
& LVIS_FOCUSED
)
4338 /* update selection mark */
4339 if (infoPtr
->nFocusedItem
== -1 && infoPtr
->nSelectionMark
== -1)
4340 infoPtr
->nSelectionMark
= lpLVItem
->iItem
;
4342 if (infoPtr
->nFocusedItem
!= -1)
4344 /* remove current focus */
4345 item
.mask
= LVIF_STATE
;
4347 item
.stateMask
= LVIS_FOCUSED
;
4349 /* recurse with redrawing an item */
4350 LISTVIEW_SetItemState(infoPtr
, infoPtr
->nFocusedItem
, &item
);
4353 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
4354 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, infoPtr
->uView
== LV_VIEW_LIST
);
4356 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
4358 infoPtr
->nFocusedItem
= -1;
4363 /* if we're inserting the item, we're done */
4364 if (isNew
) return TRUE
;
4366 /* send LVN_ITEMCHANGED notification */
4367 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
4368 if (infoPtr
->notify_mask
& NOTIFY_MASK_ITEM_CHANGE
)
4369 notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
4376 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
4379 * [I] infoPtr : valid pointer to the listview structure
4380 * [I] lpLVItem : valid pointer to new subitem attributes
4381 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4382 * [O] bChanged : will be set to TRUE if the item really changed
4388 static BOOL
set_sub_item(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
4391 SUBITEM_INFO
*lpSubItem
;
4393 /* we do not support subitems for virtual listviews */
4394 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
4396 /* set subitem only if column is present */
4397 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4399 /* First do some sanity checks */
4400 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
4401 particularly useful. We currently do not actually do anything with
4402 the flag on subitems.
4404 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
| LVIF_DI_SETITEM
)) return FALSE
;
4405 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
))) return TRUE
;
4407 /* get the subitem structure, and create it if not there */
4408 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
4409 assert (hdpaSubItems
);
4411 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
4414 SUBITEM_INFO
*tmpSubItem
;
4417 lpSubItem
= Alloc(sizeof(SUBITEM_INFO
));
4418 if (!lpSubItem
) return FALSE
;
4419 /* we could binary search here, if need be...*/
4420 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4422 tmpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
4423 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
4425 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
4430 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
4431 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
4435 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
))
4437 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
4441 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
4443 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
4452 * Sets item attributes.
4455 * [I] infoPtr : valid pointer to the listview structure
4456 * [I] lpLVItem : new item attributes
4457 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4463 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, LVITEMW
*lpLVItem
, BOOL isW
)
4465 HWND hwndSelf
= infoPtr
->hwndSelf
;
4466 LPWSTR pszText
= NULL
;
4467 BOOL bResult
, bChanged
= FALSE
;
4470 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
4472 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
4475 /* Store old item area */
4476 LISTVIEW_GetItemBox(infoPtr
, lpLVItem
->iItem
, &oldItemArea
);
4478 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
4479 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_text(lpLVItem
->pszText
))
4481 pszText
= lpLVItem
->pszText
;
4482 lpLVItem
->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
4485 /* actually set the fields */
4486 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
4488 if (lpLVItem
->iSubItem
)
4489 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
4491 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
4492 if (!IsWindow(hwndSelf
))
4495 /* redraw item, if necessary */
4496 if (bChanged
&& !infoPtr
->bIsDrawing
)
4498 /* this little optimization eliminates some nasty flicker */
4499 if ( infoPtr
->uView
== LV_VIEW_DETAILS
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
4500 !(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) &&
4501 lpLVItem
->iSubItem
> 0 && lpLVItem
->iSubItem
<= DPA_GetPtrCount(infoPtr
->hdpaColumns
) )
4502 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
4505 LISTVIEW_InvalidateRect(infoPtr
, &oldItemArea
);
4506 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
4512 textfreeT(lpLVItem
->pszText
, isW
);
4513 lpLVItem
->pszText
= pszText
;
4521 * Retrieves the index of the item at coordinate (0, 0) of the client area.
4524 * [I] infoPtr : valid pointer to the listview structure
4529 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*infoPtr
)
4532 SCROLLINFO scrollInfo
;
4534 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
4535 scrollInfo
.fMask
= SIF_POS
;
4537 if (infoPtr
->uView
== LV_VIEW_LIST
)
4539 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
4540 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
4542 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
4544 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
4545 nItem
= scrollInfo
.nPos
;
4549 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
4550 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
4553 TRACE("nItem=%d\n", nItem
);
4561 * Erases the background of the given rectangle
4564 * [I] infoPtr : valid pointer to the listview structure
4565 * [I] hdc : device context handle
4566 * [I] lprcBox : clipping rectangle
4572 static inline BOOL
LISTVIEW_FillBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
4574 if (!infoPtr
->hBkBrush
) return FALSE
;
4576 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, wine_dbgstr_rect(lprcBox
), infoPtr
->hBkBrush
);
4578 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
4581 /* Draw main item or subitem */
4582 static void LISTVIEW_DrawItemPart(LISTVIEW_INFO
*infoPtr
, LVITEMW
*item
, const NMLVCUSTOMDRAW
*nmlvcd
, const POINT
*pos
)
4584 RECT rcSelect
, rcLabel
, rcBox
, rcStateIcon
, rcIcon
;
4585 const RECT
*background
;
4590 /* now check if we need to update the focus rectangle */
4591 focus
= infoPtr
->bFocus
&& (item
->state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
4592 if (!focus
) item
->state
&= ~LVIS_FOCUSED
;
4594 LISTVIEW_GetItemMetrics(infoPtr
, item
, &rcBox
, &rcSelect
, &rcIcon
, &rcStateIcon
, &rcLabel
);
4595 OffsetRect(&rcBox
, pos
->x
, pos
->y
);
4596 OffsetRect(&rcSelect
, pos
->x
, pos
->y
);
4597 OffsetRect(&rcIcon
, pos
->x
, pos
->y
);
4598 OffsetRect(&rcStateIcon
, pos
->x
, pos
->y
);
4599 OffsetRect(&rcLabel
, pos
->x
, pos
->y
);
4600 TRACE("%d: box=%s, select=%s, icon=%s. label=%s\n", item
->iSubItem
,
4601 wine_dbgstr_rect(&rcBox
), wine_dbgstr_rect(&rcSelect
),
4602 wine_dbgstr_rect(&rcIcon
), wine_dbgstr_rect(&rcLabel
));
4604 /* FIXME: temporary hack */
4605 rcSelect
.left
= rcLabel
.left
;
4607 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& item
->iSubItem
== 0)
4609 if (!(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
4610 OffsetRect(&rcSelect
, LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
, 0);
4611 OffsetRect(&rcIcon
, LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
, 0);
4612 OffsetRect(&rcStateIcon
, LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
, 0);
4613 OffsetRect(&rcLabel
, LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
, 0);
4616 /* in icon mode, the label rect is really what we want to draw the
4618 /* in detail mode, we want to paint background for label rect when
4619 * item is not selected or listview has full row select; otherwise paint
4620 * background for text only */
4621 if ( infoPtr
->uView
== LV_VIEW_ICON
||
4622 (infoPtr
->uView
== LV_VIEW_DETAILS
&& (!(item
->state
& LVIS_SELECTED
) ||
4623 (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))))
4624 background
= &rcLabel
;
4626 background
= &rcSelect
;
4628 if (nmlvcd
->clrTextBk
!= CLR_NONE
)
4629 ExtTextOutW(nmlvcd
->nmcd
.hdc
, background
->left
, background
->top
, ETO_OPAQUE
, background
, NULL
, 0, NULL
);
4631 if (item
->state
& LVIS_FOCUSED
)
4633 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
4635 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4637 /* we have to update left focus bound too if item isn't in leftmost column
4638 and reduce right box bound */
4639 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
4643 if ((leftmost
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, 0, 0)))
4645 INT Originx
= pos
->x
- LISTVIEW_GetColumnInfo(infoPtr
, leftmost
)->rcHeader
.left
;
4646 INT rightmost
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
4647 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
4649 rcBox
.right
= LISTVIEW_GetColumnInfo(infoPtr
, rightmost
)->rcHeader
.right
+ Originx
;
4650 rcSelect
.left
= LISTVIEW_GetColumnInfo(infoPtr
, leftmost
)->rcHeader
.left
+ Originx
;
4653 rcSelect
.right
= rcBox
.right
;
4655 infoPtr
->rcFocus
= rcSelect
;
4658 infoPtr
->rcFocus
= rcLabel
;
4662 if (infoPtr
->himlState
&& STATEIMAGEINDEX(item
->state
) && (item
->iSubItem
== 0))
4664 UINT stateimage
= STATEIMAGEINDEX(item
->state
);
4667 TRACE("stateimage=%d\n", stateimage
);
4668 ImageList_Draw(infoPtr
->himlState
, stateimage
-1, nmlvcd
->nmcd
.hdc
, rcStateIcon
.left
, rcStateIcon
.top
, ILD_NORMAL
);
4673 himl
= (infoPtr
->uView
== LV_VIEW_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
4674 if (himl
&& item
->iImage
>= 0 && !IsRectEmpty(&rcIcon
))
4678 TRACE("iImage=%d\n", item
->iImage
);
4680 if (item
->state
& (LVIS_SELECTED
| LVIS_CUT
) && infoPtr
->bFocus
)
4681 style
= ILD_SELECTED
;
4685 ImageList_DrawEx(himl
, item
->iImage
, nmlvcd
->nmcd
.hdc
, rcIcon
.left
, rcIcon
.top
,
4686 rcIcon
.right
- rcIcon
.left
, rcIcon
.bottom
- rcIcon
.top
, infoPtr
->clrBk
,
4687 item
->state
& LVIS_CUT
? RGB(255, 255, 255) : CLR_DEFAULT
,
4688 style
| (item
->state
& LVIS_OVERLAYMASK
));
4691 /* Don't bother painting item being edited */
4692 if (infoPtr
->hwndEdit
&& item
->iItem
== infoPtr
->nEditLabelItem
&& item
->iSubItem
== 0) return;
4694 /* figure out the text drawing flags */
4695 format
= (infoPtr
->uView
== LV_VIEW_ICON
? (focus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
4696 if (infoPtr
->uView
== LV_VIEW_ICON
)
4697 format
= (focus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
4698 else if (item
->iSubItem
)
4700 switch (LISTVIEW_GetColumnInfo(infoPtr
, item
->iSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
4702 case LVCFMT_RIGHT
: format
|= DT_RIGHT
; break;
4703 case LVCFMT_CENTER
: format
|= DT_CENTER
; break;
4704 default: format
|= DT_LEFT
;
4707 if (!(format
& (DT_RIGHT
| DT_CENTER
)))
4709 if (himl
&& item
->iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
4710 else rcLabel
.left
+= LABEL_HOR_PADDING
;
4712 else if (format
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
4714 /* for GRIDLINES reduce the bottom so the text formats correctly */
4715 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
4718 DrawTextW(nmlvcd
->nmcd
.hdc
, item
->pszText
, -1, &rcLabel
, format
);
4726 * [I] infoPtr : valid pointer to the listview structure
4727 * [I] hdc : device context handle
4728 * [I] nItem : item index
4729 * [I] nSubItem : subitem index
4730 * [I] pos : item position in client coordinates
4731 * [I] cdmode : custom draw mode
4737 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, ITERATOR
*subitems
, POINT pos
, DWORD cdmode
)
4739 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
4740 static WCHAR callbackW
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
4741 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
4743 NMLVCUSTOMDRAW nmlvcd
;
4746 TRACE("(hdc=%p, nItem=%d, subitems=%p, pos=%s)\n", hdc
, nItem
, subitems
, wine_dbgstr_point(&pos
));
4748 /* get information needed for drawing the item */
4749 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_PARAM
| LVIF_STATE
;
4750 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
4751 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
| LVIS_CUT
| LVIS_OVERLAYMASK
;
4752 lvItem
.iItem
= nItem
;
4753 lvItem
.iSubItem
= 0;
4756 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4757 lvItem
.pszText
= szDispText
;
4758 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
4759 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= callbackW
;
4760 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
4762 /* now check if we need to update the focus rectangle */
4763 focus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
4764 if (!focus
) lvItem
.state
&= ~LVIS_FOCUSED
;
4766 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, NULL
, NULL
, NULL
);
4767 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
4769 /* Full custom draw stage sequence looks like this:
4774 - CDDS_ITEMPREPAINT|CDDS_SUBITEM | => sent n times, where n is number of subitems,
4775 CDDS_ITEMPOSTPAINT|CDDS_SUBITEM | including item itself
4776 - CDDS_ITEMPOSTPAINT
4781 - CDDS_ITEMPOSTPAINT
4784 /* fill in the custom draw structure */
4785 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
4786 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
4787 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_ITEMPREPAINT
, &nmlvcd
);
4788 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
4792 while (iterator_next(subitems
))
4794 DWORD subitemstage
= CDRF_DODEFAULT
;
4796 /* We need to query for each subitem, item's data (subitem == 0) is already here at this point */
4797 if (subitems
->nItem
)
4799 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_PARAM
| LVIF_INDENT
;
4800 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
| LVIS_CUT
| LVIS_OVERLAYMASK
;
4801 lvItem
.iItem
= nItem
;
4802 lvItem
.iSubItem
= subitems
->nItem
;
4805 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4806 lvItem
.pszText
= szDispText
;
4807 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
4808 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4809 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
4810 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= callbackW
;
4811 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
4813 /* update custom draw data */
4814 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &nmlvcd
.nmcd
.rc
, NULL
, NULL
, NULL
, NULL
);
4815 OffsetRect(&nmlvcd
.nmcd
.rc
, pos
.x
, pos
.y
);
4816 nmlvcd
.iSubItem
= subitems
->nItem
;
4819 if (cdsubitemmode
& CDRF_NOTIFYSUBITEMDRAW
)
4820 subitemstage
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
4823 nmlvcd
.clrTextBk
= infoPtr
->clrTextBk
;
4824 nmlvcd
.clrText
= infoPtr
->clrText
;
4827 if (subitems
->nItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
4828 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
4829 else if (!(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
4830 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, TRUE
);
4832 if (!(subitemstage
& CDRF_SKIPDEFAULT
))
4833 LISTVIEW_DrawItemPart(infoPtr
, &lvItem
, &nmlvcd
, &pos
);
4835 if (subitemstage
& CDRF_NOTIFYPOSTPAINT
)
4836 subitemstage
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPOSTPAINT
, &nmlvcd
);
4841 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
4842 LISTVIEW_DrawItemPart(infoPtr
, &lvItem
, &nmlvcd
, &pos
);
4846 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
4848 nmlvcd
.iSubItem
= 0;
4849 notify_customdraw(infoPtr
, CDDS_ITEMPOSTPAINT
, &nmlvcd
);
4857 * Draws listview items when in owner draw mode.
4860 * [I] infoPtr : valid pointer to the listview structure
4861 * [I] hdc : device context handle
4866 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4868 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
4869 DWORD cditemmode
= CDRF_DODEFAULT
;
4870 NMLVCUSTOMDRAW nmlvcd
;
4871 POINT Origin
, Position
;
4877 ZeroMemory(&dis
, sizeof(dis
));
4879 /* Get scroll info once before loop */
4880 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4882 /* iterate through the invalidated rows */
4883 while(iterator_next(i
))
4885 item
.iItem
= i
->nItem
;
4887 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4888 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4889 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
4891 dis
.CtlType
= ODT_LISTVIEW
;
4893 dis
.itemID
= item
.iItem
;
4894 dis
.itemAction
= ODA_DRAWENTIRE
;
4896 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
4897 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
4898 dis
.hwndItem
= infoPtr
->hwndSelf
;
4900 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
4901 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
4902 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
4903 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
4904 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
4905 dis
.itemData
= item
.lParam
;
4907 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), wine_dbgstr_rect(&dis
.rcItem
));
4910 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4911 * structure for the rest. of the paint cycle
4913 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
4914 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
4915 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4917 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
4919 prepaint_setup (infoPtr
, hdc
, &nmlvcd
, FALSE
);
4920 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
4923 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
4924 notify_postpaint(infoPtr
, &nmlvcd
);
4930 * Draws listview items when in report display mode.
4933 * [I] infoPtr : valid pointer to the listview structure
4934 * [I] hdc : device context handle
4935 * [I] cdmode : custom draw mode
4940 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4943 RECT rcClip
, rcItem
;
4951 /* figure out what to draw */
4952 rgntype
= GetClipBox(hdc
, &rcClip
);
4953 if (rgntype
== NULLREGION
) return;
4955 /* Get scroll info once before loop */
4956 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4958 colRanges
= ranges_create(DPA_GetPtrCount(infoPtr
->hdpaColumns
));
4960 /* narrow down the columns we need to paint */
4961 for(col
= 0; col
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); col
++)
4963 INT index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, col
, 0);
4965 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
4966 if ((rcItem
.right
+ Origin
.x
>= rcClip
.left
) && (rcItem
.left
+ Origin
.x
< rcClip
.right
))
4967 ranges_additem(colRanges
, index
);
4969 iterator_rangesitems(&j
, colRanges
);
4971 /* in full row select, we _have_ to draw the main item */
4972 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4975 /* iterate through the invalidated rows */
4976 while(iterator_next(i
))
4982 SelectObject(hdc
, infoPtr
->hFont
);
4983 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4984 Position
.x
= Origin
.x
;
4985 Position
.y
+= Origin
.y
;
4987 subitems
= ranges_create(DPA_GetPtrCount(infoPtr
->hdpaColumns
));
4989 /* iterate through the invalidated columns */
4990 while(iterator_next(&j
))
4992 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4994 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
4997 rcItem
.bottom
= infoPtr
->nItemHeight
;
4998 OffsetRect(&rcItem
, Origin
.x
, Position
.y
);
4999 if (!RectVisible(hdc
, &rcItem
)) continue;
5002 ranges_additem(subitems
, j
.nItem
);
5005 iterator_rangesitems(&k
, subitems
);
5006 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, &k
, Position
, cdmode
);
5007 iterator_destroy(&k
);
5009 iterator_destroy(&j
);
5014 * Draws the gridlines if necessary when in report display mode.
5017 * [I] infoPtr : valid pointer to the listview structure
5018 * [I] hdc : device context handle
5023 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
5029 RECT rcClip
, rcItem
= {0};
5037 /* figure out what to draw */
5038 rgntype
= GetClipBox(hdc
, &rcClip
);
5039 if (rgntype
== NULLREGION
) return;
5041 /* Get scroll info once before loop */
5042 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5044 colRanges
= ranges_create(DPA_GetPtrCount(infoPtr
->hdpaColumns
));
5046 /* narrow down the columns we need to paint */
5047 for(col
= 0; col
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); col
++)
5049 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, col
, 0);
5051 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
5052 if ((rcItem
.right
+ Origin
.x
>= rcClip
.left
) && (rcItem
.left
+ Origin
.x
< rcClip
.right
))
5053 ranges_additem(colRanges
, index
);
5056 /* is right most vertical line visible? */
5057 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
5059 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
5060 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
5061 rmost
= (rcItem
.right
+ Origin
.x
< rcClip
.right
);
5064 if ((hPen
= CreatePen( PS_SOLID
, 1, comctl32_color
.clr3dFace
)))
5066 hOldPen
= SelectObject ( hdc
, hPen
);
5068 /* draw the vertical lines for the columns */
5069 iterator_rangesitems(&j
, colRanges
);
5070 while(iterator_next(&j
))
5072 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
5073 if (rcItem
.left
== 0) continue; /* skip leftmost column */
5074 rcItem
.left
+= Origin
.x
;
5075 rcItem
.right
+= Origin
.x
;
5076 rcItem
.top
= infoPtr
->rcList
.top
;
5077 rcItem
.bottom
= infoPtr
->rcList
.bottom
;
5078 TRACE("vert col=%d, rcItem=%s\n", j
.nItem
, wine_dbgstr_rect(&rcItem
));
5079 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
5080 LineTo (hdc
, rcItem
.left
, rcItem
.bottom
);
5082 iterator_destroy(&j
);
5083 /* draw rightmost grid line if visible */
5086 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
5087 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
5088 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
5090 rcItem
.right
+= Origin
.x
;
5092 MoveToEx (hdc
, rcItem
.right
, infoPtr
->rcList
.top
, NULL
);
5093 LineTo (hdc
, rcItem
.right
, infoPtr
->rcList
.bottom
);
5096 /* draw the horizontal lines for the rows */
5097 itemheight
= LISTVIEW_CalculateItemHeight(infoPtr
);
5098 rcItem
.left
= infoPtr
->rcList
.left
;
5099 rcItem
.right
= infoPtr
->rcList
.right
;
5100 for(y
= Origin
.y
> 1 ? Origin
.y
- 1 : itemheight
- 1 + Origin
.y
% itemheight
; y
<=infoPtr
->rcList
.bottom
; y
+=itemheight
)
5102 rcItem
.bottom
= rcItem
.top
= y
;
5103 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem
));
5104 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
5105 LineTo (hdc
, rcItem
.right
, rcItem
.top
);
5108 SelectObject( hdc
, hOldPen
);
5109 DeleteObject( hPen
);
5112 ranges_destroy(colRanges
);
5117 * Draws listview items when in list display mode.
5120 * [I] infoPtr : valid pointer to the listview structure
5121 * [I] hdc : device context handle
5122 * [I] cdmode : custom draw mode
5127 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
5129 POINT Origin
, Position
;
5131 /* Get scroll info once before loop */
5132 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5134 while(iterator_prev(i
))
5136 SelectObject(hdc
, infoPtr
->hFont
);
5137 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
5138 Position
.x
+= Origin
.x
;
5139 Position
.y
+= Origin
.y
;
5141 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, NULL
, Position
, cdmode
);
5148 * Draws listview items.
5151 * [I] infoPtr : valid pointer to the listview structure
5152 * [I] hdc : device context handle
5153 * [I] prcErase : rect to be erased before refresh (may be NULL)
5158 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*prcErase
)
5160 COLORREF oldTextColor
= 0, oldBkColor
= 0;
5161 NMLVCUSTOMDRAW nmlvcd
;
5168 HBITMAP hbmp
= NULL
;
5171 LISTVIEW_DUMP(infoPtr
);
5173 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
5174 TRACE("double buffering\n");
5176 hdc
= CreateCompatibleDC(hdcOrig
);
5178 ERR("Failed to create DC for backbuffer\n");
5181 hbmp
= CreateCompatibleBitmap(hdcOrig
, infoPtr
->rcList
.right
,
5182 infoPtr
->rcList
.bottom
);
5184 ERR("Failed to create bitmap for backbuffer\n");
5189 SelectObject(hdc
, hbmp
);
5190 SelectObject(hdc
, infoPtr
->hFont
);
5192 if(GetClipBox(hdcOrig
, &rcClient
))
5193 IntersectClipRect(hdc
, rcClient
.left
, rcClient
.top
, rcClient
.right
, rcClient
.bottom
);
5195 /* Save dc values we're gonna trash while drawing
5196 * FIXME: Should be done in LISTVIEW_DrawItem() */
5197 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
5198 oldBkMode
= GetBkMode(hdc
);
5199 oldBkColor
= GetBkColor(hdc
);
5200 oldTextColor
= GetTextColor(hdc
);
5203 infoPtr
->bIsDrawing
= TRUE
;
5206 LISTVIEW_FillBkgnd(infoPtr
, hdc
, prcErase
);
5207 } else if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
5208 /* If no erasing was done (usually because RedrawWindow was called
5209 * with RDW_INVALIDATE only) we need to copy the old contents into
5210 * the backbuffer before continuing. */
5211 BitBlt(hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
5212 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
5213 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
5214 hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
5217 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
5218 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
5219 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
5220 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
5222 /* nothing to draw */
5223 if(infoPtr
->nItemCount
== 0) goto enddraw
;
5225 /* figure out what we need to draw */
5226 iterator_visibleitems(&i
, infoPtr
, hdc
);
5227 range
= iterator_range(&i
);
5229 /* send cache hint notification */
5230 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5234 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
5235 nmlv
.iFrom
= range
.lower
;
5236 nmlv
.iTo
= range
.upper
- 1;
5237 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
5240 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
5241 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
5244 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
5245 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
5246 else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */
5247 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
5249 /* if we have a focus rect and it's visible, draw it */
5250 if (infoPtr
->bFocus
&& range
.lower
<= infoPtr
->nFocusedItem
&&
5251 (range
.upper
- 1) >= infoPtr
->nFocusedItem
)
5252 LISTVIEW_DrawFocusRect(infoPtr
, hdc
);
5254 iterator_destroy(&i
);
5257 /* For LVS_EX_GRIDLINES go and draw lines */
5258 /* This includes the case where there were *no* items */
5259 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
5260 LISTVIEW_RefreshReportGrid(infoPtr
, hdc
);
5262 /* Draw marquee rectangle if appropriate */
5263 if (infoPtr
->bMarqueeSelect
)
5264 DrawFocusRect(hdc
, &infoPtr
->marqueeDrawRect
);
5266 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
5267 notify_postpaint(infoPtr
, &nmlvcd
);
5270 BitBlt(hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
5271 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
5272 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
5273 hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
5278 SelectObject(hdc
, hOldFont
);
5279 SetBkMode(hdc
, oldBkMode
);
5280 SetBkColor(hdc
, oldBkColor
);
5281 SetTextColor(hdc
, oldTextColor
);
5284 infoPtr
->bIsDrawing
= FALSE
;
5290 * Calculates the approximate width and height of a given number of items.
5293 * [I] infoPtr : valid pointer to the listview structure
5294 * [I] nItemCount : number of items
5295 * [I] wWidth : width
5296 * [I] wHeight : height
5299 * Returns a DWORD. The width in the low word and the height in high word.
5301 static DWORD
LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
5302 WORD wWidth
, WORD wHeight
)
5304 DWORD dwViewRect
= 0;
5306 if (nItemCount
== -1)
5307 nItemCount
= infoPtr
->nItemCount
;
5309 if (infoPtr
->uView
== LV_VIEW_LIST
)
5311 INT nItemCountPerColumn
= 1;
5312 INT nColumnCount
= 0;
5314 if (wHeight
== 0xFFFF)
5316 /* use current height */
5317 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
5320 if (wHeight
< infoPtr
->nItemHeight
)
5321 wHeight
= infoPtr
->nItemHeight
;
5325 if (infoPtr
->nItemHeight
> 0)
5327 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
5328 if (nItemCountPerColumn
== 0)
5329 nItemCountPerColumn
= 1;
5331 if (nItemCount
% nItemCountPerColumn
!= 0)
5332 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
5334 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
5338 /* Microsoft padding magic */
5339 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
5340 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
5342 dwViewRect
= MAKELONG(wWidth
, wHeight
);
5344 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
5348 if (infoPtr
->nItemCount
> 0)
5350 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
5351 wWidth
= rcBox
.right
- rcBox
.left
;
5352 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
5356 /* use current height and width */
5357 if (wHeight
== 0xffff)
5358 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
5359 if (wWidth
== 0xffff)
5360 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
5363 dwViewRect
= MAKELONG(wWidth
, wHeight
);
5365 else if (infoPtr
->uView
== LV_VIEW_ICON
)
5371 nItemWidth
= infoPtr
->iconSpacing
.cx
;
5372 nItemHeight
= infoPtr
->iconSpacing
.cy
;
5374 if (wWidth
== 0xffff)
5375 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
5377 if (wWidth
< nItemWidth
)
5378 wWidth
= nItemWidth
;
5380 cols
= wWidth
/ nItemWidth
;
5381 if (cols
> nItemCount
)
5388 rows
= nItemCount
/ cols
;
5389 if (nItemCount
% cols
)
5395 wHeight
= (nItemHeight
* rows
)+2;
5396 wWidth
= (nItemWidth
* cols
)+2;
5398 dwViewRect
= MAKELONG(wWidth
, wHeight
);
5400 else if (infoPtr
->uView
== LV_VIEW_SMALLICON
)
5401 FIXME("uView == LV_VIEW_SMALLICON: not implemented\n");
5408 * Cancel edit label with saving item text.
5411 * [I] infoPtr : valid pointer to the listview structure
5414 * Always returns TRUE.
5416 static LRESULT
LISTVIEW_CancelEditLabel(LISTVIEW_INFO
*infoPtr
)
5418 if (infoPtr
->hwndEdit
)
5420 /* handle value will be lost after LISTVIEW_EndEditLabelT */
5421 HWND edit
= infoPtr
->hwndEdit
;
5423 LISTVIEW_EndEditLabelT(infoPtr
, TRUE
, IsWindowUnicode(infoPtr
->hwndEdit
));
5424 SendMessageW(edit
, WM_CLOSE
, 0, 0);
5432 * Create a drag image list for the specified item.
5435 * [I] infoPtr : valid pointer to the listview structure
5436 * [I] iItem : index of item
5437 * [O] lppt : Upper-left corner of the image
5440 * Returns a handle to the image list if successful, NULL otherwise.
5442 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
5448 HBITMAP hbmp
, hOldbmp
;
5450 HIMAGELIST dragList
= 0;
5451 TRACE("iItem=%d Count=%d\n", iItem
, infoPtr
->nItemCount
);
5453 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
|| !lppt
)
5456 rcItem
.left
= LVIR_BOUNDS
;
5457 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
5460 lppt
->x
= rcItem
.left
;
5461 lppt
->y
= rcItem
.top
;
5463 size
.cx
= rcItem
.right
- rcItem
.left
;
5464 size
.cy
= rcItem
.bottom
- rcItem
.top
;
5466 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
5467 hdc
= CreateCompatibleDC(hdcOrig
);
5468 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
5469 hOldbmp
= SelectObject(hdc
, hbmp
);
5470 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
5472 SetRect(&rcItem
, 0, 0, size
.cx
, size
.cy
);
5473 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
5476 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, NULL
, pos
, CDRF_DODEFAULT
))
5478 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
5479 SelectObject(hdc
, hOldbmp
);
5480 ImageList_Add(dragList
, hbmp
, 0);
5483 SelectObject(hdc
, hOldbmp
);
5485 SelectObject(hdc
, hOldFont
);
5488 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
5490 TRACE("ret=%p\n", dragList
);
5498 * Removes all listview items and subitems.
5501 * [I] infoPtr : valid pointer to the listview structure
5507 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
, BOOL destroy
)
5509 HDPA hdpaSubItems
= NULL
;
5510 BOOL suppress
= FALSE
;
5518 /* we do it directly, to avoid notifications */
5519 ranges_clear(infoPtr
->selectionRanges
);
5520 infoPtr
->nSelectionMark
= -1;
5521 infoPtr
->nFocusedItem
= -1;
5522 SetRectEmpty(&infoPtr
->rcFocus
);
5523 /* But we are supposed to leave nHotItem as is! */
5525 /* send LVN_DELETEALLITEMS notification */
5526 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) || !destroy
)
5530 memset(&nmlv
, 0, sizeof(NMLISTVIEW
));
5532 suppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
5535 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
5537 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
5539 /* send LVN_DELETEITEM notification, if not suppressed
5540 and if it is not a virtual listview */
5541 if (!suppress
) notify_deleteitem(infoPtr
, i
);
5542 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
5543 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5544 /* free id struct */
5545 j
= DPA_GetPtrIndex(infoPtr
->hdpaItemIds
, lpItem
->id
);
5546 lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, j
);
5547 DPA_DeletePtr(infoPtr
->hdpaItemIds
, j
);
5549 /* both item and subitem start with ITEMHDR header */
5550 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
5552 hdrItem
= DPA_GetPtr(hdpaSubItems
, j
);
5553 if (is_text(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
5556 DPA_Destroy(hdpaSubItems
);
5557 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
5559 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
5560 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
5561 infoPtr
->nItemCount
--;
5566 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
5567 LISTVIEW_UpdateScroll(infoPtr
);
5569 LISTVIEW_InvalidateList(infoPtr
);
5576 * Scrolls, and updates the columns, when a column is changing width.
5579 * [I] infoPtr : valid pointer to the listview structure
5580 * [I] nColumn : column to scroll
5581 * [I] dx : amount of scroll, in pixels
5586 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
5588 COLUMN_INFO
*lpColumnInfo
;
5594 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
5595 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
5596 rcCol
= lpColumnInfo
->rcHeader
;
5597 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
5598 rcCol
.left
= rcCol
.right
;
5600 /* adjust the other columns */
5601 hdi
.mask
= HDI_ORDER
;
5602 if (SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdi
))
5604 INT nOrder
= hdi
.iOrder
;
5605 for (nCol
= 0; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
5607 hdi
.mask
= HDI_ORDER
;
5608 SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nCol
, (LPARAM
)&hdi
);
5609 if (hdi
.iOrder
>= nOrder
) {
5610 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
5611 lpColumnInfo
->rcHeader
.left
+= dx
;
5612 lpColumnInfo
->rcHeader
.right
+= dx
;
5617 /* do not update screen if not in report mode */
5618 if (!is_redrawing(infoPtr
) || infoPtr
->uView
!= LV_VIEW_DETAILS
) return;
5620 /* Need to reset the item width when inserting a new column */
5621 infoPtr
->nItemWidth
+= dx
;
5623 LISTVIEW_UpdateScroll(infoPtr
);
5624 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
5626 /* scroll to cover the deleted column, and invalidate for redraw */
5627 rcOld
= infoPtr
->rcList
;
5628 rcOld
.left
= ptOrigin
.x
+ rcCol
.left
+ dx
;
5629 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
5634 * Removes a column from the listview control.
5637 * [I] infoPtr : valid pointer to the listview structure
5638 * [I] nColumn : column index
5644 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
5648 TRACE("nColumn=%d\n", nColumn
);
5650 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
5653 /* While the MSDN specifically says that column zero should not be deleted,
5654 what actually happens is that the column itself is deleted but no items or subitems
5658 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
5660 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nColumn
, 0))
5663 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
5664 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
5666 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
5668 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
5670 INT nItem
, nSubItem
, i
;
5672 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
5674 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
5677 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
5679 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
5680 if (lpSubItem
->iSubItem
== nColumn
)
5683 lpDelItem
= lpSubItem
;
5685 else if (lpSubItem
->iSubItem
> nColumn
)
5687 lpSubItem
->iSubItem
--;
5691 /* if we found our subitem, zap it */
5695 if (is_text(lpDelItem
->hdr
.pszText
))
5696 Free(lpDelItem
->hdr
.pszText
);
5701 /* free dpa memory */
5702 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
5707 /* update the other column info */
5708 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
5709 LISTVIEW_InvalidateList(infoPtr
);
5711 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
5712 LISTVIEW_UpdateItemSize(infoPtr
);
5719 * Invalidates the listview after an item's insertion or deletion.
5722 * [I] infoPtr : valid pointer to the listview structure
5723 * [I] nItem : item index
5724 * [I] dir : -1 if deleting, 1 if inserting
5729 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
5731 INT nPerCol
, nItemCol
, nItemRow
;
5735 /* if we don't refresh, what's the point of scrolling? */
5736 if (!is_redrawing(infoPtr
)) return;
5738 assert (abs(dir
) == 1);
5740 /* arrange icons if autoarrange is on */
5741 if (is_autoarrange(infoPtr
))
5743 BOOL arrange
= TRUE
;
5744 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
5745 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
5746 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
5749 /* scrollbars need updating */
5750 LISTVIEW_UpdateScroll(infoPtr
);
5752 /* figure out the item's position */
5753 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
5754 nPerCol
= infoPtr
->nItemCount
+ 1;
5755 else if (infoPtr
->uView
== LV_VIEW_LIST
)
5756 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
5757 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
5760 nItemCol
= nItem
/ nPerCol
;
5761 nItemRow
= nItem
% nPerCol
;
5762 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5764 /* move the items below up a slot */
5765 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
5766 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
5767 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
5768 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
5769 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
5770 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
5771 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
5773 TRACE("Invalidating rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll
), wine_dbgstr_rect(&infoPtr
->rcList
));
5774 InvalidateRect(infoPtr
->hwndSelf
, &rcScroll
, TRUE
);
5777 /* report has only that column, so we're done */
5778 if (infoPtr
->uView
== LV_VIEW_DETAILS
) return;
5780 /* now for LISTs, we have to deal with the columns to the right */
5781 SetRect(&rcScroll
, (nItemCol
+ 1) * infoPtr
->nItemWidth
, 0,
5782 (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
,
5783 nPerCol
* infoPtr
->nItemHeight
);
5784 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
5785 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
5786 InvalidateRect(infoPtr
->hwndSelf
, &rcScroll
, TRUE
);
5791 * Removes an item from the listview control.
5794 * [I] infoPtr : valid pointer to the listview structure
5795 * [I] nItem : item index
5801 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
5804 const BOOL is_icon
= (infoPtr
->uView
== LV_VIEW_SMALLICON
|| infoPtr
->uView
== LV_VIEW_ICON
);
5805 INT focus
= infoPtr
->nFocusedItem
;
5807 TRACE("(nItem=%d)\n", nItem
);
5809 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5811 /* remove selection, and focus */
5813 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
5814 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
5816 /* send LVN_DELETEITEM notification. */
5817 if (!notify_deleteitem(infoPtr
, nItem
)) return FALSE
;
5819 /* we need to do this here, because we'll be deleting stuff */
5821 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
5823 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
5831 hdpaSubItems
= DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
5832 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5834 /* free id struct */
5835 i
= DPA_GetPtrIndex(infoPtr
->hdpaItemIds
, lpItem
->id
);
5836 lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, i
);
5837 DPA_DeletePtr(infoPtr
->hdpaItemIds
, i
);
5839 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
5841 hdrItem
= DPA_GetPtr(hdpaSubItems
, i
);
5842 if (is_text(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
5845 DPA_Destroy(hdpaSubItems
);
5850 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
5851 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
5854 infoPtr
->nItemCount
--;
5855 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
5856 LISTVIEW_ShiftFocus(infoPtr
, focus
, nItem
, -1);
5858 /* now is the invalidation fun */
5860 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
5867 * Callback implementation for editlabel control
5870 * [I] infoPtr : valid pointer to the listview structure
5871 * [I] storeText : store edit box text as item text
5872 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
5878 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, BOOL storeText
, BOOL isW
)
5880 HWND hwndSelf
= infoPtr
->hwndSelf
;
5881 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
5882 NMLVDISPINFOW dispInfo
;
5883 INT editedItem
= infoPtr
->nEditLabelItem
;
5885 WCHAR
*pszText
= NULL
;
5890 DWORD len
= isW
? GetWindowTextLengthW(infoPtr
->hwndEdit
) : GetWindowTextLengthA(infoPtr
->hwndEdit
);
5894 if (!(pszText
= Alloc(len
* (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))))
5898 GetWindowTextW(infoPtr
->hwndEdit
, pszText
, len
);
5900 GetWindowTextA(infoPtr
->hwndEdit
, (CHAR
*)pszText
, len
);
5904 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
5906 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5907 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
5908 dispInfo
.item
.iItem
= editedItem
;
5909 dispInfo
.item
.iSubItem
= 0;
5910 dispInfo
.item
.stateMask
= ~0;
5911 dispInfo
.item
.pszText
= szDispText
;
5912 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
5913 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
))
5920 same
= (lstrcmpW(dispInfo
.item
.pszText
, pszText
) == 0);
5923 LPWSTR tmp
= textdupTtoW(pszText
, FALSE
);
5924 same
= (lstrcmpW(dispInfo
.item
.pszText
, tmp
) == 0);
5925 textfreeT(tmp
, FALSE
);
5928 /* add the text from the edit in */
5929 dispInfo
.item
.mask
|= LVIF_TEXT
;
5930 dispInfo
.item
.pszText
= same
? NULL
: pszText
;
5931 dispInfo
.item
.cchTextMax
= textlenT(dispInfo
.item
.pszText
, isW
);
5933 infoPtr
->notify_mask
&= ~NOTIFY_MASK_END_LABEL_EDIT
;
5935 /* Do we need to update the Item Text */
5936 res
= notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
);
5938 infoPtr
->notify_mask
|= NOTIFY_MASK_END_LABEL_EDIT
;
5940 infoPtr
->nEditLabelItem
= -1;
5941 infoPtr
->hwndEdit
= 0;
5943 if (!res
) goto cleanup
;
5945 if (!IsWindow(hwndSelf
))
5950 if (!pszText
) return TRUE
;
5957 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
5959 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, editedItem
);
5960 ITEM_INFO
* lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5961 if (lpItem
&& lpItem
->hdr
.pszText
== LPSTR_TEXTCALLBACKW
)
5963 LISTVIEW_InvalidateItem(infoPtr
, editedItem
);
5969 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5970 dispInfo
.item
.mask
= LVIF_TEXT
;
5971 dispInfo
.item
.iItem
= editedItem
;
5972 dispInfo
.item
.iSubItem
= 0;
5973 dispInfo
.item
.pszText
= pszText
;
5974 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
5975 res
= LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
5985 * Subclassed edit control windproc function
5988 * [I] hwnd : the edit window handle
5989 * [I] uMsg : the message that is to be processed
5990 * [I] wParam : first message parameter
5991 * [I] lParam : second message parameter
5992 * [I] isW : TRUE if input is Unicode
5997 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
5999 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
6002 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
6003 hwnd
, uMsg
, wParam
, lParam
, isW
);
6008 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
6012 WNDPROC editProc
= infoPtr
->EditWndProc
;
6013 infoPtr
->EditWndProc
= 0;
6014 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
6015 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
6019 if (VK_ESCAPE
== (INT
)wParam
)
6024 else if (VK_RETURN
== (INT
)wParam
)
6028 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
6032 if (infoPtr
->hwndEdit
)
6033 LISTVIEW_EndEditLabelT(infoPtr
, save
, isW
);
6035 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
6041 * Subclassed edit control Unicode windproc function
6044 * [I] hwnd : the edit window handle
6045 * [I] uMsg : the message that is to be processed
6046 * [I] wParam : first message parameter
6047 * [I] lParam : second message parameter
6051 static LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
6053 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
6058 * Subclassed edit control ANSI windproc function
6061 * [I] hwnd : the edit window handle
6062 * [I] uMsg : the message that is to be processed
6063 * [I] wParam : first message parameter
6064 * [I] lParam : second message parameter
6068 static LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
6070 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
6075 * Creates a subclassed edit control
6078 * [I] infoPtr : valid pointer to the listview structure
6079 * [I] text : initial text for the edit
6080 * [I] style : the window style
6081 * [I] isW : TRUE if input is Unicode
6085 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, BOOL isW
)
6087 static const DWORD style
= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
|WS_VISIBLE
;
6088 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
6091 TRACE("(%p, text=%s, isW=%d)\n", infoPtr
, debugtext_t(text
, isW
), isW
);
6093 /* window will be resized and positioned after LVN_BEGINLABELEDIT */
6095 hedit
= CreateWindowW(WC_EDITW
, text
, style
, 0, 0, 0, 0, infoPtr
->hwndSelf
, 0, hinst
, 0);
6097 hedit
= CreateWindowA(WC_EDITA
, (LPCSTR
)text
, style
, 0, 0, 0, 0, infoPtr
->hwndSelf
, 0, hinst
, 0);
6099 if (!hedit
) return 0;
6101 infoPtr
->EditWndProc
= (WNDPROC
)
6102 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
6103 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
6105 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);
6106 SendMessageW(hedit
, EM_SETLIMITTEXT
, DISP_TEXT_SIZE
-1, 0);
6113 * Begin in place editing of specified list view item
6116 * [I] infoPtr : valid pointer to the listview structure
6117 * [I] nItem : item index
6118 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
6124 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
6126 WCHAR disptextW
[DISP_TEXT_SIZE
] = { 0 };
6127 HWND hwndSelf
= infoPtr
->hwndSelf
;
6128 NMLVDISPINFOW dispInfo
;
6129 HFONT hOldFont
= NULL
;
6135 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
6137 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
6139 /* remove existing edit box */
6140 if (infoPtr
->hwndEdit
)
6142 SetFocus(infoPtr
->hwndSelf
);
6143 infoPtr
->hwndEdit
= 0;
6146 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6148 infoPtr
->nEditLabelItem
= nItem
;
6150 LISTVIEW_SetSelection(infoPtr
, nItem
);
6151 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
6152 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
6154 rect
.left
= LVIR_LABEL
;
6155 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
6157 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
6158 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
6159 dispInfo
.item
.iItem
= nItem
;
6160 dispInfo
.item
.iSubItem
= 0;
6161 dispInfo
.item
.stateMask
= ~0;
6162 dispInfo
.item
.pszText
= disptextW
;
6163 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
6164 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
6166 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, isW
);
6167 if (!infoPtr
->hwndEdit
) return 0;
6169 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
6171 if (!IsWindow(hwndSelf
))
6173 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
6174 infoPtr
->hwndEdit
= 0;
6178 TRACE("disp text=%s\n", debugtext_t(dispInfo
.item
.pszText
, isW
));
6180 /* position and display edit box */
6181 hdc
= GetDC(infoPtr
->hwndSelf
);
6183 /* select the font to get appropriate metric dimensions */
6185 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
6187 /* use real edit box content, it could be altered during LVN_BEGINLABELEDIT notification */
6188 GetWindowTextW(infoPtr
->hwndEdit
, disptextW
, DISP_TEXT_SIZE
);
6189 TRACE("edit box text=%s\n", debugstr_w(disptextW
));
6191 /* get string length in pixels */
6192 GetTextExtentPoint32W(hdc
, disptextW
, lstrlenW(disptextW
), &sz
);
6194 /* add extra spacing for the next character */
6195 GetTextMetricsW(hdc
, &tm
);
6196 sz
.cx
+= tm
.tmMaxCharWidth
* 2;
6199 SelectObject(hdc
, hOldFont
);
6201 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6203 sz
.cy
= rect
.bottom
- rect
.top
+ 2;
6206 TRACE("moving edit=(%d,%d)-(%d,%d)\n", rect
.left
, rect
.top
, sz
.cx
, sz
.cy
);
6207 MoveWindow(infoPtr
->hwndEdit
, rect
.left
, rect
.top
, sz
.cx
, sz
.cy
, FALSE
);
6208 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
6209 SetFocus(infoPtr
->hwndEdit
);
6210 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
6211 return infoPtr
->hwndEdit
;
6217 * Ensures the specified item is visible, scrolling into view if necessary.
6220 * [I] infoPtr : valid pointer to the listview structure
6221 * [I] nItem : item index
6222 * [I] bPartial : partially or entirely visible
6228 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
6230 INT nScrollPosHeight
= 0;
6231 INT nScrollPosWidth
= 0;
6232 INT nHorzAdjust
= 0;
6233 INT nVertAdjust
= 0;
6236 RECT rcItem
, rcTemp
;
6238 rcItem
.left
= LVIR_BOUNDS
;
6239 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
6241 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
6243 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
6245 /* scroll left/right, but in LV_VIEW_DETAILS mode */
6246 if (infoPtr
->uView
== LV_VIEW_LIST
)
6247 nScrollPosWidth
= infoPtr
->nItemWidth
;
6248 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
6249 nScrollPosWidth
= 1;
6251 if (rcItem
.left
< infoPtr
->rcList
.left
)
6254 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
6259 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
6263 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
6265 /* scroll up/down, but not in LVS_LIST mode */
6266 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
6267 nScrollPosHeight
= infoPtr
->nItemHeight
;
6268 else if ((infoPtr
->uView
== LV_VIEW_ICON
) || (infoPtr
->uView
== LV_VIEW_SMALLICON
))
6269 nScrollPosHeight
= 1;
6271 if (rcItem
.top
< infoPtr
->rcList
.top
)
6274 if (infoPtr
->uView
!= LV_VIEW_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
6279 if (infoPtr
->uView
!= LV_VIEW_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
6283 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
6285 if (nScrollPosWidth
)
6287 INT diff
= nHorzDiff
/ nScrollPosWidth
;
6288 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
6289 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
);
6292 if (nScrollPosHeight
)
6294 INT diff
= nVertDiff
/ nScrollPosHeight
;
6295 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
6296 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
);
6304 * Searches for an item with specific characteristics.
6307 * [I] hwnd : window handle
6308 * [I] nStart : base item index
6309 * [I] lpFindInfo : item information to look for
6312 * SUCCESS : index of item
6315 static INT
LISTVIEW_FindItemW(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
6316 const LVFINDINFOW
*lpFindInfo
)
6318 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6319 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
6320 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
6321 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
6322 POINT Position
, Destination
;
6325 /* Search in virtual listviews should be done by application, not by
6326 listview control, so we just send LVN_ODFINDITEMW and return the result */
6327 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
6331 nmlv
.iStart
= nStart
;
6332 nmlv
.lvfi
= *lpFindInfo
;
6333 return notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
6336 if (!lpFindInfo
|| nItem
< 0) return -1;
6339 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
| LVFI_SUBSTRING
))
6341 lvItem
.mask
|= LVIF_TEXT
;
6342 lvItem
.pszText
= szDispText
;
6343 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6346 if (lpFindInfo
->flags
& LVFI_WRAP
)
6349 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
6350 (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
))
6355 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6356 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
6357 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
6358 switch(lpFindInfo
->vkDirection
)
6360 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
6361 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
6362 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
6363 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
6364 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
6365 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
6366 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
6368 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
6369 Destination
.x
= rcArea
.right
;
6370 Destination
.y
= rcArea
.bottom
;
6372 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
6376 else Destination
.x
= Destination
.y
= 0;
6378 /* if LVFI_PARAM is specified, all other flags are ignored */
6379 if (lpFindInfo
->flags
& LVFI_PARAM
)
6381 lvItem
.mask
|= LVIF_PARAM
;
6383 lvItem
.mask
&= ~LVIF_TEXT
;
6386 nItem
= bNearest
? -1 : nStart
+ 1;
6389 for (; nItem
< nLast
; nItem
++)
6391 lvItem
.iItem
= nItem
;
6392 lvItem
.iSubItem
= 0;
6393 lvItem
.pszText
= szDispText
;
6394 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
6396 if (lvItem
.mask
& LVIF_PARAM
)
6398 if (lpFindInfo
->lParam
== lvItem
.lParam
)
6404 if (lvItem
.mask
& LVIF_TEXT
)
6406 if (lpFindInfo
->flags
& (LVFI_PARTIAL
| LVFI_SUBSTRING
))
6408 WCHAR
*p
= strstrW(lvItem
.pszText
, lpFindInfo
->psz
);
6409 if (!p
|| p
!= lvItem
.pszText
) continue;
6413 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
6417 if (!bNearest
) return nItem
;
6419 /* This is very inefficient. To do a good job here,
6420 * we need a sorted array of (x,y) item positions */
6421 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
6423 /* compute the distance^2 to the destination */
6424 xdist
= Destination
.x
- Position
.x
;
6425 ydist
= Destination
.y
- Position
.y
;
6426 dist
= xdist
* xdist
+ ydist
* ydist
;
6428 /* remember the distance, and item if it's closer */
6432 nNearestItem
= nItem
;
6439 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
6444 return nNearestItem
;
6449 * Searches for an item with specific characteristics.
6452 * [I] hwnd : window handle
6453 * [I] nStart : base item index
6454 * [I] lpFindInfo : item information to look for
6457 * SUCCESS : index of item
6460 static INT
LISTVIEW_FindItemA(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
6461 const LVFINDINFOA
*lpFindInfo
)
6467 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
6468 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
| LVFI_SUBSTRING
))
6469 fiw
.psz
= strW
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
6470 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
6471 textfreeT(strW
, FALSE
);
6477 * Retrieves column attributes.
6480 * [I] infoPtr : valid pointer to the listview structure
6481 * [I] nColumn : column index
6482 * [IO] lpColumn : column information
6483 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
6484 * otherwise it is in fact a LPLVCOLUMNA
6490 static BOOL
LISTVIEW_GetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
6492 COLUMN_INFO
*lpColumnInfo
;
6495 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6496 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
6498 /* initialize memory */
6499 ZeroMemory(&hdi
, sizeof(hdi
));
6501 if (lpColumn
->mask
& LVCF_TEXT
)
6503 hdi
.mask
|= HDI_TEXT
;
6504 hdi
.pszText
= lpColumn
->pszText
;
6505 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
6508 if (lpColumn
->mask
& LVCF_IMAGE
)
6509 hdi
.mask
|= HDI_IMAGE
;
6511 if (lpColumn
->mask
& LVCF_ORDER
)
6512 hdi
.mask
|= HDI_ORDER
;
6514 if (lpColumn
->mask
& LVCF_SUBITEM
)
6515 hdi
.mask
|= HDI_LPARAM
;
6517 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
6519 if (lpColumn
->mask
& LVCF_FMT
)
6520 lpColumn
->fmt
= lpColumnInfo
->fmt
;
6522 if (lpColumn
->mask
& LVCF_WIDTH
)
6523 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
6525 if (lpColumn
->mask
& LVCF_IMAGE
)
6526 lpColumn
->iImage
= hdi
.iImage
;
6528 if (lpColumn
->mask
& LVCF_ORDER
)
6529 lpColumn
->iOrder
= hdi
.iOrder
;
6531 if (lpColumn
->mask
& LVCF_SUBITEM
)
6532 lpColumn
->iSubItem
= hdi
.lParam
;
6534 if (lpColumn
->mask
& LVCF_MINWIDTH
)
6535 lpColumn
->cxMin
= lpColumnInfo
->cxMin
;
6540 static inline BOOL
LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
6542 if (!infoPtr
->hwndHeader
) return FALSE
;
6543 return SendMessageW(infoPtr
->hwndHeader
, HDM_GETORDERARRAY
, iCount
, (LPARAM
)lpiArray
);
6548 * Retrieves the column width.
6551 * [I] infoPtr : valid pointer to the listview structure
6552 * [I] int : column index
6555 * SUCCESS : column width
6558 static INT
LISTVIEW_GetColumnWidth(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
6560 INT nColumnWidth
= 0;
6563 TRACE("nColumn=%d\n", nColumn
);
6565 /* we have a 'column' in LIST and REPORT mode only */
6566 switch(infoPtr
->uView
)
6569 nColumnWidth
= infoPtr
->nItemWidth
;
6571 case LV_VIEW_DETAILS
:
6572 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDN_ITEMCHANGED.
6573 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
6574 * HDN_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
6576 hdItem
.mask
= HDI_WIDTH
;
6577 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdItem
))
6579 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr
->hwndSelf
, nColumn
);
6582 nColumnWidth
= hdItem
.cxy
;
6586 TRACE("nColumnWidth=%d\n", nColumnWidth
);
6587 return nColumnWidth
;
6592 * In list or report display mode, retrieves the number of items that can fit
6593 * vertically in the visible area. In icon or small icon display mode,
6594 * retrieves the total number of visible items.
6597 * [I] infoPtr : valid pointer to the listview structure
6600 * Number of fully visible items.
6602 static INT
LISTVIEW_GetCountPerPage(const LISTVIEW_INFO
*infoPtr
)
6604 switch (infoPtr
->uView
)
6607 case LV_VIEW_SMALLICON
:
6608 return infoPtr
->nItemCount
;
6609 case LV_VIEW_DETAILS
:
6610 return LISTVIEW_GetCountPerColumn(infoPtr
);
6612 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
6620 * Retrieves an image list handle.
6623 * [I] infoPtr : valid pointer to the listview structure
6624 * [I] nImageList : image list identifier
6627 * SUCCESS : image list handle
6630 static HIMAGELIST
LISTVIEW_GetImageList(const LISTVIEW_INFO
*infoPtr
, INT nImageList
)
6634 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
6635 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
6636 case LVSIL_STATE
: return infoPtr
->himlState
;
6637 case LVSIL_GROUPHEADER
:
6638 FIXME("LVSIL_GROUPHEADER not supported\n");
6641 WARN("got unknown imagelist index - %d\n", nImageList
);
6646 /* LISTVIEW_GetISearchString */
6650 * Retrieves item attributes.
6653 * [I] hwnd : window handle
6654 * [IO] lpLVItem : item info
6655 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6656 * if FALSE, then lpLVItem is a LPLVITEMA.
6659 * This is the internal 'GetItem' interface -- it tries to
6660 * be smart and avoid text copies, if possible, by modifying
6661 * lpLVItem->pszText to point to the text string. Please note
6662 * that this is not always possible (e.g. OWNERDATA), so on
6663 * entry you *must* supply valid values for pszText, and cchTextMax.
6664 * The only difference to the documented interface is that upon
6665 * return, you should use *only* the lpLVItem->pszText, rather than
6666 * the buffer pointer you provided on input. Most code already does
6667 * that, so it's not a problem.
6668 * For the two cases when the text must be copied (that is,
6669 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
6675 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
6677 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
6678 NMLVDISPINFOW dispInfo
;
6684 TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6686 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
6689 if (lpLVItem
->mask
== 0) return TRUE
;
6690 TRACE("mask=%x\n", lpLVItem
->mask
);
6692 /* make a local copy */
6693 isubitem
= lpLVItem
->iSubItem
;
6695 if (isubitem
&& (lpLVItem
->mask
& LVIF_STATE
))
6696 lpLVItem
->state
= 0;
6698 /* a quick optimization if all we're asked is the focus state
6699 * these queries are worth optimising since they are common,
6700 * and can be answered in constant time, without the heavy accesses */
6701 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
6702 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
6704 lpLVItem
->state
= 0;
6705 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
&& isubitem
== 0)
6706 lpLVItem
->state
|= LVIS_FOCUSED
;
6710 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
6712 /* if the app stores all the data, handle it separately */
6713 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
6715 dispInfo
.item
.state
= 0;
6717 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
6718 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) ||
6719 ((lpLVItem
->mask
& LVIF_STATE
) && (infoPtr
->uCallbackMask
& lpLVItem
->stateMask
)))
6721 UINT mask
= lpLVItem
->mask
;
6723 /* NOTE: copy only fields which we _know_ are initialized, some apps
6724 * depend on the uninitialized fields being 0 */
6725 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
6726 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
6727 dispInfo
.item
.iSubItem
= isubitem
;
6728 if (lpLVItem
->mask
& LVIF_TEXT
)
6730 if (lpLVItem
->mask
& LVIF_NORECOMPUTE
)
6732 dispInfo
.item
.mask
&= ~(LVIF_TEXT
| LVIF_NORECOMPUTE
);
6735 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
6736 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
6739 if (lpLVItem
->mask
& LVIF_STATE
)
6740 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
6741 /* could be zeroed on LVIF_NORECOMPUTE case */
6742 if (dispInfo
.item
.mask
)
6744 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
6745 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
6746 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6748 /* full size structure expected - _WIN32IE >= 0x560 */
6749 *lpLVItem
= dispInfo
.item
;
6751 else if (lpLVItem
->mask
& LVIF_INDENT
)
6753 /* indent member expected - _WIN32IE >= 0x300 */
6754 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
6758 /* minimal structure expected */
6759 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
6761 lpLVItem
->mask
= mask
;
6762 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
6766 /* make sure lParam is zeroed out */
6767 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
6769 /* callback marked pointer required here */
6770 if ((lpLVItem
->mask
& LVIF_TEXT
) && (lpLVItem
->mask
& LVIF_NORECOMPUTE
))
6771 lpLVItem
->pszText
= LPSTR_TEXTCALLBACKW
;
6773 /* we store only a little state, so if we're not asked, we're done */
6774 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
6776 /* if focus is handled by us, report it */
6777 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
6779 lpLVItem
->state
&= ~LVIS_FOCUSED
;
6780 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
6781 lpLVItem
->state
|= LVIS_FOCUSED
;
6784 /* and do the same for selection, if we handle it */
6785 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
6787 lpLVItem
->state
&= ~LVIS_SELECTED
;
6788 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
6789 lpLVItem
->state
|= LVIS_SELECTED
;
6795 /* find the item and subitem structures before we proceed */
6796 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
6797 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
6802 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
6803 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
6806 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
6811 pItemHdr
= &lpItem
->hdr
;
6813 /* Do we need to query the state from the app? */
6814 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
6816 dispInfo
.item
.mask
|= LVIF_STATE
;
6817 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
6820 /* Do we need to enquire about the image? */
6821 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
6822 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
6824 dispInfo
.item
.mask
|= LVIF_IMAGE
;
6825 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
6828 /* Only items support indentation */
6829 if ((lpLVItem
->mask
& LVIF_INDENT
) && lpItem
->iIndent
== I_INDENTCALLBACK
&&
6832 dispInfo
.item
.mask
|= LVIF_INDENT
;
6833 dispInfo
.item
.iIndent
= I_INDENTCALLBACK
;
6836 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
6837 if ((lpLVItem
->mask
& LVIF_TEXT
) && !(lpLVItem
->mask
& LVIF_NORECOMPUTE
) &&
6838 !is_text(pItemHdr
->pszText
))
6840 dispInfo
.item
.mask
|= LVIF_TEXT
;
6841 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
6842 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
6843 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
6844 *dispInfo
.item
.pszText
= '\0';
6847 /* If we don't have all the requested info, query the application */
6848 if (dispInfo
.item
.mask
)
6850 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
6851 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
6852 dispInfo
.item
.lParam
= lpItem
->lParam
;
6853 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
6854 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
6857 /* we should not store values for subitems */
6858 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
6860 /* Now, handle the iImage field */
6861 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
6863 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
6864 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
6865 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
6867 else if (lpLVItem
->mask
& LVIF_IMAGE
)
6869 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
6870 lpLVItem
->iImage
= pItemHdr
->iImage
;
6872 lpLVItem
->iImage
= 0;
6875 /* The pszText field */
6876 if (dispInfo
.item
.mask
& LVIF_TEXT
)
6878 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
6879 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
6881 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
6883 else if (lpLVItem
->mask
& LVIF_TEXT
)
6885 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
6886 if (isW
|| !is_text(pItemHdr
->pszText
)) lpLVItem
->pszText
= pItemHdr
->pszText
;
6887 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
6890 /* Next is the lParam field */
6891 if (dispInfo
.item
.mask
& LVIF_PARAM
)
6893 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
6894 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
6895 lpItem
->lParam
= dispInfo
.item
.lParam
;
6897 else if (lpLVItem
->mask
& LVIF_PARAM
)
6898 lpLVItem
->lParam
= lpItem
->lParam
;
6900 /* if this is a subitem, we're done */
6901 if (isubitem
) return TRUE
;
6903 /* ... the state field (this one is different due to uCallbackmask) */
6904 if (lpLVItem
->mask
& LVIF_STATE
)
6906 lpLVItem
->state
= lpItem
->state
& lpLVItem
->stateMask
;
6907 if (dispInfo
.item
.mask
& LVIF_STATE
)
6909 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
6910 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
6912 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
6914 lpLVItem
->state
&= ~LVIS_FOCUSED
;
6915 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
6916 lpLVItem
->state
|= LVIS_FOCUSED
;
6918 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
6920 lpLVItem
->state
&= ~LVIS_SELECTED
;
6921 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
6922 lpLVItem
->state
|= LVIS_SELECTED
;
6926 /* and last, but not least, the indent field */
6927 if (dispInfo
.item
.mask
& LVIF_INDENT
)
6929 lpLVItem
->iIndent
= dispInfo
.item
.iIndent
;
6930 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && lpItem
->iIndent
== I_INDENTCALLBACK
)
6931 lpItem
->iIndent
= dispInfo
.item
.iIndent
;
6933 else if (lpLVItem
->mask
& LVIF_INDENT
)
6935 lpLVItem
->iIndent
= lpItem
->iIndent
;
6943 * Retrieves item attributes.
6946 * [I] hwnd : window handle
6947 * [IO] lpLVItem : item info
6948 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6949 * if FALSE, then lpLVItem is a LPLVITEMA.
6952 * This is the external 'GetItem' interface -- it properly copies
6953 * the text in the provided buffer.
6959 static BOOL
LISTVIEW_GetItemExtT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
6964 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
6967 pszText
= lpLVItem
->pszText
;
6968 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
6969 if (bResult
&& (lpLVItem
->mask
& LVIF_TEXT
) && lpLVItem
->pszText
!= pszText
)
6971 if (lpLVItem
->pszText
!= LPSTR_TEXTCALLBACKW
)
6972 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
6974 pszText
= LPSTR_TEXTCALLBACKW
;
6976 lpLVItem
->pszText
= pszText
;
6984 * Retrieves the position (upper-left) of the listview control item.
6985 * Note that for LVS_ICON style, the upper-left is that of the icon
6986 * and not the bounding box.
6989 * [I] infoPtr : valid pointer to the listview structure
6990 * [I] nItem : item index
6991 * [O] lpptPosition : coordinate information
6997 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
7001 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
7003 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
7005 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7006 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
7008 if (infoPtr
->uView
== LV_VIEW_ICON
)
7010 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
7011 lpptPosition
->y
+= ICON_TOP_PADDING
;
7013 lpptPosition
->x
+= Origin
.x
;
7014 lpptPosition
->y
+= Origin
.y
;
7016 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition
));
7023 * Retrieves the bounding rectangle for a listview control item.
7026 * [I] infoPtr : valid pointer to the listview structure
7027 * [I] nItem : item index
7028 * [IO] lprc : bounding rectangle coordinates
7029 * lprc->left specifies the portion of the item for which the bounding
7030 * rectangle will be retrieved.
7032 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
7033 * including the icon and label.
7036 * * Experiment shows that native control returns:
7037 * * width = min (48, length of text line)
7038 * * .left = position.x - (width - iconsize.cx)/2
7039 * * .right = .left + width
7040 * * height = #lines of text * ntmHeight + icon height + 8
7041 * * .top = position.y - 2
7042 * * .bottom = .top + height
7043 * * separation between items .y = itemSpacing.cy - height
7044 * * .x = itemSpacing.cx - width
7045 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
7048 * * Experiment shows that native control returns:
7049 * * width = iconSize.cx + 16
7050 * * .left = position.x - (width - iconsize.cx)/2
7051 * * .right = .left + width
7052 * * height = iconSize.cy + 4
7053 * * .top = position.y - 2
7054 * * .bottom = .top + height
7055 * * separation between items .y = itemSpacing.cy - height
7056 * * .x = itemSpacing.cx - width
7057 * LVIR_LABEL Returns the bounding rectangle of the item text.
7060 * * Experiment shows that native control returns:
7061 * * width = text length
7062 * * .left = position.x - width/2
7063 * * .right = .left + width
7064 * * height = ntmH * linecount + 2
7065 * * .top = position.y + iconSize.cy + 6
7066 * * .bottom = .top + height
7067 * * separation between items .y = itemSpacing.cy - height
7068 * * .x = itemSpacing.cx - width
7069 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
7070 * rectangles, but excludes columns in report view.
7077 * Note that the bounding rectangle of the label in the LVS_ICON view depends
7078 * upon whether the window has the focus currently and on whether the item
7079 * is the one with the focus. Ensure that the control's record of which
7080 * item has the focus agrees with the items' records.
7082 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
7084 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
7085 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
7086 POINT Position
, Origin
;
7090 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
7092 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
7094 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7095 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
7097 /* Be smart and try to figure out the minimum we have to do */
7098 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
7099 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
7100 if (infoPtr
->uView
== LV_VIEW_ICON
&& lprc
->left
!= LVIR_ICON
&&
7101 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
7102 oversizedBox
= TRUE
;
7104 /* get what we need from the item before hand, so we make
7105 * only one request. This can speed up things, if data
7106 * is stored on the app side */
7108 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
7109 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
7110 lvItem
.iItem
= nItem
;
7111 lvItem
.iSubItem
= 0;
7112 lvItem
.pszText
= szDispText
;
7113 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7114 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
7115 /* we got the state already up, simulate it here, to avoid a reget */
7116 if (infoPtr
->uView
== LV_VIEW_ICON
&& (lprc
->left
!= LVIR_ICON
))
7118 lvItem
.mask
|= LVIF_STATE
;
7119 lvItem
.stateMask
= LVIS_FOCUSED
;
7120 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
7123 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
7124 lprc
->left
= LVIR_BOUNDS
;
7130 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
7134 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, NULL
, lprc
);
7138 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
7141 case LVIR_SELECTBOUNDS
:
7142 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, lprc
, NULL
, NULL
, NULL
);
7146 WARN("Unknown value: %d\n", lprc
->left
);
7150 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7152 if (mode
!= LVIR_BOUNDS
)
7153 OffsetRect(lprc
, Origin
.x
+ LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
,
7154 Position
.y
+ Origin
.y
);
7156 OffsetRect(lprc
, Origin
.x
, Position
.y
+ Origin
.y
);
7159 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
7161 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc
));
7168 * Retrieves the spacing between listview control items.
7171 * [I] infoPtr : valid pointer to the listview structure
7172 * [IO] lprc : rectangle to receive the output
7173 * on input, lprc->top = nSubItem
7174 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
7176 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
7177 * not only those of the first column.
7183 static BOOL
LISTVIEW_GetSubItemRect(const LISTVIEW_INFO
*infoPtr
, INT item
, LPRECT lprc
)
7185 RECT rect
= { 0, 0, 0, 0 };
7189 if (!lprc
) return FALSE
;
7191 TRACE("(item=%d, subitem=%d, type=%d)\n", item
, lprc
->top
, lprc
->left
);
7192 /* Subitem of '0' means item itself, and this works for all control view modes */
7194 return LISTVIEW_GetItemRect(infoPtr
, item
, lprc
);
7196 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) return FALSE
;
7198 LISTVIEW_GetOrigin(infoPtr
, &origin
);
7199 /* this works for any item index, no matter if it exists or not */
7200 y
= item
* infoPtr
->nItemHeight
+ origin
.y
;
7202 if (infoPtr
->hwndHeader
&& SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMRECT
, lprc
->top
, (LPARAM
)&rect
))
7205 rect
.bottom
= infoPtr
->nItemHeight
;
7209 /* Native implementation is broken for this case and garbage is left for left and right fields,
7210 we zero them to get predictable output */
7211 lprc
->left
= lprc
->right
= lprc
->top
= 0;
7212 lprc
->bottom
= infoPtr
->nItemHeight
;
7213 OffsetRect(lprc
, origin
.x
, y
);
7214 TRACE("return rect %s\n", wine_dbgstr_rect(lprc
));
7222 /* it doesn't matter if main item actually has an icon, if imagelist is set icon width is returned */
7223 if (infoPtr
->himlSmall
)
7224 rect
.right
= rect
.left
+ infoPtr
->iconSize
.cx
;
7226 rect
.right
= rect
.left
;
7228 rect
.bottom
= rect
.top
+ infoPtr
->iconSize
.cy
;
7236 ERR("Unknown bounds=%d\n", lprc
->left
);
7240 OffsetRect(&rect
, origin
.x
, y
);
7242 TRACE("return rect %s\n", wine_dbgstr_rect(lprc
));
7249 * Retrieves the spacing between listview control items.
7252 * [I] infoPtr : valid pointer to the listview structure
7253 * [I] bSmall : flag for small or large icon
7256 * Horizontal + vertical spacing
7258 static LONG
LISTVIEW_GetItemSpacing(const LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
7264 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
7268 if (infoPtr
->uView
== LV_VIEW_ICON
)
7269 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
7271 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
7278 * Retrieves the state of a listview control item.
7281 * [I] infoPtr : valid pointer to the listview structure
7282 * [I] nItem : item index
7283 * [I] uMask : state mask
7286 * State specified by the mask.
7288 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
7292 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
7294 lvItem
.iItem
= nItem
;
7295 lvItem
.iSubItem
= 0;
7296 lvItem
.mask
= LVIF_STATE
;
7297 lvItem
.stateMask
= uMask
;
7298 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
7300 return lvItem
.state
& uMask
;
7305 * Retrieves the text of a listview control item or subitem.
7308 * [I] hwnd : window handle
7309 * [I] nItem : item index
7310 * [IO] lpLVItem : item information
7311 * [I] isW : TRUE if lpLVItem is Unicode
7314 * SUCCESS : string length
7317 static INT
LISTVIEW_GetItemTextT(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
7319 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
7321 lpLVItem
->mask
= LVIF_TEXT
;
7322 lpLVItem
->iItem
= nItem
;
7323 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
7325 return textlenT(lpLVItem
->pszText
, isW
);
7330 * Searches for an item based on properties + relationships.
7333 * [I] infoPtr : valid pointer to the listview structure
7334 * [I] nItem : item index
7335 * [I] uFlags : relationship flag
7338 * SUCCESS : item index
7341 static INT
LISTVIEW_GetNextItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
7344 LVFINDINFOW lvFindInfo
;
7345 INT nCountPerColumn
;
7349 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
7350 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
7352 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
7354 if (uFlags
& LVNI_CUT
)
7357 if (uFlags
& LVNI_DROPHILITED
)
7358 uMask
|= LVIS_DROPHILITED
;
7360 if (uFlags
& LVNI_FOCUSED
)
7361 uMask
|= LVIS_FOCUSED
;
7363 if (uFlags
& LVNI_SELECTED
)
7364 uMask
|= LVIS_SELECTED
;
7366 /* if we're asked for the focused item, that's only one,
7367 * so it's worth optimizing */
7368 if (uFlags
& LVNI_FOCUSED
)
7370 if ((LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) != uMask
) return -1;
7371 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
7374 if (uFlags
& LVNI_ABOVE
)
7376 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
7381 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7387 /* Special case for autoarrange - move 'til the top of a list */
7388 if (is_autoarrange(infoPtr
))
7390 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7391 while (nItem
- nCountPerRow
>= 0)
7393 nItem
-= nCountPerRow
;
7394 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7399 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7400 lvFindInfo
.vkDirection
= VK_UP
;
7401 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7402 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7404 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7409 else if (uFlags
& LVNI_BELOW
)
7411 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
7413 while (nItem
< infoPtr
->nItemCount
)
7416 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7422 /* Special case for autoarrange - move 'til the bottom of a list */
7423 if (is_autoarrange(infoPtr
))
7425 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7426 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
7428 nItem
+= nCountPerRow
;
7429 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7434 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7435 lvFindInfo
.vkDirection
= VK_DOWN
;
7436 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7437 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7439 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7444 else if (uFlags
& LVNI_TOLEFT
)
7446 if (infoPtr
->uView
== LV_VIEW_LIST
)
7448 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
7449 while (nItem
- nCountPerColumn
>= 0)
7451 nItem
-= nCountPerColumn
;
7452 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7456 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
7458 /* Special case for autoarrange - move 'til the beginning of a row */
7459 if (is_autoarrange(infoPtr
))
7461 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7462 while (nItem
% nCountPerRow
> 0)
7465 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7470 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7471 lvFindInfo
.vkDirection
= VK_LEFT
;
7472 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7473 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7475 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7480 else if (uFlags
& LVNI_TORIGHT
)
7482 if (infoPtr
->uView
== LV_VIEW_LIST
)
7484 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
7485 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
7487 nItem
+= nCountPerColumn
;
7488 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7492 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
7494 /* Special case for autoarrange - move 'til the end of a row */
7495 if (is_autoarrange(infoPtr
))
7497 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7498 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
7501 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7506 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7507 lvFindInfo
.vkDirection
= VK_RIGHT
;
7508 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7509 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7511 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7520 /* search by index */
7521 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
7523 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
7531 /* LISTVIEW_GetNumberOfWorkAreas */
7535 * Retrieves the origin coordinates when in icon or small icon display mode.
7538 * [I] infoPtr : valid pointer to the listview structure
7539 * [O] lpptOrigin : coordinate information
7544 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
7546 INT nHorzPos
= 0, nVertPos
= 0;
7547 SCROLLINFO scrollInfo
;
7549 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7550 scrollInfo
.fMask
= SIF_POS
;
7552 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
7553 nHorzPos
= scrollInfo
.nPos
;
7554 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
7555 nVertPos
= scrollInfo
.nPos
;
7557 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
7559 lpptOrigin
->x
= infoPtr
->rcList
.left
;
7560 lpptOrigin
->y
= infoPtr
->rcList
.top
;
7561 if (infoPtr
->uView
== LV_VIEW_LIST
)
7562 nHorzPos
*= infoPtr
->nItemWidth
;
7563 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7564 nVertPos
*= infoPtr
->nItemHeight
;
7566 lpptOrigin
->x
-= nHorzPos
;
7567 lpptOrigin
->y
-= nVertPos
;
7569 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin
));
7574 * Retrieves the width of a string.
7577 * [I] hwnd : window handle
7578 * [I] lpszText : text string to process
7579 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
7582 * SUCCESS : string width (in pixels)
7585 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
7590 if (is_text(lpszText
))
7592 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
7593 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
7594 HFONT hOldFont
= SelectObject(hdc
, hFont
);
7597 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
7599 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
7600 SelectObject(hdc
, hOldFont
);
7601 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7603 return stringSize
.cx
;
7608 * Determines which listview item is located at the specified position.
7611 * [I] infoPtr : valid pointer to the listview structure
7612 * [IO] lpht : hit test information
7613 * [I] subitem : fill out iSubItem.
7614 * [I] select : return the index only if the hit selects the item
7617 * (mm 20001022): We must not allow iSubItem to be touched, for
7618 * an app might pass only a structure with space up to iItem!
7619 * (MS Office 97 does that for instance in the file open dialog)
7622 * SUCCESS : item index
7625 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
7627 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
7628 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
7629 POINT Origin
, Position
, opt
;
7635 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht
->pt
), subitem
, select
);
7639 if (subitem
) lpht
->iSubItem
= 0;
7641 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7643 /* set whole list relation flags */
7644 if (subitem
&& infoPtr
->uView
== LV_VIEW_DETAILS
)
7646 /* LVM_SUBITEMHITTEST checks left bound of possible client area */
7647 if (infoPtr
->rcList
.left
> lpht
->pt
.x
&& Origin
.x
< lpht
->pt
.x
)
7648 lpht
->flags
|= LVHT_TOLEFT
;
7650 if (lpht
->pt
.y
< infoPtr
->rcList
.top
&& lpht
->pt
.y
>= 0)
7651 opt
.y
= lpht
->pt
.y
+ infoPtr
->rcList
.top
;
7655 if (infoPtr
->rcList
.bottom
< opt
.y
)
7656 lpht
->flags
|= LVHT_BELOW
;
7660 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
7661 lpht
->flags
|= LVHT_TOLEFT
;
7662 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
7663 lpht
->flags
|= LVHT_TORIGHT
;
7665 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
7666 lpht
->flags
|= LVHT_ABOVE
;
7667 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
7668 lpht
->flags
|= LVHT_BELOW
;
7671 /* even if item is invalid try to find subitem */
7672 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& subitem
)
7677 opt
.x
= lpht
->pt
.x
- Origin
.x
;
7679 lpht
->iSubItem
= -1;
7680 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
7682 pRect
= &LISTVIEW_GetColumnInfo(infoPtr
, j
)->rcHeader
;
7684 if ((opt
.x
>= pRect
->left
) && (opt
.x
< pRect
->right
))
7690 TRACE("lpht->iSubItem=%d\n", lpht
->iSubItem
);
7692 /* if we're outside horizontal columns bounds there's nothing to test further */
7693 if (lpht
->iSubItem
== -1)
7696 lpht
->flags
= LVHT_NOWHERE
;
7701 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
7702 if (lpht
->flags
) return -1;
7704 lpht
->flags
|= LVHT_NOWHERE
;
7706 /* first deal with the large items */
7707 rcSearch
.left
= lpht
->pt
.x
;
7708 rcSearch
.top
= lpht
->pt
.y
;
7709 rcSearch
.right
= rcSearch
.left
+ 1;
7710 rcSearch
.bottom
= rcSearch
.top
+ 1;
7712 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
7713 iterator_next(&i
); /* go to first item in the sequence */
7715 iterator_destroy(&i
);
7717 TRACE("lpht->iItem=%d\n", iItem
);
7718 if (iItem
== -1) return -1;
7720 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
7721 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
7722 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
7723 if (infoPtr
->uView
== LV_VIEW_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
7724 lvItem
.iItem
= iItem
;
7725 lvItem
.iSubItem
= subitem
? lpht
->iSubItem
: 0;
7726 lvItem
.pszText
= szDispText
;
7727 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7728 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
7729 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
7731 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
7732 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
7733 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
7735 if (lpht
->pt
.y
< infoPtr
->rcList
.top
&& lpht
->pt
.y
>= 0)
7736 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
+ infoPtr
->rcList
.top
;
7738 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
7740 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7743 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
7744 opt
.x
= lpht
->pt
.x
- Origin
.x
;
7748 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
7749 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
7751 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds
));
7752 if (!PtInRect(&rcBounds
, opt
)) return -1;
7754 /* That's a special case - row rectangle is used as item rectangle and
7755 returned flags contain all item parts. */
7756 is_fullrow
= (infoPtr
->uView
== LV_VIEW_DETAILS
) && ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) || (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
));
7758 if (PtInRect(&rcIcon
, opt
))
7759 lpht
->flags
|= LVHT_ONITEMICON
;
7760 else if (PtInRect(&rcLabel
, opt
))
7761 lpht
->flags
|= LVHT_ONITEMLABEL
;
7762 else if (infoPtr
->himlState
&& PtInRect(&rcState
, opt
))
7763 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
7764 if (is_fullrow
&& !(lpht
->flags
& LVHT_ONITEM
))
7766 lpht
->flags
= LVHT_ONITEM
| LVHT_ABOVE
;
7768 if (lpht
->flags
& LVHT_ONITEM
)
7769 lpht
->flags
&= ~LVHT_NOWHERE
;
7770 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
7772 if (select
&& !is_fullrow
)
7774 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7776 /* get main item bounds */
7777 lvItem
.iSubItem
= 0;
7778 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
7779 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
7780 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
7782 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
7784 return lpht
->iItem
= iItem
;
7789 * Inserts a new item in the listview control.
7792 * [I] infoPtr : valid pointer to the listview structure
7793 * [I] lpLVItem : item information
7794 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
7797 * SUCCESS : new item index
7800 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
7807 BOOL is_sorted
, has_changed
;
7809 HWND hwndSelf
= infoPtr
->hwndSelf
;
7811 TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
7813 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
7815 /* make sure it's an item, and not a subitem; cannot insert a subitem */
7816 if (!lpLVItem
|| lpLVItem
->iSubItem
) return -1;
7818 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
7820 if (!(lpItem
= Alloc(sizeof(ITEM_INFO
)))) return -1;
7822 /* insert item in listview control data structure */
7823 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
7824 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
7826 /* link with id struct */
7827 if (!(lpID
= Alloc(sizeof(ITEM_ID
)))) goto fail
;
7829 lpID
->item
= hdpaSubItems
;
7830 lpID
->id
= get_next_itemid(infoPtr
);
7831 if ( DPA_InsertPtr(infoPtr
->hdpaItemIds
, infoPtr
->nItemCount
, lpID
) == -1) goto fail
;
7833 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
7834 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
7836 if (lpLVItem
->iItem
< 0 && !is_sorted
) return -1;
7838 /* calculate new item index */
7846 textW
= textdupTtoW(lpLVItem
->pszText
, isW
);
7848 while (i
< infoPtr
->nItemCount
)
7850 hItem
= DPA_GetPtr( infoPtr
->hdpaItems
, i
);
7851 item_s
= DPA_GetPtr(hItem
, 0);
7853 cmpv
= textcmpWT(item_s
->hdr
.pszText
, textW
, TRUE
);
7854 if (infoPtr
->dwStyle
& LVS_SORTDESCENDING
) cmpv
*= -1;
7856 if (cmpv
>= 0) break;
7860 textfreeT(textW
, isW
);
7865 nItem
= min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
7867 TRACE("inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
7868 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
7869 if (nItem
== -1) goto fail
;
7870 infoPtr
->nItemCount
++;
7872 /* shift indices first so they don't get tangled */
7873 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
7875 /* set the item attributes */
7876 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
7878 /* full size structure expected - _WIN32IE >= 0x560 */
7881 else if (lpLVItem
->mask
& LVIF_INDENT
)
7883 /* indent member expected - _WIN32IE >= 0x300 */
7884 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
7888 /* minimal structure expected */
7889 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
7892 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
7894 if (item
.mask
& LVIF_STATE
)
7896 item
.stateMask
|= LVIS_STATEIMAGEMASK
;
7897 item
.state
&= ~LVIS_STATEIMAGEMASK
;
7898 item
.state
|= INDEXTOSTATEIMAGEMASK(1);
7902 item
.mask
|= LVIF_STATE
;
7903 item
.stateMask
= LVIS_STATEIMAGEMASK
;
7904 item
.state
= INDEXTOSTATEIMAGEMASK(1);
7908 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
7910 /* make room for the position, if we are in the right mode */
7911 if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
7913 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
7915 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
7917 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
7922 /* send LVN_INSERTITEM notification */
7923 memset(&nmlv
, 0, sizeof(NMLISTVIEW
));
7925 nmlv
.lParam
= lpItem
->lParam
;
7926 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
7927 if (!IsWindow(hwndSelf
))
7930 /* align items (set position of each item) */
7931 if (infoPtr
->uView
== LV_VIEW_SMALLICON
|| infoPtr
->uView
== LV_VIEW_ICON
)
7935 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
7936 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
7938 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
7940 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
7943 /* now is the invalidation fun */
7944 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
7948 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
7949 LISTVIEW_ShiftFocus(infoPtr
, infoPtr
->nFocusedItem
, nItem
, -1);
7950 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
7951 infoPtr
->nItemCount
--;
7953 DPA_DeletePtr(hdpaSubItems
, 0);
7954 DPA_Destroy (hdpaSubItems
);
7961 * Checks item visibility.
7964 * [I] infoPtr : valid pointer to the listview structure
7965 * [I] nFirst : item index to check for
7968 * Item visible : TRUE
7969 * Item invisible or failure : FALSE
7971 static BOOL
LISTVIEW_IsItemVisible(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
7973 POINT Origin
, Position
;
7978 TRACE("nItem=%d\n", nItem
);
7980 if (nItem
< 0 || nItem
>= DPA_GetPtrCount(infoPtr
->hdpaItems
)) return FALSE
;
7982 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7983 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
7984 rcItem
.left
= Position
.x
+ Origin
.x
;
7985 rcItem
.top
= Position
.y
+ Origin
.y
;
7986 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
7987 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
7989 hdc
= GetDC(infoPtr
->hwndSelf
);
7990 if (!hdc
) return FALSE
;
7991 ret
= RectVisible(hdc
, &rcItem
);
7992 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7999 * Redraws a range of items.
8002 * [I] infoPtr : valid pointer to the listview structure
8003 * [I] nFirst : first item
8004 * [I] nLast : last item
8010 static BOOL
LISTVIEW_RedrawItems(const LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
8014 for (i
= max(nFirst
, 0); i
<= min(nLast
, infoPtr
->nItemCount
- 1); i
++)
8015 LISTVIEW_InvalidateItem(infoPtr
, i
);
8022 * Scroll the content of a listview.
8025 * [I] infoPtr : valid pointer to the listview structure
8026 * [I] dx : horizontal scroll amount in pixels
8027 * [I] dy : vertical scroll amount in pixels
8034 * If the control is in report view (LV_VIEW_DETAILS) the control can
8035 * be scrolled only in line increments. "dy" will be rounded to the
8036 * nearest number of pixels that are a whole line. Ex: if line height
8037 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
8038 * is passed, then the scroll will be 0. (per MSDN 7/2002)
8040 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
8042 switch(infoPtr
->uView
) {
8043 case LV_VIEW_DETAILS
:
8044 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
8045 dy
/= infoPtr
->nItemHeight
;
8048 if (dy
!= 0) return FALSE
;
8054 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
);
8055 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
);
8062 * Sets the background color.
8065 * [I] infoPtr : valid pointer to the listview structure
8066 * [I] color : background color
8072 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF color
)
8074 TRACE("(color=%x)\n", color
);
8076 if(infoPtr
->clrBk
!= color
) {
8077 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
8078 infoPtr
->clrBk
= color
;
8079 if (color
== CLR_NONE
)
8080 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
8083 infoPtr
->hBkBrush
= CreateSolidBrush(color
);
8084 infoPtr
->dwLvExStyle
&= ~LVS_EX_TRANSPARENTBKGND
;
8091 /* LISTVIEW_SetBkImage */
8093 /*** Helper for {Insert,Set}ColumnT *only* */
8094 static void column_fill_hditem(const LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
,
8095 const LVCOLUMNW
*lpColumn
, BOOL isW
)
8097 if (lpColumn
->mask
& LVCF_FMT
)
8099 /* format member is valid */
8100 lphdi
->mask
|= HDI_FORMAT
;
8102 /* set text alignment (leftmost column must be left-aligned) */
8103 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
8104 lphdi
->fmt
|= HDF_LEFT
;
8105 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
8106 lphdi
->fmt
|= HDF_RIGHT
;
8107 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
8108 lphdi
->fmt
|= HDF_CENTER
;
8110 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
8111 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
8113 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
8115 lphdi
->fmt
|= HDF_IMAGE
;
8116 lphdi
->iImage
= I_IMAGECALLBACK
;
8119 if (lpColumn
->fmt
& LVCFMT_FIXED_WIDTH
)
8120 lphdi
->fmt
|= HDF_FIXEDWIDTH
;
8123 if (lpColumn
->mask
& LVCF_WIDTH
)
8125 lphdi
->mask
|= HDI_WIDTH
;
8126 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
8128 /* make it fill the remainder of the controls width */
8132 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
8134 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
8135 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
8138 /* retrieve the layout of the header */
8139 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
8140 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, wine_dbgstr_rect(&rcHeader
));
8142 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
8145 lphdi
->cxy
= lpColumn
->cx
;
8148 if (lpColumn
->mask
& LVCF_TEXT
)
8150 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
8151 lphdi
->fmt
|= HDF_STRING
;
8152 lphdi
->pszText
= lpColumn
->pszText
;
8153 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
8156 if (lpColumn
->mask
& LVCF_IMAGE
)
8158 lphdi
->mask
|= HDI_IMAGE
;
8159 lphdi
->iImage
= lpColumn
->iImage
;
8162 if (lpColumn
->mask
& LVCF_ORDER
)
8164 lphdi
->mask
|= HDI_ORDER
;
8165 lphdi
->iOrder
= lpColumn
->iOrder
;
8172 * Inserts a new column.
8175 * [I] infoPtr : valid pointer to the listview structure
8176 * [I] nColumn : column index
8177 * [I] lpColumn : column information
8178 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
8181 * SUCCESS : new column index
8184 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
8185 const LVCOLUMNW
*lpColumn
, BOOL isW
)
8187 COLUMN_INFO
*lpColumnInfo
;
8191 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
8193 if (!lpColumn
|| nColumn
< 0) return -1;
8194 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
8196 ZeroMemory(&hdi
, sizeof(HDITEMW
));
8197 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
8200 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
8201 * (can be seen in SPY) otherwise column never gets added.
8203 if (!(lpColumn
->mask
& LVCF_WIDTH
)) {
8204 hdi
.mask
|= HDI_WIDTH
;
8209 * when the iSubItem is available Windows copies it to the header lParam. It seems
8210 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
8212 if (lpColumn
->mask
& LVCF_SUBITEM
)
8214 hdi
.mask
|= HDI_LPARAM
;
8215 hdi
.lParam
= lpColumn
->iSubItem
;
8218 /* create header if not present */
8219 LISTVIEW_CreateHeader(infoPtr
);
8220 if (!(LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
) &&
8221 (infoPtr
->uView
== LV_VIEW_DETAILS
) && (WS_VISIBLE
& infoPtr
->dwStyle
))
8223 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
8226 /* insert item in header control */
8227 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
8228 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
8229 nColumn
, (LPARAM
)&hdi
);
8230 if (nNewColumn
== -1) return -1;
8231 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
8233 /* create our own column info */
8234 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
8235 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
8237 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
8238 if (lpColumn
->mask
& LVCF_MINWIDTH
) lpColumnInfo
->cxMin
= lpColumn
->cxMin
;
8239 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMRECT
, nNewColumn
, (LPARAM
)&lpColumnInfo
->rcHeader
))
8242 /* now we have to actually adjust the data */
8243 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
8245 SUBITEM_INFO
*lpSubItem
;
8251 item
.iSubItem
= nNewColumn
;
8252 item
.mask
= LVIF_TEXT
| LVIF_IMAGE
;
8253 item
.iImage
= I_IMAGECALLBACK
;
8254 item
.pszText
= LPSTR_TEXTCALLBACKW
;
8256 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
8258 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
8259 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
8261 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
8262 if (lpSubItem
->iSubItem
>= nNewColumn
)
8263 lpSubItem
->iSubItem
++;
8266 /* add new subitem for each item */
8268 set_sub_item(infoPtr
, &item
, isW
, &changed
);
8272 /* make space for the new column */
8273 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
8274 LISTVIEW_UpdateItemSize(infoPtr
);
8279 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
8282 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
8290 * Sets the attributes of a header item.
8293 * [I] infoPtr : valid pointer to the listview structure
8294 * [I] nColumn : column index
8295 * [I] lpColumn : column attributes
8296 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
8302 static BOOL
LISTVIEW_SetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
,
8303 const LVCOLUMNW
*lpColumn
, BOOL isW
)
8305 HDITEMW hdi
, hdiget
;
8308 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
8310 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
8312 ZeroMemory(&hdi
, sizeof(HDITEMW
));
8313 if (lpColumn
->mask
& LVCF_FMT
)
8315 hdi
.mask
|= HDI_FORMAT
;
8316 hdiget
.mask
= HDI_FORMAT
;
8317 if (SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdiget
))
8318 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
8320 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
8322 /* set header item attributes */
8323 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, nColumn
, (LPARAM
)&hdi
);
8324 if (!bResult
) return FALSE
;
8326 if (lpColumn
->mask
& LVCF_FMT
)
8328 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
8329 INT oldFmt
= lpColumnInfo
->fmt
;
8331 lpColumnInfo
->fmt
= lpColumn
->fmt
;
8332 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
8334 if (infoPtr
->uView
== LV_VIEW_DETAILS
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
8338 if (lpColumn
->mask
& LVCF_MINWIDTH
)
8339 LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->cxMin
= lpColumn
->cxMin
;
8346 * Sets the column order array
8349 * [I] infoPtr : valid pointer to the listview structure
8350 * [I] iCount : number of elements in column order array
8351 * [I] lpiArray : pointer to column order array
8357 static BOOL
LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
8359 if (!infoPtr
->hwndHeader
) return FALSE
;
8360 infoPtr
->colRectsDirty
= TRUE
;
8361 return SendMessageW(infoPtr
->hwndHeader
, HDM_SETORDERARRAY
, iCount
, (LPARAM
)lpiArray
);
8366 * Sets the width of a column
8369 * [I] infoPtr : valid pointer to the listview structure
8370 * [I] nColumn : column index
8371 * [I] cx : column width
8377 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
8379 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
8383 TRACE("(nColumn=%d, cx=%d)\n", nColumn
, cx
);
8385 /* set column width only if in report or list mode */
8386 if (infoPtr
->uView
!= LV_VIEW_DETAILS
&& infoPtr
->uView
!= LV_VIEW_LIST
) return FALSE
;
8388 /* take care of invalid cx values - LVSCW_AUTOSIZE_* values are negative,
8389 with _USEHEADER being the lowest */
8390 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& cx
< LVSCW_AUTOSIZE_USEHEADER
) cx
= LVSCW_AUTOSIZE
;
8391 else if (infoPtr
->uView
== LV_VIEW_LIST
&& cx
<= 0) return FALSE
;
8393 /* resize all columns if in LV_VIEW_LIST mode */
8394 if(infoPtr
->uView
== LV_VIEW_LIST
)
8396 infoPtr
->nItemWidth
= cx
;
8397 LISTVIEW_InvalidateList(infoPtr
);
8401 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
8403 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
8408 lvItem
.mask
= LVIF_TEXT
;
8410 lvItem
.iSubItem
= nColumn
;
8411 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
8412 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
8414 lvItem
.pszText
= szDispText
;
8415 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
8416 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
8417 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
8419 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
8420 max_cx
+= infoPtr
->iconSize
.cx
;
8421 max_cx
+= TRAILING_LABEL_PADDING
;
8422 if (nColumn
== 0 && (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
))
8423 max_cx
+= GetSystemMetrics(SM_CXSMICON
);
8426 /* autosize based on listview items width */
8427 if(cx
== LVSCW_AUTOSIZE
)
8429 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
8431 /* if iCol is the last column make it fill the remainder of the controls width */
8432 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
8437 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
8438 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
8440 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
8444 /* Despite what the MS docs say, if this is not the last
8445 column, then MS resizes the column to the width of the
8446 largest text string in the column, including headers
8447 and items. This is different from LVSCW_AUTOSIZE in that
8448 LVSCW_AUTOSIZE ignores the header string length. */
8451 /* retrieve header text */
8452 hdi
.mask
= HDI_TEXT
|HDI_FORMAT
|HDI_IMAGE
|HDI_BITMAP
;
8453 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
8454 hdi
.pszText
= szDispText
;
8455 if (SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdi
))
8457 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
8458 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
8459 HIMAGELIST himl
= (HIMAGELIST
)SendMessageW(infoPtr
->hwndHeader
, HDM_GETIMAGELIST
, 0, 0);
8460 INT bitmap_margin
= 0;
8463 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
8464 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
8466 if (hdi
.fmt
& (HDF_IMAGE
|HDF_BITMAP
))
8467 bitmap_margin
= SendMessageW(infoPtr
->hwndHeader
, HDM_GETBITMAPMARGIN
, 0, 0);
8469 if ((hdi
.fmt
& HDF_IMAGE
) && himl
)
8471 INT icon_cx
, icon_cy
;
8473 if (!ImageList_GetIconSize(himl
, &icon_cx
, &icon_cy
))
8474 cx
+= icon_cx
+ 2*bitmap_margin
;
8476 else if (hdi
.fmt
& HDF_BITMAP
)
8480 GetObjectW(hdi
.hbm
, sizeof(BITMAP
), &bmp
);
8481 cx
+= bmp
.bmWidth
+ 2*bitmap_margin
;
8484 SelectObject(hdc
, old_font
);
8485 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
8487 cx
= max (cx
, max_cx
);
8491 if (cx
< 0) return FALSE
;
8493 /* call header to update the column change */
8494 hdi
.mask
= HDI_WIDTH
;
8495 hdi
.cxy
= max(cx
, LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->cxMin
);
8496 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
8497 return SendMessageW(infoPtr
->hwndHeader
, HDM_SETITEMW
, nColumn
, (LPARAM
)&hdi
);
8501 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
8504 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO
*infoPtr
)
8507 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
8509 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
8510 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
8513 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
8514 ILC_COLOR
| ILC_MASK
, 2, 2);
8515 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
8516 hdc
= CreateCompatibleDC(hdc_wnd
);
8517 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
8518 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
8519 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
8521 SetRect(&rc
, 0, 0, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
8522 hbm_orig
= SelectObject(hdc
, hbm_mask
);
8523 FillRect(hdc
, &rc
, hbr_white
);
8524 InflateRect(&rc
, -2, -2);
8525 FillRect(hdc
, &rc
, hbr_black
);
8527 SelectObject(hdc
, hbm_im
);
8528 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
8529 SelectObject(hdc
, hbm_orig
);
8530 ImageList_Add(himl
, hbm_im
, hbm_mask
);
8532 SelectObject(hdc
, hbm_im
);
8533 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
8534 SelectObject(hdc
, hbm_orig
);
8535 ImageList_Add(himl
, hbm_im
, hbm_mask
);
8537 DeleteObject(hbm_mask
);
8538 DeleteObject(hbm_im
);
8546 * Sets the extended listview style.
8549 * [I] infoPtr : valid pointer to the listview structure
8551 * [I] dwStyle : style
8554 * SUCCESS : previous style
8557 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD mask
, DWORD ex_style
)
8559 DWORD old_ex_style
= infoPtr
->dwLvExStyle
;
8561 TRACE("mask=0x%08x, ex_style=0x%08x\n", mask
, ex_style
);
8565 infoPtr
->dwLvExStyle
= (old_ex_style
& ~mask
) | (ex_style
& mask
);
8567 infoPtr
->dwLvExStyle
= ex_style
;
8569 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_CHECKBOXES
)
8571 HIMAGELIST himl
= 0;
8572 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
8575 item
.mask
= LVIF_STATE
;
8576 item
.stateMask
= LVIS_STATEIMAGEMASK
;
8577 item
.state
= INDEXTOSTATEIMAGEMASK(1);
8578 LISTVIEW_SetItemState(infoPtr
, -1, &item
);
8580 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
8581 if(!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
8582 ImageList_Destroy(infoPtr
->himlState
);
8584 himl
= LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
8585 /* checkbox list replaces previous custom list or... */
8586 if(((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) &&
8587 !(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
)) ||
8588 /* ...previous was checkbox list */
8589 (old_ex_style
& LVS_EX_CHECKBOXES
))
8590 ImageList_Destroy(himl
);
8593 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_HEADERDRAGDROP
)
8597 /* if not already created */
8598 LISTVIEW_CreateHeader(infoPtr
);
8600 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
8601 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
)
8602 style
|= HDS_DRAGDROP
;
8604 style
&= ~HDS_DRAGDROP
;
8605 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
);
8608 /* GRIDLINES adds decoration at top so changes sizes */
8609 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_GRIDLINES
)
8611 LISTVIEW_CreateHeader(infoPtr
);
8612 LISTVIEW_UpdateSize(infoPtr
);
8615 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_FULLROWSELECT
)
8617 LISTVIEW_CreateHeader(infoPtr
);
8620 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_TRANSPARENTBKGND
)
8622 if (infoPtr
->dwLvExStyle
& LVS_EX_TRANSPARENTBKGND
)
8623 LISTVIEW_SetBkColor(infoPtr
, CLR_NONE
);
8626 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_HEADERINALLVIEWS
)
8628 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERINALLVIEWS
)
8629 LISTVIEW_CreateHeader(infoPtr
);
8631 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
8632 LISTVIEW_UpdateSize(infoPtr
);
8633 LISTVIEW_UpdateScroll(infoPtr
);
8636 LISTVIEW_InvalidateList(infoPtr
);
8637 return old_ex_style
;
8642 * Sets the new hot cursor used during hot tracking and hover selection.
8645 * [I] infoPtr : valid pointer to the listview structure
8646 * [I] hCursor : the new hot cursor handle
8649 * Returns the previous hot cursor
8651 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
8653 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
8655 infoPtr
->hHotCursor
= hCursor
;
8663 * Sets the hot item index.
8666 * [I] infoPtr : valid pointer to the listview structure
8667 * [I] iIndex : index
8670 * SUCCESS : previous hot item index
8671 * FAILURE : -1 (no hot item)
8673 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
8675 INT iOldIndex
= infoPtr
->nHotItem
;
8677 infoPtr
->nHotItem
= iIndex
;
8685 * Sets the amount of time the cursor must hover over an item before it is selected.
8688 * [I] infoPtr : valid pointer to the listview structure
8689 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
8692 * Returns the previous hover time
8694 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
8696 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
8698 infoPtr
->dwHoverTime
= dwHoverTime
;
8700 return oldHoverTime
;
8705 * Sets spacing for icons of LVS_ICON style.
8708 * [I] infoPtr : valid pointer to the listview structure
8709 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
8710 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
8713 * MAKELONG(oldcx, oldcy)
8715 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
8717 INT iconWidth
= 0, iconHeight
= 0;
8718 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
8720 TRACE("requested=(%d,%d)\n", cx
, cy
);
8722 /* set to defaults, if instructed to */
8723 if (cx
== -1 && cy
== -1)
8725 infoPtr
->autoSpacing
= TRUE
;
8726 if (infoPtr
->himlNormal
)
8727 ImageList_GetIconSize(infoPtr
->himlNormal
, &iconWidth
, &iconHeight
);
8728 cx
= GetSystemMetrics(SM_CXICONSPACING
) - GetSystemMetrics(SM_CXICON
) + iconWidth
;
8729 cy
= GetSystemMetrics(SM_CYICONSPACING
) - GetSystemMetrics(SM_CYICON
) + iconHeight
;
8732 infoPtr
->autoSpacing
= FALSE
;
8734 /* if 0 then keep width */
8736 infoPtr
->iconSpacing
.cx
= cx
;
8738 /* if 0 then keep height */
8740 infoPtr
->iconSpacing
.cy
= cy
;
8742 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
8743 LOWORD(oldspacing
), HIWORD(oldspacing
), infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
,
8744 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
8745 infoPtr
->ntmHeight
);
8747 /* these depend on the iconSpacing */
8748 LISTVIEW_UpdateItemSize(infoPtr
);
8753 static inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL small
)
8757 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
8764 size
->cx
= GetSystemMetrics(small
? SM_CXSMICON
: SM_CXICON
);
8765 size
->cy
= GetSystemMetrics(small
? SM_CYSMICON
: SM_CYICON
);
8774 * [I] infoPtr : valid pointer to the listview structure
8775 * [I] nType : image list type
8776 * [I] himl : image list handle
8779 * SUCCESS : old image list
8782 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
8784 INT oldHeight
= infoPtr
->nItemHeight
;
8785 HIMAGELIST himlOld
= 0;
8787 TRACE("(nType=%d, himl=%p)\n", nType
, himl
);
8792 himlOld
= infoPtr
->himlNormal
;
8793 infoPtr
->himlNormal
= himl
;
8794 if (infoPtr
->uView
== LV_VIEW_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
8795 if (infoPtr
->autoSpacing
)
8796 LISTVIEW_SetIconSpacing(infoPtr
, -1, -1);
8800 himlOld
= infoPtr
->himlSmall
;
8801 infoPtr
->himlSmall
= himl
;
8802 if (infoPtr
->uView
!= LV_VIEW_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
8803 if (infoPtr
->hwndHeader
)
8804 SendMessageW(infoPtr
->hwndHeader
, HDM_SETIMAGELIST
, 0, (LPARAM
)himl
);
8808 himlOld
= infoPtr
->himlState
;
8809 infoPtr
->himlState
= himl
;
8810 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
8811 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
8815 ERR("Unknown icon type=%d\n", nType
);
8819 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
8820 if (infoPtr
->nItemHeight
!= oldHeight
)
8821 LISTVIEW_UpdateScroll(infoPtr
);
8828 * Preallocates memory (does *not* set the actual count of items !)
8831 * [I] infoPtr : valid pointer to the listview structure
8832 * [I] nItems : item count (projected number of items to allocate)
8833 * [I] dwFlags : update flags
8839 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
8841 TRACE("(nItems=%d, dwFlags=%x)\n", nItems
, dwFlags
);
8843 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
8845 INT nOldCount
= infoPtr
->nItemCount
;
8846 infoPtr
->nItemCount
= nItems
;
8848 if (nItems
< nOldCount
)
8850 RANGE range
= { nItems
, nOldCount
};
8851 ranges_del(infoPtr
->selectionRanges
, range
);
8852 if (infoPtr
->nFocusedItem
>= nItems
)
8854 LISTVIEW_SetItemFocus(infoPtr
, -1);
8855 infoPtr
->nFocusedItem
= -1;
8856 SetRectEmpty(&infoPtr
->rcFocus
);
8860 LISTVIEW_UpdateScroll(infoPtr
);
8862 /* the flags are valid only in ownerdata report and list modes */
8863 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
) dwFlags
= 0;
8865 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
8866 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
8868 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
8869 LISTVIEW_InvalidateList(infoPtr
);
8876 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
8877 nFrom
= min(nOldCount
, nItems
);
8878 nTo
= max(nOldCount
, nItems
);
8880 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
8882 SetRect(&rcErase
, 0, nFrom
* infoPtr
->nItemHeight
, infoPtr
->nItemWidth
,
8883 nTo
* infoPtr
->nItemHeight
);
8884 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
8885 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
8886 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
8888 else /* LV_VIEW_LIST */
8890 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
8892 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
8893 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
8894 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
8895 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
8896 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
8897 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
8898 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
8900 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
8902 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
8903 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
8904 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
8905 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
8906 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
8912 /* According to MSDN for non-LVS_OWNERDATA this is just
8913 * a performance issue. The control allocates its internal
8914 * data structures for the number of items specified. It
8915 * cuts down on the number of memory allocations. Therefore
8916 * we will just issue a WARN here
8918 WARN("for non-ownerdata performance option not implemented.\n");
8926 * Sets the position of an item.
8929 * [I] infoPtr : valid pointer to the listview structure
8930 * [I] nItem : item index
8931 * [I] pt : coordinate
8937 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*pt
)
8941 TRACE("(nItem=%d, pt=%s)\n", nItem
, wine_dbgstr_point(pt
));
8943 if (!pt
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
8944 !(infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)) return FALSE
;
8947 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
8949 /* This point value seems to be an undocumented feature.
8950 * The best guess is that it means either at the origin,
8951 * or at true beginning of the list. I will assume the origin. */
8952 if ((Pt
.x
== -1) && (Pt
.y
== -1))
8955 if (infoPtr
->uView
== LV_VIEW_ICON
)
8957 Pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
8958 Pt
.y
-= ICON_TOP_PADDING
;
8963 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &Pt
, FALSE
);
8968 * Sets the state of one or many items.
8971 * [I] infoPtr : valid pointer to the listview structure
8972 * [I] nItem : item index
8973 * [I] item : item or subitem info
8979 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*item
)
8984 if (!item
) return FALSE
;
8986 lvItem
.iItem
= nItem
;
8987 lvItem
.iSubItem
= 0;
8988 lvItem
.mask
= LVIF_STATE
;
8989 lvItem
.state
= item
->state
;
8990 lvItem
.stateMask
= item
->stateMask
;
8991 TRACE("item=%s\n", debuglvitem_t(&lvItem
, TRUE
));
8998 /* special case optimization for recurring attempt to deselect all */
8999 if (lvItem
.state
== 0 && lvItem
.stateMask
== LVIS_SELECTED
&& !LISTVIEW_GetSelectedCount(infoPtr
))
9002 /* select all isn't allowed in LVS_SINGLESEL */
9003 if ((lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) && (infoPtr
->dwStyle
& LVS_SINGLESEL
))
9006 /* focus all isn't allowed */
9007 if (lvItem
.state
& lvItem
.stateMask
& LVIS_FOCUSED
) return FALSE
;
9009 old_mask
= infoPtr
->notify_mask
& NOTIFY_MASK_ITEM_CHANGE
;
9010 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
9012 infoPtr
->notify_mask
&= ~NOTIFY_MASK_ITEM_CHANGE
;
9013 if (!(lvItem
.state
& LVIS_SELECTED
) && LISTVIEW_GetSelectedCount(infoPtr
))
9014 oldstate
|= LVIS_SELECTED
;
9015 if (infoPtr
->nFocusedItem
!= -1) oldstate
|= LVIS_FOCUSED
;
9018 /* apply to all items */
9019 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
9020 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) ret
= FALSE
;
9022 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
9026 infoPtr
->notify_mask
|= old_mask
;
9030 nmlv
.uNewState
= lvItem
.state
& lvItem
.stateMask
;
9031 nmlv
.uOldState
= oldstate
& lvItem
.stateMask
;
9032 nmlv
.uChanged
= LVIF_STATE
;
9033 nmlv
.ptAction
.x
= nmlv
.ptAction
.y
= 0;
9036 notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
9040 ret
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
9047 * Sets the text of an item or subitem.
9050 * [I] hwnd : window handle
9051 * [I] nItem : item index
9052 * [I] lpLVItem : item or subitem info
9053 * [I] isW : TRUE if input is Unicode
9059 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
9063 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
9064 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
9066 lvItem
.iItem
= nItem
;
9067 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
9068 lvItem
.mask
= LVIF_TEXT
;
9069 lvItem
.pszText
= lpLVItem
->pszText
;
9070 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
9072 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
9074 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
9079 * Set item index that marks the start of a multiple selection.
9082 * [I] infoPtr : valid pointer to the listview structure
9083 * [I] nIndex : index
9086 * Index number or -1 if there is no selection mark.
9088 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
9090 INT nOldIndex
= infoPtr
->nSelectionMark
;
9092 TRACE("(nIndex=%d)\n", nIndex
);
9094 if (nIndex
>= -1 && nIndex
< infoPtr
->nItemCount
)
9095 infoPtr
->nSelectionMark
= nIndex
;
9102 * Sets the text background color.
9105 * [I] infoPtr : valid pointer to the listview structure
9106 * [I] color : text background color
9112 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF color
)
9114 TRACE("(color=%x)\n", color
);
9116 infoPtr
->clrTextBk
= color
;
9122 * Sets the text foreground color.
9125 * [I] infoPtr : valid pointer to the listview structure
9126 * [I] color : text color
9132 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF color
)
9134 TRACE("(color=%x)\n", color
);
9136 infoPtr
->clrText
= color
;
9142 * Sets new ToolTip window to ListView control.
9145 * [I] infoPtr : valid pointer to the listview structure
9146 * [I] hwndNewToolTip : handle to new ToolTip
9151 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
9153 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
9154 infoPtr
->hwndToolTip
= hwndNewToolTip
;
9155 return hwndOldToolTip
;
9160 * sets the Unicode character format flag for the control
9162 * [I] infoPtr :valid pointer to the listview structure
9163 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
9166 * Old Unicode Format
9168 static BOOL
LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO
*infoPtr
, BOOL unicode
)
9170 SHORT rc
= infoPtr
->notifyFormat
;
9171 infoPtr
->notifyFormat
= (unicode
) ? NFR_UNICODE
: NFR_ANSI
;
9172 return rc
== NFR_UNICODE
;
9177 * sets the control view mode
9179 * [I] infoPtr :valid pointer to the listview structure
9180 * [I] nView :new view mode value
9186 static INT
LISTVIEW_SetView(LISTVIEW_INFO
*infoPtr
, DWORD nView
)
9190 if (infoPtr
->uView
== nView
) return 1;
9192 if ((INT
)nView
< 0 || nView
> LV_VIEW_MAX
) return -1;
9193 if (nView
== LV_VIEW_TILE
)
9195 FIXME("View LV_VIEW_TILE unimplemented\n");
9199 infoPtr
->uView
= nView
;
9201 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9202 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
9204 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
9205 SetRectEmpty(&infoPtr
->rcFocus
);
9207 himl
= (nView
== LV_VIEW_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
9208 set_icon_size(&infoPtr
->iconSize
, himl
, nView
!= LV_VIEW_ICON
);
9213 case LV_VIEW_SMALLICON
:
9214 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9216 case LV_VIEW_DETAILS
:
9221 LISTVIEW_CreateHeader( infoPtr
);
9223 hl
.prc
= &infoPtr
->rcList
;
9225 SendMessageW(infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9226 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9227 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9234 LISTVIEW_UpdateItemSize(infoPtr
);
9235 LISTVIEW_UpdateSize(infoPtr
);
9236 LISTVIEW_UpdateScroll(infoPtr
);
9237 LISTVIEW_InvalidateList(infoPtr
);
9239 TRACE("nView=%d\n", nView
);
9244 /* LISTVIEW_SetWorkAreas */
9248 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
9251 * [I] first : pointer to first ITEM_INFO to compare
9252 * [I] second : pointer to second ITEM_INFO to compare
9253 * [I] lParam : HWND of control
9256 * if first comes before second : negative
9257 * if first comes after second : positive
9258 * if first and second are equivalent : zero
9260 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
9262 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
9263 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
9264 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
9266 /* Forward the call to the client defined callback */
9267 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
9272 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
9275 * [I] first : pointer to first ITEM_INFO to compare
9276 * [I] second : pointer to second ITEM_INFO to compare
9277 * [I] lParam : HWND of control
9280 * if first comes before second : negative
9281 * if first comes after second : positive
9282 * if first and second are equivalent : zero
9284 static INT WINAPI
LISTVIEW_CallBackCompareEx(LPVOID first
, LPVOID second
, LPARAM lParam
)
9286 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
9287 INT first_idx
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, first
);
9288 INT second_idx
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, second
);
9290 /* Forward the call to the client defined callback */
9291 return (infoPtr
->pfnCompare
)( first_idx
, second_idx
, infoPtr
->lParamSort
);
9296 * Sorts the listview items.
9299 * [I] infoPtr : valid pointer to the listview structure
9300 * [I] pfnCompare : application-defined value
9301 * [I] lParamSort : pointer to comparison callback
9302 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
9308 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
,
9309 LPARAM lParamSort
, BOOL IsEx
)
9313 LPVOID selectionMarkItem
= NULL
;
9314 LPVOID focusedItem
= NULL
;
9317 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
9319 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
9321 if (!pfnCompare
) return FALSE
;
9322 if (!infoPtr
->hdpaItems
) return FALSE
;
9324 /* if there are 0 or 1 items, there is no need to sort */
9325 if (infoPtr
->nItemCount
< 2) return TRUE
;
9327 /* clear selection */
9328 ranges_clear(infoPtr
->selectionRanges
);
9330 /* save selection mark and focused item */
9331 if (infoPtr
->nSelectionMark
>= 0)
9332 selectionMarkItem
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
);
9333 if (infoPtr
->nFocusedItem
>= 0)
9334 focusedItem
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
9336 infoPtr
->pfnCompare
= pfnCompare
;
9337 infoPtr
->lParamSort
= lParamSort
;
9339 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompareEx
, (LPARAM
)infoPtr
);
9341 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
9343 /* restore selection ranges */
9344 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
9346 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
9347 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
9349 if (lpItem
->state
& LVIS_SELECTED
)
9350 ranges_additem(infoPtr
->selectionRanges
, i
);
9352 /* restore selection mark and focused item */
9353 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
9354 infoPtr
->nFocusedItem
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, focusedItem
);
9356 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
9358 /* refresh the display */
9359 LISTVIEW_InvalidateList(infoPtr
);
9365 * Update theme handle after a theme change.
9368 * [I] infoPtr : valid pointer to the listview structure
9372 * FAILURE : something else
9374 static LRESULT
LISTVIEW_ThemeChanged(const LISTVIEW_INFO
*infoPtr
)
9376 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
9377 CloseThemeData(theme
);
9378 OpenThemeData(infoPtr
->hwndSelf
, themeClass
);
9384 * Updates an items or rearranges the listview control.
9387 * [I] infoPtr : valid pointer to the listview structure
9388 * [I] nItem : item index
9394 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
9396 TRACE("(nItem=%d)\n", nItem
);
9398 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
9400 /* rearrange with default alignment style */
9401 if (is_autoarrange(infoPtr
))
9402 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9404 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
9411 * Draw the track line at the place defined in the infoPtr structure.
9412 * The line is drawn with a XOR pen so drawing the line for the second time
9413 * in the same place erases the line.
9416 * [I] infoPtr : valid pointer to the listview structure
9422 static BOOL
LISTVIEW_DrawTrackLine(const LISTVIEW_INFO
*infoPtr
)
9426 if (infoPtr
->xTrackLine
== -1)
9429 if (!(hdc
= GetDC(infoPtr
->hwndSelf
)))
9431 PatBlt( hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.top
,
9432 1, infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
, DSTINVERT
);
9433 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
9439 * Called when an edit control should be displayed. This function is called after
9440 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
9443 * [I] hwnd : Handle to the listview
9444 * [I] uMsg : WM_TIMER (ignored)
9445 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
9446 * [I] dwTimer : The elapsed time (ignored)
9451 static VOID CALLBACK
LISTVIEW_DelayedEditItem(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
9453 DELAYED_ITEM_EDIT
*editItem
= (DELAYED_ITEM_EDIT
*)idEvent
;
9454 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9456 KillTimer(hwnd
, idEvent
);
9457 editItem
->fEnabled
= FALSE
;
9458 /* check if the item is still selected */
9459 if (infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, editItem
->iItem
, LVIS_SELECTED
))
9460 LISTVIEW_EditLabelT(infoPtr
, editItem
->iItem
, TRUE
);
9465 * Creates the listview control - the WM_NCCREATE phase.
9468 * [I] hwnd : window handle
9469 * [I] lpcs : the create parameters
9475 static LRESULT
LISTVIEW_NCCreate(HWND hwnd
, WPARAM wParam
, const CREATESTRUCTW
*lpcs
)
9477 LISTVIEW_INFO
*infoPtr
;
9480 TRACE("(lpcs=%p)\n", lpcs
);
9482 /* initialize info pointer */
9483 infoPtr
= Alloc(sizeof(LISTVIEW_INFO
));
9484 if (!infoPtr
) return FALSE
;
9486 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
9488 infoPtr
->hwndSelf
= hwnd
;
9489 infoPtr
->dwStyle
= lpcs
->style
; /* Note: may be changed in WM_CREATE */
9490 map_style_view(infoPtr
);
9491 /* determine the type of structures to use */
9492 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
9493 /* infoPtr->notifyFormat will be filled in WM_CREATE */
9495 /* initialize color information */
9496 infoPtr
->clrBk
= CLR_NONE
;
9497 infoPtr
->clrText
= CLR_DEFAULT
;
9498 infoPtr
->clrTextBk
= CLR_DEFAULT
;
9499 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
9501 /* set default values */
9502 infoPtr
->nFocusedItem
= -1;
9503 infoPtr
->nSelectionMark
= -1;
9504 infoPtr
->nHotItem
= -1;
9505 infoPtr
->redraw
= TRUE
;
9506 infoPtr
->bNoItemMetrics
= TRUE
;
9507 infoPtr
->notify_mask
= NOTIFY_MASK_UNMASK_ALL
;
9508 infoPtr
->autoSpacing
= TRUE
;
9509 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
) - GetSystemMetrics(SM_CXICON
);
9510 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
) - GetSystemMetrics(SM_CYICON
);
9511 infoPtr
->nEditLabelItem
= -1;
9512 infoPtr
->nLButtonDownItem
= -1;
9513 infoPtr
->dwHoverTime
= HOVER_DEFAULT
; /* default system hover time */
9514 infoPtr
->cWheelRemainder
= 0;
9515 infoPtr
->nMeasureItemHeight
= 0;
9516 infoPtr
->xTrackLine
= -1; /* no track line */
9517 infoPtr
->itemEdit
.fEnabled
= FALSE
;
9518 infoPtr
->iVersion
= COMCTL32_VERSION
;
9519 infoPtr
->colRectsDirty
= FALSE
;
9521 /* get default font (icon title) */
9522 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
9523 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
9524 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
9525 LISTVIEW_SaveTextMetrics(infoPtr
);
9527 /* allocate memory for the data structure */
9528 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
9529 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
9530 if (!(infoPtr
->hdpaItemIds
= DPA_Create(10))) goto fail
;
9531 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
9532 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
9533 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
9535 return DefWindowProcW(hwnd
, WM_NCCREATE
, wParam
, (LPARAM
)lpcs
);
9538 DestroyWindow(infoPtr
->hwndHeader
);
9539 ranges_destroy(infoPtr
->selectionRanges
);
9540 DPA_Destroy(infoPtr
->hdpaItems
);
9541 DPA_Destroy(infoPtr
->hdpaItemIds
);
9542 DPA_Destroy(infoPtr
->hdpaPosX
);
9543 DPA_Destroy(infoPtr
->hdpaPosY
);
9544 DPA_Destroy(infoPtr
->hdpaColumns
);
9551 * Creates the listview control - the WM_CREATE phase. Most of the data is
9552 * already set up in LISTVIEW_NCCreate
9555 * [I] hwnd : window handle
9556 * [I] lpcs : the create parameters
9562 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
9564 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9566 TRACE("(lpcs=%p, style=0x%08x)\n", lpcs
, lpcs
->style
);
9568 infoPtr
->dwStyle
= lpcs
->style
;
9569 map_style_view(infoPtr
);
9571 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
9572 (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
9573 /* on error defaulting to ANSI notifications */
9574 if (infoPtr
->notifyFormat
== 0) infoPtr
->notifyFormat
= NFR_ANSI
;
9575 TRACE("notify format=%d\n", infoPtr
->notifyFormat
);
9577 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && (lpcs
->style
& WS_VISIBLE
))
9579 if (LISTVIEW_CreateHeader(infoPtr
) < 0) return -1;
9582 infoPtr
->hwndHeader
= 0;
9584 /* init item size to avoid division by 0 */
9585 LISTVIEW_UpdateItemSize (infoPtr
);
9586 LISTVIEW_UpdateSize (infoPtr
);
9588 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
9590 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
) && (WS_VISIBLE
& lpcs
->style
))
9592 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
9594 LISTVIEW_UpdateScroll(infoPtr
);
9595 /* send WM_MEASUREITEM notification */
9596 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) notify_measureitem(infoPtr
);
9599 OpenThemeData(hwnd
, themeClass
);
9601 /* initialize the icon sizes */
9602 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, infoPtr
->uView
!= LV_VIEW_ICON
);
9603 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
9609 * Destroys the listview control.
9612 * [I] infoPtr : valid pointer to the listview structure
9618 static LRESULT
LISTVIEW_Destroy(LISTVIEW_INFO
*infoPtr
)
9620 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
9621 CloseThemeData(theme
);
9623 /* delete all items */
9624 LISTVIEW_DeleteAllItems(infoPtr
, TRUE
);
9631 * Enables the listview control.
9634 * [I] infoPtr : valid pointer to the listview structure
9635 * [I] bEnable : specifies whether to enable or disable the window
9641 static BOOL
LISTVIEW_Enable(const LISTVIEW_INFO
*infoPtr
)
9643 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
9644 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
9650 * Erases the background of the listview control.
9653 * [I] infoPtr : valid pointer to the listview structure
9654 * [I] hdc : device context handle
9660 static inline BOOL
LISTVIEW_EraseBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
9664 TRACE("(hdc=%p)\n", hdc
);
9666 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
9668 if (infoPtr
->clrBk
== CLR_NONE
)
9670 if (infoPtr
->dwLvExStyle
& LVS_EX_TRANSPARENTBKGND
)
9671 return SendMessageW(infoPtr
->hwndNotify
, WM_PRINTCLIENT
,
9672 (WPARAM
)hdc
, PRF_ERASEBKGND
);
9674 return SendMessageW(infoPtr
->hwndNotify
, WM_ERASEBKGND
, (WPARAM
)hdc
, 0);
9677 /* for double buffered controls we need to do this during refresh */
9678 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) return FALSE
;
9680 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
9686 * Helper function for LISTVIEW_[HV]Scroll *only*.
9687 * Performs vertical/horizontal scrolling by a give amount.
9690 * [I] infoPtr : valid pointer to the listview structure
9691 * [I] dx : amount of horizontal scroll
9692 * [I] dy : amount of vertical scroll
9694 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
9696 /* now we can scroll the list */
9697 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
9698 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
9699 /* if we have focus, adjust rect */
9700 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
9701 UpdateWindow(infoPtr
->hwndSelf
);
9706 * Performs vertical scrolling.
9709 * [I] infoPtr : valid pointer to the listview structure
9710 * [I] nScrollCode : scroll code
9711 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9712 * [I] hScrollWnd : scrollbar control window handle
9718 * SB_LINEUP/SB_LINEDOWN:
9719 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
9720 * for LVS_REPORT is 1 line
9721 * for LVS_LIST cannot occur
9724 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
9727 INT nOldScrollPos
, nNewScrollPos
;
9728 SCROLLINFO scrollInfo
;
9731 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
9732 debugscrollcode(nScrollCode
), nScrollDiff
);
9734 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9736 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
9737 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
9739 is_an_icon
= ((infoPtr
->uView
== LV_VIEW_ICON
) || (infoPtr
->uView
== LV_VIEW_SMALLICON
));
9741 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
9743 nOldScrollPos
= scrollInfo
.nPos
;
9744 switch (nScrollCode
)
9750 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
9754 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
9758 nScrollDiff
= -scrollInfo
.nPage
;
9762 nScrollDiff
= scrollInfo
.nPage
;
9765 case SB_THUMBPOSITION
:
9767 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
9774 /* quit right away if pos isn't changing */
9775 if (nScrollDiff
== 0) return 0;
9777 /* calculate new position, and handle overflows */
9778 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
9779 if (nScrollDiff
> 0) {
9780 if (nNewScrollPos
< nOldScrollPos
||
9781 nNewScrollPos
> scrollInfo
.nMax
)
9782 nNewScrollPos
= scrollInfo
.nMax
;
9784 if (nNewScrollPos
> nOldScrollPos
||
9785 nNewScrollPos
< scrollInfo
.nMin
)
9786 nNewScrollPos
= scrollInfo
.nMin
;
9789 /* set the new position, and reread in case it changed */
9790 scrollInfo
.fMask
= SIF_POS
;
9791 scrollInfo
.nPos
= nNewScrollPos
;
9792 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
9794 /* carry on only if it really changed */
9795 if (nNewScrollPos
== nOldScrollPos
) return 0;
9797 /* now adjust to client coordinates */
9798 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
9799 if (infoPtr
->uView
== LV_VIEW_DETAILS
) nScrollDiff
*= infoPtr
->nItemHeight
;
9801 /* and scroll the window */
9802 scroll_list(infoPtr
, 0, nScrollDiff
);
9809 * Performs horizontal scrolling.
9812 * [I] infoPtr : valid pointer to the listview structure
9813 * [I] nScrollCode : scroll code
9814 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9815 * [I] hScrollWnd : scrollbar control window handle
9821 * SB_LINELEFT/SB_LINERIGHT:
9822 * for LVS_ICON, LVS_SMALLICON 1 pixel
9823 * for LVS_REPORT is 1 pixel
9824 * for LVS_LIST is 1 column --> which is a 1 because the
9825 * scroll is based on columns not pixels
9828 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
9831 INT nOldScrollPos
, nNewScrollPos
;
9832 SCROLLINFO scrollInfo
;
9835 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
9836 debugscrollcode(nScrollCode
), nScrollDiff
);
9838 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9840 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
9841 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
9843 is_an_icon
= ((infoPtr
->uView
== LV_VIEW_ICON
) || (infoPtr
->uView
== LV_VIEW_SMALLICON
));
9845 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
9847 nOldScrollPos
= scrollInfo
.nPos
;
9849 switch (nScrollCode
)
9855 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
9859 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
9863 nScrollDiff
= -scrollInfo
.nPage
;
9867 nScrollDiff
= scrollInfo
.nPage
;
9870 case SB_THUMBPOSITION
:
9872 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
9879 /* quit right away if pos isn't changing */
9880 if (nScrollDiff
== 0) return 0;
9882 /* calculate new position, and handle overflows */
9883 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
9884 if (nScrollDiff
> 0) {
9885 if (nNewScrollPos
< nOldScrollPos
||
9886 nNewScrollPos
> scrollInfo
.nMax
)
9887 nNewScrollPos
= scrollInfo
.nMax
;
9889 if (nNewScrollPos
> nOldScrollPos
||
9890 nNewScrollPos
< scrollInfo
.nMin
)
9891 nNewScrollPos
= scrollInfo
.nMin
;
9894 /* set the new position, and reread in case it changed */
9895 scrollInfo
.fMask
= SIF_POS
;
9896 scrollInfo
.nPos
= nNewScrollPos
;
9897 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
9899 /* carry on only if it really changed */
9900 if (nNewScrollPos
== nOldScrollPos
) return 0;
9902 LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
9904 /* now adjust to client coordinates */
9905 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
9906 if (infoPtr
->uView
== LV_VIEW_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
9908 /* and scroll the window */
9909 scroll_list(infoPtr
, nScrollDiff
, 0);
9914 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
9916 UINT pulScrollLines
= 3;
9918 TRACE("(wheelDelta=%d)\n", wheelDelta
);
9920 switch(infoPtr
->uView
)
9923 case LV_VIEW_SMALLICON
:
9925 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
9926 * should be fixed in the future.
9928 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (wheelDelta
> 0) ?
9929 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
);
9932 case LV_VIEW_DETAILS
:
9933 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
9935 /* if scrolling changes direction, ignore left overs */
9936 if ((wheelDelta
< 0 && infoPtr
->cWheelRemainder
< 0) ||
9937 (wheelDelta
> 0 && infoPtr
->cWheelRemainder
> 0))
9938 infoPtr
->cWheelRemainder
+= wheelDelta
;
9940 infoPtr
->cWheelRemainder
= wheelDelta
;
9941 if (infoPtr
->cWheelRemainder
&& pulScrollLines
)
9944 pulScrollLines
= min((UINT
)LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
9945 cLineScroll
= pulScrollLines
* (float)infoPtr
->cWheelRemainder
/ WHEEL_DELTA
;
9946 infoPtr
->cWheelRemainder
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
9947 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, -cLineScroll
);
9952 LISTVIEW_HScroll(infoPtr
, (wheelDelta
> 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0);
9963 * [I] infoPtr : valid pointer to the listview structure
9964 * [I] nVirtualKey : virtual key
9965 * [I] lKeyData : key data
9970 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
9972 HWND hwndSelf
= infoPtr
->hwndSelf
;
9974 NMLVKEYDOWN nmKeyDown
;
9976 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey
, lKeyData
);
9978 /* send LVN_KEYDOWN notification */
9979 nmKeyDown
.wVKey
= nVirtualKey
;
9980 nmKeyDown
.flags
= 0;
9981 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
9982 if (!IsWindow(hwndSelf
))
9985 switch (nVirtualKey
)
9988 nItem
= infoPtr
->nFocusedItem
;
9989 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
9990 toggle_checkbox_state(infoPtr
, infoPtr
->nFocusedItem
);
9994 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
9996 if (!notify(infoPtr
, NM_RETURN
)) return 0;
9997 if (!notify(infoPtr
, LVN_ITEMACTIVATE
)) return 0;
10002 if (infoPtr
->nItemCount
> 0)
10007 if (infoPtr
->nItemCount
> 0)
10008 nItem
= infoPtr
->nItemCount
- 1;
10012 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
10016 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
10020 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
10024 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
10028 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
10030 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
10031 if (infoPtr
->nFocusedItem
== topidx
)
10032 nItem
= topidx
- LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
10037 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
10038 * LISTVIEW_GetCountPerRow(infoPtr
);
10039 if(nItem
< 0) nItem
= 0;
10043 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
10045 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
10046 INT cnt
= LISTVIEW_GetCountPerColumn(infoPtr
);
10047 if (infoPtr
->nFocusedItem
== topidx
+ cnt
- 1)
10048 nItem
= infoPtr
->nFocusedItem
+ cnt
- 1;
10050 nItem
= topidx
+ cnt
- 1;
10053 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
10054 * LISTVIEW_GetCountPerRow(infoPtr
);
10055 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
10059 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
|| nVirtualKey
== VK_SPACE
))
10060 LISTVIEW_KeySelection(infoPtr
, nItem
, nVirtualKey
== VK_SPACE
);
10070 * [I] infoPtr : valid pointer to the listview structure
10075 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
10079 /* drop any left over scroll amount */
10080 infoPtr
->cWheelRemainder
= 0;
10082 /* if we did not have the focus, there's nothing more to do */
10083 if (!infoPtr
->bFocus
) return 0;
10085 /* send NM_KILLFOCUS notification */
10086 if (!notify(infoPtr
, NM_KILLFOCUS
)) return 0;
10088 /* if we have a focus rectangle, get rid of it */
10089 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
10091 /* if have a marquee selection, stop it */
10092 if (infoPtr
->bMarqueeSelect
)
10094 /* Remove the marquee rectangle and release our mouse capture */
10095 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeRect
);
10098 SetRectEmpty(&infoPtr
->marqueeRect
);
10100 infoPtr
->bMarqueeSelect
= FALSE
;
10101 infoPtr
->bScrolling
= FALSE
;
10102 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
);
10105 /* set window focus flag */
10106 infoPtr
->bFocus
= FALSE
;
10108 /* invalidate the selected items before resetting focus flag */
10109 LISTVIEW_InvalidateSelectedItems(infoPtr
);
10116 * Processes double click messages (left mouse button).
10119 * [I] infoPtr : valid pointer to the listview structure
10120 * [I] wKey : key flag
10121 * [I] x,y : mouse coordinate
10126 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10128 LVHITTESTINFO htInfo
;
10130 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey
, x
, y
);
10132 /* Cancel the item edition if any */
10133 if (infoPtr
->itemEdit
.fEnabled
)
10135 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
)&infoPtr
->itemEdit
);
10136 infoPtr
->itemEdit
.fEnabled
= FALSE
;
10139 /* send NM_RELEASEDCAPTURE notification */
10140 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10145 /* send NM_DBLCLK notification */
10146 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
10147 if (!notify_click(infoPtr
, NM_DBLCLK
, &htInfo
)) return 0;
10149 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
10150 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
10155 static LRESULT
LISTVIEW_TrackMouse(const LISTVIEW_INFO
*infoPtr
, POINT pt
)
10160 r
.top
= r
.bottom
= pt
.y
;
10161 r
.left
= r
.right
= pt
.x
;
10163 InflateRect(&r
, GetSystemMetrics(SM_CXDRAG
), GetSystemMetrics(SM_CYDRAG
));
10165 SetCapture(infoPtr
->hwndSelf
);
10169 if (PeekMessageW(&msg
, 0, 0, 0, PM_REMOVE
| PM_NOYIELD
))
10171 if (msg
.message
== WM_MOUSEMOVE
)
10173 pt
.x
= (short)LOWORD(msg
.lParam
);
10174 pt
.y
= (short)HIWORD(msg
.lParam
);
10175 if (PtInRect(&r
, pt
))
10183 else if (msg
.message
>= WM_LBUTTONDOWN
&&
10184 msg
.message
<= WM_RBUTTONDBLCLK
)
10189 DispatchMessageW(&msg
);
10192 if (GetCapture() != infoPtr
->hwndSelf
)
10203 * Processes mouse down messages (left mouse button).
10206 * infoPtr [I ] valid pointer to the listview structure
10207 * wKey [I ] key flag
10208 * x,y [I ] mouse coordinate
10213 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10215 LVHITTESTINFO lvHitTestInfo
;
10216 static BOOL bGroupSelect
= TRUE
;
10217 POINT pt
= { x
, y
};
10220 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey
, x
, y
);
10222 /* send NM_RELEASEDCAPTURE notification */
10223 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10225 /* set left button down flag and record the click position */
10226 infoPtr
->bLButtonDown
= TRUE
;
10227 infoPtr
->ptClickPos
= pt
;
10228 infoPtr
->bDragging
= FALSE
;
10229 infoPtr
->bMarqueeSelect
= FALSE
;
10230 infoPtr
->bScrolling
= FALSE
;
10232 lvHitTestInfo
.pt
.x
= x
;
10233 lvHitTestInfo
.pt
.y
= y
;
10235 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
10236 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt
), nItem
);
10237 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
10239 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
10241 notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
);
10242 toggle_checkbox_state(infoPtr
, nItem
);
10243 infoPtr
->bLButtonDown
= FALSE
;
10247 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
10249 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
10250 infoPtr
->nEditLabelItem
= nItem
;
10252 LISTVIEW_SetSelection(infoPtr
, nItem
);
10256 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
10260 if (!LISTVIEW_AddGroupSelection(infoPtr
, nItem
)) return 0;
10261 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
10262 infoPtr
->nSelectionMark
= nItem
;
10268 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
10269 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
10271 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
10272 infoPtr
->nSelectionMark
= nItem
;
10275 else if (wKey
& MK_CONTROL
)
10279 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
10281 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
10282 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
10283 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
10284 infoPtr
->nSelectionMark
= nItem
;
10286 else if (wKey
& MK_SHIFT
)
10288 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
10292 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
10294 infoPtr
->nEditLabelItem
= nItem
;
10295 infoPtr
->nLButtonDownItem
= nItem
;
10297 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
10300 /* set selection (clears other pre-existing selections) */
10301 LISTVIEW_SetSelection(infoPtr
, nItem
);
10305 if (!infoPtr
->bFocus
)
10306 SetFocus(infoPtr
->hwndSelf
);
10308 if (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
)
10309 if(lvHitTestInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&lvHitTestInfo
);
10313 if (!infoPtr
->bFocus
)
10314 SetFocus(infoPtr
->hwndSelf
);
10316 /* remove all selections */
10317 if (!(wKey
& MK_CONTROL
) && !(wKey
& MK_SHIFT
))
10318 LISTVIEW_DeselectAll(infoPtr
);
10327 * Processes mouse up messages (left mouse button).
10330 * infoPtr [I ] valid pointer to the listview structure
10331 * wKey [I ] key flag
10332 * x,y [I ] mouse coordinate
10337 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10339 LVHITTESTINFO lvHitTestInfo
;
10341 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey
, x
, y
);
10343 if (!infoPtr
->bLButtonDown
) return 0;
10345 lvHitTestInfo
.pt
.x
= x
;
10346 lvHitTestInfo
.pt
.y
= y
;
10348 /* send NM_CLICK notification */
10349 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
10350 if (!notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
)) return 0;
10352 /* set left button flag */
10353 infoPtr
->bLButtonDown
= FALSE
;
10355 /* set a single selection, reset others */
10356 if(lvHitTestInfo
.iItem
== infoPtr
->nLButtonDownItem
&& lvHitTestInfo
.iItem
!= -1)
10357 LISTVIEW_SetSelection(infoPtr
, infoPtr
->nLButtonDownItem
);
10358 infoPtr
->nLButtonDownItem
= -1;
10360 if (infoPtr
->bDragging
|| infoPtr
->bMarqueeSelect
)
10362 /* Remove the marquee rectangle and release our mouse capture */
10363 if (infoPtr
->bMarqueeSelect
)
10365 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeDrawRect
);
10369 SetRectEmpty(&infoPtr
->marqueeRect
);
10370 SetRectEmpty(&infoPtr
->marqueeDrawRect
);
10372 infoPtr
->bDragging
= FALSE
;
10373 infoPtr
->bMarqueeSelect
= FALSE
;
10374 infoPtr
->bScrolling
= FALSE
;
10376 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
);
10380 /* if we clicked on a selected item, edit the label */
10381 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
10383 /* we want to make sure the user doesn't want to do a double click. So we will
10384 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
10386 infoPtr
->itemEdit
.fEnabled
= TRUE
;
10387 infoPtr
->itemEdit
.iItem
= lvHitTestInfo
.iItem
;
10388 SetTimer(infoPtr
->hwndSelf
,
10389 (UINT_PTR
)&infoPtr
->itemEdit
,
10390 GetDoubleClickTime(),
10391 LISTVIEW_DelayedEditItem
);
10399 * Destroys the listview control (called after WM_DESTROY).
10402 * [I] infoPtr : valid pointer to the listview structure
10407 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
10413 /* destroy data structure */
10414 DPA_Destroy(infoPtr
->hdpaItems
);
10415 DPA_Destroy(infoPtr
->hdpaItemIds
);
10416 DPA_Destroy(infoPtr
->hdpaPosX
);
10417 DPA_Destroy(infoPtr
->hdpaPosY
);
10419 for (i
= 0; i
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); i
++)
10420 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, i
));
10421 DPA_Destroy(infoPtr
->hdpaColumns
);
10422 ranges_destroy(infoPtr
->selectionRanges
);
10424 /* destroy image lists */
10425 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
10427 ImageList_Destroy(infoPtr
->himlNormal
);
10428 ImageList_Destroy(infoPtr
->himlSmall
);
10429 ImageList_Destroy(infoPtr
->himlState
);
10432 /* destroy font, bkgnd brush */
10433 infoPtr
->hFont
= 0;
10434 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
10435 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
10437 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
10439 /* free listview info pointer*/
10447 * Handles notifications.
10450 * [I] infoPtr : valid pointer to the listview structure
10451 * [I] lpnmhdr : notification information
10456 static LRESULT
LISTVIEW_Notify(LISTVIEW_INFO
*infoPtr
, NMHDR
*lpnmhdr
)
10460 TRACE("(lpnmhdr=%p)\n", lpnmhdr
);
10462 if (!lpnmhdr
|| lpnmhdr
->hwndFrom
!= infoPtr
->hwndHeader
) return 0;
10464 /* remember: HDN_LAST < HDN_FIRST */
10465 if (lpnmhdr
->code
> HDN_FIRST
|| lpnmhdr
->code
< HDN_LAST
) return 0;
10466 lpnmh
= (NMHEADERW
*)lpnmhdr
;
10468 if (lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
10470 switch (lpnmhdr
->code
)
10475 COLUMN_INFO
*lpColumnInfo
;
10479 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
10482 /* remove the old line (if any) */
10483 LISTVIEW_DrawTrackLine(infoPtr
);
10485 /* compute & draw the new line */
10486 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
10487 x
= lpColumnInfo
->rcHeader
.left
+ lpnmh
->pitem
->cxy
;
10488 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
10489 infoPtr
->xTrackLine
= x
+ ptOrigin
.x
;
10490 LISTVIEW_DrawTrackLine(infoPtr
);
10491 return notify_forward_header(infoPtr
, lpnmh
);
10494 case HDN_ENDTRACKA
:
10495 case HDN_ENDTRACKW
:
10496 /* remove the track line (if any) */
10497 LISTVIEW_DrawTrackLine(infoPtr
);
10498 infoPtr
->xTrackLine
= -1;
10499 return notify_forward_header(infoPtr
, lpnmh
);
10501 case HDN_BEGINDRAG
:
10502 if ((infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
) == 0) return 1;
10503 return notify_forward_header(infoPtr
, lpnmh
);
10506 infoPtr
->colRectsDirty
= TRUE
;
10507 LISTVIEW_InvalidateList(infoPtr
);
10508 return notify_forward_header(infoPtr
, lpnmh
);
10510 case HDN_ITEMCHANGEDW
:
10511 case HDN_ITEMCHANGEDA
:
10513 COLUMN_INFO
*lpColumnInfo
;
10517 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
10519 hdi
.mask
= HDI_WIDTH
;
10520 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, lpnmh
->iItem
, (LPARAM
)&hdi
)) return 0;
10524 cxy
= lpnmh
->pitem
->cxy
;
10526 /* determine how much we change since the last know position */
10527 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
10528 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
10531 lpColumnInfo
->rcHeader
.right
+= dx
;
10533 hdi
.mask
= HDI_ORDER
;
10534 SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, lpnmh
->iItem
, (LPARAM
)&hdi
);
10536 /* not the rightmost one */
10537 if (hdi
.iOrder
+ 1 < DPA_GetPtrCount(infoPtr
->hdpaColumns
))
10539 INT nIndex
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
10540 hdi
.iOrder
+ 1, 0);
10541 LISTVIEW_ScrollColumns(infoPtr
, nIndex
, dx
);
10545 /* only needs to update the scrolls */
10546 infoPtr
->nItemWidth
+= dx
;
10547 LISTVIEW_UpdateScroll(infoPtr
);
10549 LISTVIEW_UpdateItemSize(infoPtr
);
10550 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& is_redrawing(infoPtr
))
10553 RECT rcCol
= lpColumnInfo
->rcHeader
;
10555 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
10556 OffsetRect(&rcCol
, ptOrigin
.x
, 0);
10558 rcCol
.top
= infoPtr
->rcList
.top
;
10559 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
10561 /* resizing left-aligned columns leaves most of the left side untouched */
10562 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
10564 INT nMaxDirty
= infoPtr
->nEllipsisWidth
+ infoPtr
->ntmMaxCharWidth
;
10567 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- nMaxDirty
);
10570 /* when shrinking the last column clear the now unused field */
10571 if (hdi
.iOrder
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
10577 /* deal with right from rightmost column area */
10578 right
.left
= rcCol
.right
;
10579 right
.top
= rcCol
.top
;
10580 right
.bottom
= rcCol
.bottom
;
10581 right
.right
= infoPtr
->rcList
.right
;
10583 LISTVIEW_InvalidateRect(infoPtr
, &right
);
10586 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
10592 case HDN_ITEMCLICKW
:
10593 case HDN_ITEMCLICKA
:
10595 /* Handle sorting by Header Column */
10598 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
10600 nmlv
.iSubItem
= lpnmh
->iItem
;
10601 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
10602 return notify_forward_header(infoPtr
, lpnmh
);
10605 case HDN_DIVIDERDBLCLICKW
:
10606 case HDN_DIVIDERDBLCLICKA
:
10607 /* FIXME: for LVS_EX_HEADERINALLVIEWS and not LV_VIEW_DETAILS
10608 we should use LVSCW_AUTOSIZE_USEHEADER, helper rework or
10609 split needed for that */
10610 LISTVIEW_SetColumnWidth(infoPtr
, lpnmh
->iItem
, LVSCW_AUTOSIZE
);
10611 return notify_forward_header(infoPtr
, lpnmh
);
10618 * Paint non-client area of control.
10621 * [I] infoPtr : valid pointer to the listview structureof the sender
10622 * [I] region : update region
10625 * TRUE - frame was painted
10626 * FALSE - call default window proc
10628 static BOOL
LISTVIEW_NCPaint(const LISTVIEW_INFO
*infoPtr
, HRGN region
)
10630 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
10634 int cxEdge
= GetSystemMetrics (SM_CXEDGE
),
10635 cyEdge
= GetSystemMetrics (SM_CYEDGE
);
10638 return DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)region
, 0);
10640 GetWindowRect(infoPtr
->hwndSelf
, &r
);
10642 cliprgn
= CreateRectRgn (r
.left
+ cxEdge
, r
.top
+ cyEdge
,
10643 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
10644 if (region
!= (HRGN
)1)
10645 CombineRgn (cliprgn
, cliprgn
, region
, RGN_AND
);
10646 OffsetRect(&r
, -r
.left
, -r
.top
);
10648 dc
= GetDCEx(infoPtr
->hwndSelf
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
10649 OffsetRect(&r
, -r
.left
, -r
.top
);
10651 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
10652 DrawThemeParentBackground(infoPtr
->hwndSelf
, dc
, &r
);
10653 DrawThemeBackground (theme
, dc
, 0, 0, &r
, 0);
10654 ReleaseDC(infoPtr
->hwndSelf
, dc
);
10656 /* Call default proc to get the scrollbars etc. painted */
10657 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)cliprgn
, 0);
10664 * Determines the type of structure to use.
10667 * [I] infoPtr : valid pointer to the listview structureof the sender
10668 * [I] hwndFrom : listview window handle
10669 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
10674 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
10676 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
10678 if (nCommand
== NF_REQUERY
)
10679 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
10681 return infoPtr
->notifyFormat
;
10686 * Paints/Repaints the listview control. Internal use.
10689 * [I] infoPtr : valid pointer to the listview structure
10690 * [I] hdc : device context handle
10695 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
10697 TRACE("(hdc=%p)\n", hdc
);
10699 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
10701 infoPtr
->bNoItemMetrics
= FALSE
;
10702 LISTVIEW_UpdateItemSize(infoPtr
);
10703 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)
10704 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
10705 LISTVIEW_UpdateScroll(infoPtr
);
10708 if (infoPtr
->hwndHeader
) UpdateWindow(infoPtr
->hwndHeader
);
10711 LISTVIEW_Refresh(infoPtr
, hdc
, NULL
);
10716 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
10717 if (!hdc
) return 1;
10718 LISTVIEW_Refresh(infoPtr
, hdc
, ps
.fErase
? &ps
.rcPaint
: NULL
);
10719 EndPaint(infoPtr
->hwndSelf
, &ps
);
10727 * Paints/Repaints the listview control, WM_PAINT handler.
10730 * [I] infoPtr : valid pointer to the listview structure
10731 * [I] hdc : device context handle
10736 static inline LRESULT
LISTVIEW_WMPaint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
10738 TRACE("(hdc=%p)\n", hdc
);
10740 if (!is_redrawing(infoPtr
))
10741 return DefWindowProcW (infoPtr
->hwndSelf
, WM_PAINT
, (WPARAM
)hdc
, 0);
10743 return LISTVIEW_Paint(infoPtr
, hdc
);
10748 * Paints/Repaints the listview control.
10751 * [I] infoPtr : valid pointer to the listview structure
10752 * [I] hdc : device context handle
10753 * [I] options : drawing options
10758 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
10760 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
10763 if (options
& ~(PRF_ERASEBKGND
|PRF_CLIENT
))
10764 FIXME("(hdc=%p options=0x%08x) partial stub\n", hdc
, options
);
10766 if (options
& PRF_ERASEBKGND
)
10767 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
10769 if (options
& PRF_CLIENT
)
10770 LISTVIEW_Paint(infoPtr
, hdc
);
10778 * Processes double click messages (right mouse button).
10781 * [I] infoPtr : valid pointer to the listview structure
10782 * [I] wKey : key flag
10783 * [I] x,y : mouse coordinate
10788 static LRESULT
LISTVIEW_RButtonDblClk(const LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10790 LVHITTESTINFO lvHitTestInfo
;
10792 TRACE("(key=%hu,X=%u,Y=%u)\n", wKey
, x
, y
);
10794 /* send NM_RELEASEDCAPTURE notification */
10795 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10797 /* send NM_RDBLCLK notification */
10798 lvHitTestInfo
.pt
.x
= x
;
10799 lvHitTestInfo
.pt
.y
= y
;
10800 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
10801 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
10808 * Processes WM_RBUTTONDOWN message and corresponding drag operation.
10811 * [I] infoPtr : valid pointer to the listview structure
10812 * [I] wKey : key flag
10813 * [I] x, y : mouse coordinate
10818 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10823 TRACE("(key=%hu, x=%d, y=%d)\n", wKey
, x
, y
);
10825 /* send NM_RELEASEDCAPTURE notification */
10826 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10828 /* determine the index of the selected item */
10831 item
= LISTVIEW_HitTest(infoPtr
, &ht
, TRUE
, TRUE
);
10833 /* make sure the listview control window has the focus */
10834 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
10836 if ((item
>= 0) && (item
< infoPtr
->nItemCount
))
10838 LISTVIEW_SetItemFocus(infoPtr
, item
);
10839 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
10840 !LISTVIEW_GetItemState(infoPtr
, item
, LVIS_SELECTED
))
10841 LISTVIEW_SetSelection(infoPtr
, item
);
10844 LISTVIEW_DeselectAll(infoPtr
);
10846 if (LISTVIEW_TrackMouse(infoPtr
, ht
.pt
))
10848 if (ht
.iItem
!= -1)
10852 memset(&nmlv
, 0, sizeof(nmlv
));
10853 nmlv
.iItem
= ht
.iItem
;
10854 nmlv
.ptAction
= ht
.pt
;
10856 notify_listview(infoPtr
, LVN_BEGINRDRAG
, &nmlv
);
10861 SetFocus(infoPtr
->hwndSelf
);
10865 LISTVIEW_HitTest(infoPtr
, &ht
, TRUE
, FALSE
);
10867 if (notify_click(infoPtr
, NM_RCLICK
, &ht
))
10869 /* Send a WM_CONTEXTMENU message in response to the WM_RBUTTONUP */
10870 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
10871 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)GetMessagePos());
10883 * [I] infoPtr : valid pointer to the listview structure
10884 * [I] hwnd : window handle of window containing the cursor
10885 * [I] nHittest : hit-test code
10886 * [I] wMouseMsg : ideintifier of the mouse message
10889 * TRUE if cursor is set
10892 static BOOL
LISTVIEW_SetCursor(const LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
10894 LVHITTESTINFO lvHitTestInfo
;
10896 if (!LISTVIEW_IsHotTracking(infoPtr
)) goto forward
;
10898 if (!infoPtr
->hHotCursor
) goto forward
;
10900 GetCursorPos(&lvHitTestInfo
.pt
);
10901 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) goto forward
;
10903 SetCursor(infoPtr
->hHotCursor
);
10909 return DefWindowProcW(infoPtr
->hwndSelf
, WM_SETCURSOR
, wParam
, lParam
);
10917 * [I] infoPtr : valid pointer to the listview structure
10918 * [I] hwndLoseFocus : handle of previously focused window
10923 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
10925 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
10927 /* if we have the focus already, there's nothing to do */
10928 if (infoPtr
->bFocus
) return 0;
10930 /* send NM_SETFOCUS notification */
10931 if (!notify(infoPtr
, NM_SETFOCUS
)) return 0;
10933 /* set window focus flag */
10934 infoPtr
->bFocus
= TRUE
;
10936 /* put the focus rect back on */
10937 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
10939 /* redraw all visible selected items */
10940 LISTVIEW_InvalidateSelectedItems(infoPtr
);
10950 * [I] infoPtr : valid pointer to the listview structure
10951 * [I] fRedraw : font handle
10952 * [I] fRedraw : redraw flag
10957 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
10959 HFONT oldFont
= infoPtr
->hFont
;
10960 INT oldHeight
= infoPtr
->nItemHeight
;
10962 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
10964 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
10965 if (infoPtr
->hFont
== oldFont
) return 0;
10967 LISTVIEW_SaveTextMetrics(infoPtr
);
10969 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
10971 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
10973 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
10974 LISTVIEW_UpdateSize(infoPtr
);
10975 LISTVIEW_UpdateScroll(infoPtr
);
10977 else if (infoPtr
->nItemHeight
!= oldHeight
)
10978 LISTVIEW_UpdateScroll(infoPtr
);
10980 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
10987 * Message handling for WM_SETREDRAW.
10988 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
10991 * [I] infoPtr : valid pointer to the listview structure
10992 * [I] redraw: state of redraw flag
10997 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL redraw
)
10999 TRACE("old=%d, new=%d\n", infoPtr
->redraw
, redraw
);
11001 if (infoPtr
->redraw
== !!redraw
)
11004 if (!(infoPtr
->redraw
= !!redraw
))
11007 if (is_autoarrange(infoPtr
))
11008 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
11009 LISTVIEW_UpdateScroll(infoPtr
);
11011 /* despite what the WM_SETREDRAW docs says, apps expect us
11012 * to invalidate the listview here... stupid! */
11013 LISTVIEW_InvalidateList(infoPtr
);
11020 * Resizes the listview control. This function processes WM_SIZE
11021 * messages. At this time, the width and height are not used.
11024 * [I] infoPtr : valid pointer to the listview structure
11025 * [I] Width : new width
11026 * [I] Height : new height
11031 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
11033 RECT rcOld
= infoPtr
->rcList
;
11035 TRACE("(width=%d, height=%d)\n", Width
, Height
);
11037 LISTVIEW_UpdateSize(infoPtr
);
11038 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
11040 /* do not bother with display related stuff if we're not redrawing */
11041 if (!is_redrawing(infoPtr
)) return 0;
11043 if (is_autoarrange(infoPtr
))
11044 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
11046 LISTVIEW_UpdateScroll(infoPtr
);
11048 /* refresh all only for lists whose height changed significantly */
11049 if ((infoPtr
->uView
== LV_VIEW_LIST
) &&
11050 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
11051 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
11052 LISTVIEW_InvalidateList(infoPtr
);
11059 * Sets the size information.
11062 * [I] infoPtr : valid pointer to the listview structure
11067 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
11069 TRACE("uView=%d, rcList(old)=%s\n", infoPtr
->uView
, wine_dbgstr_rect(&infoPtr
->rcList
));
11071 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
11073 if (infoPtr
->uView
== LV_VIEW_LIST
)
11075 /* Apparently the "LIST" style is supposed to have the same
11076 * number of items in a column even if there is no scroll bar.
11077 * Since if a scroll bar already exists then the bottom is already
11078 * reduced, only reduce if the scroll bar does not currently exist.
11079 * The "2" is there to mimic the native control. I think it may be
11080 * related to either padding or edges. (GLA 7/2002)
11082 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_HSCROLL
))
11083 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
11084 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
11087 /* When ListView control is created invisible, header isn't created right away. */
11088 if (infoPtr
->hwndHeader
)
11095 LISTVIEW_GetOrigin(infoPtr
, &origin
);
11097 rect
= infoPtr
->rcList
;
11098 rect
.left
+= origin
.x
;
11099 rect
.top
+= origin
.y
;
11103 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
11104 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp
.flags
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
11106 if (LISTVIEW_IsHeaderEnabled(infoPtr
))
11107 wp
.flags
|= SWP_SHOWWINDOW
;
11110 wp
.flags
|= SWP_HIDEWINDOW
;
11114 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
11115 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
11117 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
11119 /* extra padding for grid */
11120 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
11121 infoPtr
->rcList
.top
+= 2;
11123 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr
->rcList
));
11128 * Processes WM_STYLECHANGED messages.
11131 * [I] infoPtr : valid pointer to the listview structure
11132 * [I] wStyleType : window style type (normal or extended)
11133 * [I] lpss : window style information
11138 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
11139 const STYLESTRUCT
*lpss
)
11141 UINT uNewView
, uOldView
;
11144 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
11145 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
11147 if (wStyleType
!= GWL_STYLE
|| lpss
->styleNew
== infoPtr
->dwStyle
) return 0;
11149 infoPtr
->dwStyle
= lpss
->styleNew
;
11151 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
11152 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
11153 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
11155 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
11156 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
11157 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
11159 uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
11160 uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
11162 if (uNewView
!= uOldView
)
11166 /* LVM_SETVIEW doesn't change window style bits within LVS_TYPEMASK,
11167 changing style updates current view only when view bits change. */
11168 map_style_view(infoPtr
);
11169 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
11170 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
11172 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
11173 SetRectEmpty(&infoPtr
->rcFocus
);
11175 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
11176 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
11178 if (uNewView
== LVS_REPORT
)
11183 LISTVIEW_CreateHeader( infoPtr
);
11185 hl
.prc
= &infoPtr
->rcList
;
11187 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
11188 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
11189 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
11190 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
11193 LISTVIEW_UpdateItemSize(infoPtr
);
11196 if (uNewView
== LVS_REPORT
|| infoPtr
->dwLvExStyle
& LVS_EX_HEADERINALLVIEWS
)
11198 if ((lpss
->styleOld
^ lpss
->styleNew
) & LVS_NOCOLUMNHEADER
)
11200 if (lpss
->styleNew
& LVS_NOCOLUMNHEADER
)
11202 /* Turn off the header control */
11203 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
11204 TRACE("Hide header control, was 0x%08x\n", style
);
11205 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
| HDS_HIDDEN
);
11207 /* Turn on the header control */
11208 if ((style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
)) & HDS_HIDDEN
)
11210 TRACE("Show header control, was 0x%08x\n", style
);
11211 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, (style
& ~HDS_HIDDEN
) | WS_VISIBLE
);
11217 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
11218 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
11219 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
11221 /* update the size of the client area */
11222 LISTVIEW_UpdateSize(infoPtr
);
11224 /* add scrollbars if needed */
11225 LISTVIEW_UpdateScroll(infoPtr
);
11227 /* invalidate client area + erase background */
11228 LISTVIEW_InvalidateList(infoPtr
);
11235 * Processes WM_STYLECHANGING messages.
11238 * [I] wStyleType : window style type (normal or extended)
11239 * [I0] lpss : window style information
11244 static INT
LISTVIEW_StyleChanging(WPARAM wStyleType
,
11247 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
11248 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
11250 /* don't forward LVS_OWNERDATA only if not already set to */
11251 if ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_OWNERDATA
)
11253 if (lpss
->styleOld
& LVS_OWNERDATA
)
11254 lpss
->styleNew
|= LVS_OWNERDATA
;
11256 lpss
->styleNew
&= ~LVS_OWNERDATA
;
11264 * Processes WM_SHOWWINDOW messages.
11267 * [I] infoPtr : valid pointer to the listview structure
11268 * [I] bShown : window is being shown (FALSE when hidden)
11269 * [I] iStatus : window show status
11274 static LRESULT
LISTVIEW_ShowWindow(LISTVIEW_INFO
*infoPtr
, WPARAM bShown
, LPARAM iStatus
)
11276 /* header delayed creation */
11277 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && bShown
)
11279 LISTVIEW_CreateHeader(infoPtr
);
11281 if (!(LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
))
11282 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
11285 return DefWindowProcW(infoPtr
->hwndSelf
, WM_SHOWWINDOW
, bShown
, iStatus
);
11290 * Processes CCM_GETVERSION messages.
11293 * [I] infoPtr : valid pointer to the listview structure
11298 static inline LRESULT
LISTVIEW_GetVersion(const LISTVIEW_INFO
*infoPtr
)
11300 return infoPtr
->iVersion
;
11305 * Processes CCM_SETVERSION messages.
11308 * [I] infoPtr : valid pointer to the listview structure
11309 * [I] iVersion : version to be set
11312 * -1 when requested version is greater than DLL version;
11313 * previous version otherwise
11315 static LRESULT
LISTVIEW_SetVersion(LISTVIEW_INFO
*infoPtr
, DWORD iVersion
)
11317 INT iOldVersion
= infoPtr
->iVersion
;
11319 if (iVersion
> COMCTL32_VERSION
)
11322 infoPtr
->iVersion
= iVersion
;
11324 TRACE("new version %d\n", iVersion
);
11326 return iOldVersion
;
11331 * Window procedure of the listview control.
11334 static LRESULT WINAPI
11335 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
11337 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
11339 TRACE("(hwnd=%p uMsg=%x wParam=%lx lParam=%lx)\n", hwnd
, uMsg
, wParam
, lParam
);
11341 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
11342 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11346 case LVM_APPROXIMATEVIEWRECT
:
11347 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
11348 LOWORD(lParam
), HIWORD(lParam
));
11350 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
11352 case LVM_CANCELEDITLABEL
:
11353 return LISTVIEW_CancelEditLabel(infoPtr
);
11355 case LVM_CREATEDRAGIMAGE
:
11356 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
11358 case LVM_DELETEALLITEMS
:
11359 return LISTVIEW_DeleteAllItems(infoPtr
, FALSE
);
11361 case LVM_DELETECOLUMN
:
11362 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
11364 case LVM_DELETEITEM
:
11365 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
11367 case LVM_EDITLABELA
:
11368 case LVM_EDITLABELW
:
11369 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
,
11370 uMsg
== LVM_EDITLABELW
);
11371 /* case LVM_ENABLEGROUPVIEW: */
11373 case LVM_ENSUREVISIBLE
:
11374 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
11376 case LVM_FINDITEMW
:
11377 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
11379 case LVM_FINDITEMA
:
11380 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
11382 case LVM_GETBKCOLOR
:
11383 return infoPtr
->clrBk
;
11385 /* case LVM_GETBKIMAGE: */
11387 case LVM_GETCALLBACKMASK
:
11388 return infoPtr
->uCallbackMask
;
11390 case LVM_GETCOLUMNA
:
11391 case LVM_GETCOLUMNW
:
11392 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
,
11393 uMsg
== LVM_GETCOLUMNW
);
11395 case LVM_GETCOLUMNORDERARRAY
:
11396 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
11398 case LVM_GETCOLUMNWIDTH
:
11399 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
11401 case LVM_GETCOUNTPERPAGE
:
11402 return LISTVIEW_GetCountPerPage(infoPtr
);
11404 case LVM_GETEDITCONTROL
:
11405 return (LRESULT
)infoPtr
->hwndEdit
;
11407 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
11408 return infoPtr
->dwLvExStyle
;
11410 /* case LVM_GETGROUPINFO: */
11412 /* case LVM_GETGROUPMETRICS: */
11414 case LVM_GETHEADER
:
11415 return (LRESULT
)infoPtr
->hwndHeader
;
11417 case LVM_GETHOTCURSOR
:
11418 return (LRESULT
)infoPtr
->hHotCursor
;
11420 case LVM_GETHOTITEM
:
11421 return infoPtr
->nHotItem
;
11423 case LVM_GETHOVERTIME
:
11424 return infoPtr
->dwHoverTime
;
11426 case LVM_GETIMAGELIST
:
11427 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
11429 /* case LVM_GETINSERTMARK: */
11431 /* case LVM_GETINSERTMARKCOLOR: */
11433 /* case LVM_GETINSERTMARKRECT: */
11435 case LVM_GETISEARCHSTRINGA
:
11436 case LVM_GETISEARCHSTRINGW
:
11437 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
11442 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, uMsg
== LVM_GETITEMW
);
11444 case LVM_GETITEMCOUNT
:
11445 return infoPtr
->nItemCount
;
11447 case LVM_GETITEMPOSITION
:
11448 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
11450 case LVM_GETITEMRECT
:
11451 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
11453 case LVM_GETITEMSPACING
:
11454 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
11456 case LVM_GETITEMSTATE
:
11457 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
11459 case LVM_GETITEMTEXTA
:
11460 case LVM_GETITEMTEXTW
:
11461 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
,
11462 uMsg
== LVM_GETITEMTEXTW
);
11464 case LVM_GETNEXTITEM
:
11465 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
11467 case LVM_GETNUMBEROFWORKAREAS
:
11468 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
11471 case LVM_GETORIGIN
:
11472 if (!lParam
) return FALSE
;
11473 if (infoPtr
->uView
== LV_VIEW_DETAILS
||
11474 infoPtr
->uView
== LV_VIEW_LIST
) return FALSE
;
11475 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
11478 /* case LVM_GETOUTLINECOLOR: */
11480 /* case LVM_GETSELECTEDCOLUMN: */
11482 case LVM_GETSELECTEDCOUNT
:
11483 return LISTVIEW_GetSelectedCount(infoPtr
);
11485 case LVM_GETSELECTIONMARK
:
11486 return infoPtr
->nSelectionMark
;
11488 case LVM_GETSTRINGWIDTHA
:
11489 case LVM_GETSTRINGWIDTHW
:
11490 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
,
11491 uMsg
== LVM_GETSTRINGWIDTHW
);
11493 case LVM_GETSUBITEMRECT
:
11494 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
11496 case LVM_GETTEXTBKCOLOR
:
11497 return infoPtr
->clrTextBk
;
11499 case LVM_GETTEXTCOLOR
:
11500 return infoPtr
->clrText
;
11502 /* case LVM_GETTILEINFO: */
11504 /* case LVM_GETTILEVIEWINFO: */
11506 case LVM_GETTOOLTIPS
:
11507 if( !infoPtr
->hwndToolTip
)
11508 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
11509 return (LRESULT
)infoPtr
->hwndToolTip
;
11511 case LVM_GETTOPINDEX
:
11512 return LISTVIEW_GetTopIndex(infoPtr
);
11514 case LVM_GETUNICODEFORMAT
:
11515 return (infoPtr
->notifyFormat
== NFR_UNICODE
);
11518 return infoPtr
->uView
;
11520 case LVM_GETVIEWRECT
:
11521 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
11523 case LVM_GETWORKAREAS
:
11524 FIXME("LVM_GETWORKAREAS: unimplemented\n");
11527 /* case LVM_HASGROUP: */
11530 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, TRUE
);
11532 case LVM_INSERTCOLUMNA
:
11533 case LVM_INSERTCOLUMNW
:
11534 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
,
11535 uMsg
== LVM_INSERTCOLUMNW
);
11537 /* case LVM_INSERTGROUP: */
11539 /* case LVM_INSERTGROUPSORTED: */
11541 case LVM_INSERTITEMA
:
11542 case LVM_INSERTITEMW
:
11543 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, uMsg
== LVM_INSERTITEMW
);
11545 /* case LVM_INSERTMARKHITTEST: */
11547 /* case LVM_ISGROUPVIEWENABLED: */
11549 case LVM_ISITEMVISIBLE
:
11550 return LISTVIEW_IsItemVisible(infoPtr
, (INT
)wParam
);
11552 case LVM_MAPIDTOINDEX
:
11553 return LISTVIEW_MapIdToIndex(infoPtr
, (UINT
)wParam
);
11555 case LVM_MAPINDEXTOID
:
11556 return LISTVIEW_MapIndexToId(infoPtr
, (INT
)wParam
);
11558 /* case LVM_MOVEGROUP: */
11560 /* case LVM_MOVEITEMTOGROUP: */
11562 case LVM_REDRAWITEMS
:
11563 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
11565 /* case LVM_REMOVEALLGROUPS: */
11567 /* case LVM_REMOVEGROUP: */
11570 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
11572 case LVM_SETBKCOLOR
:
11573 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
11575 /* case LVM_SETBKIMAGE: */
11577 case LVM_SETCALLBACKMASK
:
11578 infoPtr
->uCallbackMask
= (UINT
)wParam
;
11581 case LVM_SETCOLUMNA
:
11582 case LVM_SETCOLUMNW
:
11583 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
,
11584 uMsg
== LVM_SETCOLUMNW
);
11586 case LVM_SETCOLUMNORDERARRAY
:
11587 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
11589 case LVM_SETCOLUMNWIDTH
:
11590 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
11592 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
11593 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
11595 /* case LVM_SETGROUPINFO: */
11597 /* case LVM_SETGROUPMETRICS: */
11599 case LVM_SETHOTCURSOR
:
11600 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
11602 case LVM_SETHOTITEM
:
11603 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
11605 case LVM_SETHOVERTIME
:
11606 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)lParam
);
11608 case LVM_SETICONSPACING
:
11610 return LISTVIEW_SetIconSpacing(infoPtr
, -1, -1);
11611 return LISTVIEW_SetIconSpacing(infoPtr
, LOWORD(lParam
), HIWORD(lParam
));
11613 case LVM_SETIMAGELIST
:
11614 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
11616 /* case LVM_SETINFOTIP: */
11618 /* case LVM_SETINSERTMARK: */
11620 /* case LVM_SETINSERTMARKCOLOR: */
11625 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
11626 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, (uMsg
== LVM_SETITEMW
));
11629 case LVM_SETITEMCOUNT
:
11630 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
11632 case LVM_SETITEMPOSITION
:
11635 pt
.x
= (short)LOWORD(lParam
);
11636 pt
.y
= (short)HIWORD(lParam
);
11637 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, &pt
);
11640 case LVM_SETITEMPOSITION32
:
11641 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, (POINT
*)lParam
);
11643 case LVM_SETITEMSTATE
:
11644 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
11646 case LVM_SETITEMTEXTA
:
11647 case LVM_SETITEMTEXTW
:
11648 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
,
11649 uMsg
== LVM_SETITEMTEXTW
);
11651 /* case LVM_SETOUTLINECOLOR: */
11653 /* case LVM_SETSELECTEDCOLUMN: */
11655 case LVM_SETSELECTIONMARK
:
11656 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
11658 case LVM_SETTEXTBKCOLOR
:
11659 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
11661 case LVM_SETTEXTCOLOR
:
11662 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
11664 /* case LVM_SETTILEINFO: */
11666 /* case LVM_SETTILEVIEWINFO: */
11668 /* case LVM_SETTILEWIDTH: */
11670 case LVM_SETTOOLTIPS
:
11671 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
11673 case LVM_SETUNICODEFORMAT
:
11674 return LISTVIEW_SetUnicodeFormat(infoPtr
, wParam
);
11677 return LISTVIEW_SetView(infoPtr
, wParam
);
11679 /* case LVM_SETWORKAREAS: */
11681 /* case LVM_SORTGROUPS: */
11683 case LVM_SORTITEMS
:
11684 case LVM_SORTITEMSEX
:
11685 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, wParam
,
11686 uMsg
== LVM_SORTITEMSEX
);
11687 case LVM_SUBITEMHITTEST
:
11688 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
11691 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
11693 case CCM_GETVERSION
:
11694 return LISTVIEW_GetVersion(infoPtr
);
11696 case CCM_SETVERSION
:
11697 return LISTVIEW_SetVersion(infoPtr
, wParam
);
11700 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
11703 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
11706 return LISTVIEW_NCCreate(hwnd
, wParam
, (LPCREATESTRUCTW
)lParam
);
11709 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
11712 return LISTVIEW_Destroy(infoPtr
);
11715 return LISTVIEW_Enable(infoPtr
);
11717 case WM_ERASEBKGND
:
11718 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
11720 case WM_GETDLGCODE
:
11721 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
11724 return (LRESULT
)infoPtr
->hFont
;
11727 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0);
11730 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
11733 return LISTVIEW_KillFocus(infoPtr
);
11735 case WM_LBUTTONDBLCLK
:
11736 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11738 case WM_LBUTTONDOWN
:
11739 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11742 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11745 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11747 case WM_MOUSEHOVER
:
11748 return LISTVIEW_MouseHover(infoPtr
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11751 return LISTVIEW_NCDestroy(infoPtr
);
11754 return LISTVIEW_NCPaint(infoPtr
, (HRGN
)wParam
);
11757 return LISTVIEW_Notify(infoPtr
, (LPNMHDR
)lParam
);
11759 case WM_NOTIFYFORMAT
:
11760 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
11762 case WM_PRINTCLIENT
:
11763 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
11766 return LISTVIEW_WMPaint(infoPtr
, (HDC
)wParam
);
11768 case WM_RBUTTONDBLCLK
:
11769 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11771 case WM_RBUTTONDOWN
:
11772 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11775 return LISTVIEW_SetCursor(infoPtr
, wParam
, lParam
);
11778 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
11781 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
11784 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
11786 case WM_SHOWWINDOW
:
11787 return LISTVIEW_ShowWindow(infoPtr
, wParam
, lParam
);
11789 case WM_STYLECHANGED
:
11790 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
11792 case WM_STYLECHANGING
:
11793 return LISTVIEW_StyleChanging(wParam
, (LPSTYLESTRUCT
)lParam
);
11795 case WM_SYSCOLORCHANGE
:
11796 COMCTL32_RefreshSysColors();
11799 /* case WM_TIMER: */
11800 case WM_THEMECHANGED
:
11801 return LISTVIEW_ThemeChanged(infoPtr
);
11804 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0);
11806 case WM_MOUSEWHEEL
:
11807 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
11808 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11809 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
11811 case WM_WINDOWPOSCHANGED
:
11812 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
11814 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
11815 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
11817 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
11819 if (notify_measureitem(infoPtr
)) LISTVIEW_InvalidateList(infoPtr
);
11821 LISTVIEW_Size(infoPtr
, ((WINDOWPOS
*)lParam
)->cx
, ((WINDOWPOS
*)lParam
)->cy
);
11823 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11825 /* case WM_WININICHANGE: */
11828 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
) && !COMCTL32_IsReflectedMessage(uMsg
))
11829 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg
, wParam
, lParam
);
11831 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11838 * Registers the window class.
11846 void LISTVIEW_Register(void)
11848 WNDCLASSW wndClass
;
11850 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
11851 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
11852 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
11853 wndClass
.cbClsExtra
= 0;
11854 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
11855 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
11856 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
11857 wndClass
.lpszClassName
= WC_LISTVIEWW
;
11858 RegisterClassW(&wndClass
);
11863 * Unregisters the window class.
11871 void LISTVIEW_Unregister(void)
11873 UnregisterClassW(WC_LISTVIEWW
, NULL
);
11878 * Handle any WM_COMMAND messages
11881 * [I] infoPtr : valid pointer to the listview structure
11882 * [I] wParam : the first message parameter
11883 * [I] lParam : the second message parameter
11888 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
11891 TRACE("(%p %x %x %lx)\n", infoPtr
, HIWORD(wParam
), LOWORD(wParam
), lParam
);
11893 if (!infoPtr
->hwndEdit
) return 0;
11895 switch (HIWORD(wParam
))
11900 * Adjust the edit window size
11902 WCHAR buffer
[1024];
11903 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
11904 HFONT hFont
, hOldFont
= 0;
11908 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
11909 GetWindowTextW(infoPtr
->hwndEdit
, buffer
, ARRAY_SIZE(buffer
));
11910 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
11912 /* Select font to get the right dimension of the string */
11913 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
11916 hOldFont
= SelectObject(hdc
, hFont
);
11919 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
11921 TEXTMETRICW textMetric
;
11923 /* Add Extra spacing for the next character */
11924 GetTextMetricsW(hdc
, &textMetric
);
11925 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
11927 SetWindowPos(infoPtr
->hwndEdit
, NULL
, 0, 0, sz
.cx
,
11928 rect
.bottom
- rect
.top
, SWP_DRAWFRAME
| SWP_NOMOVE
| SWP_NOZORDER
);
11931 SelectObject(hdc
, hOldFont
);
11933 ReleaseDC(infoPtr
->hwndEdit
, hdc
);
11939 if (infoPtr
->notify_mask
& NOTIFY_MASK_END_LABEL_EDIT
)
11940 LISTVIEW_CancelEditLabel(infoPtr
);
11945 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);