4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
35 * Default Message Processing
36 * -- EN_KILLFOCUS should be handled in WM_COMMAND
37 * -- WM_CREATE: create the icon and small icon image lists at this point only if
38 * the LVS_SHAREIMAGELISTS style is not specified.
39 * -- WM_ERASEBKGND: forward this message to the parent window if the bkgnd
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_[GS]etColumnOrderArray stubs
58 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
59 * -- LISTVIEW_SetIconSpacing is incomplete
60 * -- LISTVIEW_SortItems is broken
61 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
64 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
65 * linear in the number of items in the list, and this is
66 * unacceptable for large lists.
67 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
68 * instead of inserting in the right spot
69 * -- we should keep an ordered array of coordinates in iconic mode
70 * this would allow to frame items (iterator_frameditems),
71 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
79 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
86 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
89 * -- LVS_TYPESTYLEMASK
92 * -- LVS_EX_BORDERSELECT
95 * -- LVS_EX_HEADERDRAGDROP
98 * -- LVS_EX_MULTIWORKAREAS
99 * -- LVS_EX_ONECLICKACTIVATE
101 * -- LVS_EX_SIMPLESELECT
102 * -- LVS_EX_TRACKSELECT
103 * -- LVS_EX_TWOCLICKACTIVATE
104 * -- LVS_EX_UNDERLINECOLD
105 * -- LVS_EX_UNDERLINEHOT
108 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
111 * -- LVN_MARQUEEBEGIN
118 * -- LVM_CANCELEDITLABEL
119 * -- LVM_ENABLEGROUPVIEW
120 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
121 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
122 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
123 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
124 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
125 * -- LVM_GETINSERTMARKRECT
126 * -- LVM_GETNUMBEROFWORKAREAS
127 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
128 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
129 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
130 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
131 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
132 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
133 * -- LVM_GETVIEW, LVM_SETVIEW
134 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
135 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
136 * -- LVM_INSERTGROUPSORTED
137 * -- LVM_INSERTMARKHITTEST
138 * -- LVM_ISGROUPVIEWENABLED
139 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
141 * -- LVM_MOVEITEMTOGROUP
143 * -- LVM_SETTILEWIDTH
148 * -- ListView_GetCheckSate, ListView_SetCheckState
149 * -- ListView_GetHoverTime, ListView_SetHoverTime
150 * -- ListView_GetISearchString
151 * -- ListView_GetNumberOfWorkAreas
152 * -- ListView_GetOrigin
153 * -- ListView_GetTextBkColor
154 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
155 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
156 * -- ListView_SortItemsEx
161 * Known differences in message stream from native control (not known if
162 * these differences cause problems):
163 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
164 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
165 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
166 * processing for "USEDOUBLECLICKTIME".
170 #include "wine/port.h"
185 #include "commctrl.h"
186 #include "comctl32.h"
189 #include "wine/debug.h"
190 #include "wine/unicode.h"
192 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
194 /* make sure you set this to 0 for production use! */
195 #define DEBUG_RANGES 1
197 typedef struct tagCOLUMN_INFO
199 RECT rcHeader
; /* tracks the header's rectangle */
200 int fmt
; /* same as LVCOLUMN.fmt */
203 typedef struct tagITEMHDR
207 } ITEMHDR
, *LPITEMHDR
;
209 typedef struct tagSUBITEM_INFO
215 typedef struct tagITEM_INFO
223 typedef struct tagRANGE
229 typedef struct tagRANGES
234 typedef struct tagITERATOR
243 typedef struct tagLISTVIEW_INFO
250 COLORREF clrTextBkDefault
;
251 HIMAGELIST himlNormal
;
252 HIMAGELIST himlSmall
;
253 HIMAGELIST himlState
;
256 POINT ptClickPos
; /* point where the user clicked */
257 BOOL bNoItemMetrics
; /* flags if item metrics are not yet computed */
260 RANGES selectionRanges
;
265 RECT rcList
; /* This rectangle is really the window
266 * client rectangle possibly reduced by the
267 * horizontal scroll bar and/or header - see
268 * LISTVIEW_UpdateSize. This rectangle offset
269 * by the LISTVIEW_GetOrigin value is in
270 * client coordinates */
279 INT ntmHeight
; /* Some cached metrics of the font used */
280 INT ntmMaxCharWidth
; /* by the listview to draw items */
282 BOOL bRedraw
; /* Turns on/off repaints & invalidations */
283 BOOL bAutoarrange
; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
285 BOOL bDoChangeNotify
; /* send change notification messages? */
288 DWORD dwStyle
; /* the cached window GWL_STYLE */
289 DWORD dwLvExStyle
; /* extended listview style */
290 INT nItemCount
; /* the number of items in the list */
291 HDPA hdpaItems
; /* array ITEM_INFO pointers */
292 HDPA hdpaPosX
; /* maintains the (X, Y) coordinates of the */
293 HDPA hdpaPosY
; /* items in LVS_ICON, and LVS_SMALLICON modes */
294 HDPA hdpaColumns
; /* array of COLUMN_INFO pointers */
295 POINT currIconPos
; /* this is the position next icon will be placed */
296 PFNLVCOMPARE pfnCompare
;
304 DWORD cditemmode
; /* Keep the custom draw flags for an item/row */
306 DWORD lastKeyPressTimestamp
;
308 INT nSearchParamLength
;
309 WCHAR szSearchParam
[ MAX_PATH
];
311 INT nMeasureItemHeight
;
317 /* How many we debug buffer to allocate */
318 #define DEBUG_BUFFERS 20
319 /* The size of a single debug bbuffer */
320 #define DEBUG_BUFFER_SIZE 256
322 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
323 #define SB_INTERNAL -1
325 /* maximum size of a label */
326 #define DISP_TEXT_SIZE 512
328 /* padding for items in list and small icon display modes */
329 #define WIDTH_PADDING 12
331 /* padding for items in list, report and small icon display modes */
332 #define HEIGHT_PADDING 1
334 /* offset of items in report display mode */
335 #define REPORT_MARGINX 2
337 /* padding for icon in large icon display mode
338 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
339 * that HITTEST will see.
340 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
341 * ICON_TOP_PADDING - sum of the two above.
342 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
343 * LABEL_HOR_PADDING - between text and sides of box
344 * LABEL_VERT_PADDING - between bottom of text and end of box
346 * ICON_LR_PADDING - additional width above icon size.
347 * ICON_LR_HALF - half of the above value
349 #define ICON_TOP_PADDING_NOTHITABLE 2
350 #define ICON_TOP_PADDING_HITABLE 2
351 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
352 #define ICON_BOTTOM_PADDING 4
353 #define LABEL_HOR_PADDING 5
354 #define LABEL_VERT_PADDING 7
355 #define ICON_LR_PADDING 16
356 #define ICON_LR_HALF (ICON_LR_PADDING/2)
358 /* default label width for items in list and small icon display modes */
359 #define DEFAULT_LABEL_WIDTH 40
361 /* default column width for items in list display mode */
362 #define DEFAULT_COLUMN_WIDTH 128
364 /* Size of "line" scroll for V & H scrolls */
365 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
367 /* Padding betwen image and label */
368 #define IMAGE_PADDING 2
370 /* Padding behind the label */
371 #define TRAILING_LABEL_PADDING 12
372 #define TRAILING_HEADER_PADDING 11
374 /* Border for the icon caption */
375 #define CAPTION_BORDER 2
377 /* Standard DrawText flags */
378 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
379 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
380 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
382 /* Image index from state */
383 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
385 /* The time in milliseconds to reset the search in the list */
386 #define KEY_DELAY 450
388 /* Dump the LISTVIEW_INFO structure to the debug channel */
389 #define LISTVIEW_DUMP(iP) do { \
390 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
391 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
392 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
393 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
394 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
395 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
396 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
397 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
398 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
399 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
402 static const WCHAR themeClass
[] = {'L','i','s','t','V','i','e','w',0};
405 * forward declarations
407 static BOOL
LISTVIEW_GetItemT(LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
408 static void LISTVIEW_GetItemBox(LISTVIEW_INFO
*, INT
, LPRECT
);
409 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO
*, INT
, LPPOINT
);
410 static BOOL
LISTVIEW_GetItemPosition(LISTVIEW_INFO
*, INT
, LPPOINT
);
411 static BOOL
LISTVIEW_GetItemRect(LISTVIEW_INFO
*, INT
, LPRECT
);
412 static INT
LISTVIEW_GetLabelWidth(LISTVIEW_INFO
*, INT
);
413 static void LISTVIEW_GetOrigin(LISTVIEW_INFO
*, LPPOINT
);
414 static BOOL
LISTVIEW_GetViewRect(LISTVIEW_INFO
*, LPRECT
);
415 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*, INT
);
416 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*, const LVITEMW
*, BOOL
);
417 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO
*);
418 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*, INT
);
419 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
420 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*, INT
, BOOL
);
421 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*, WPARAM
, LPARAM
);
422 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*, PFNLVCOMPARE
, LPARAM
);
423 static INT
LISTVIEW_GetStringWidthT(LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
424 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
);
425 static UINT
LISTVIEW_GetItemState(LISTVIEW_INFO
*, INT
, UINT
);
426 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, const LVITEMW
*);
427 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
428 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
429 static INT
LISTVIEW_GetTopIndex(LISTVIEW_INFO
*);
430 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
431 static HWND
CreateEditLabelT(LISTVIEW_INFO
*, LPCWSTR
, DWORD
, INT
, INT
, INT
, INT
, BOOL
);
432 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*, INT
, HIMAGELIST
);
433 static INT
LISTVIEW_HitTest(LISTVIEW_INFO
*, LPLVHITTESTINFO
, BOOL
, BOOL
);
435 /******** Text handling functions *************************************/
437 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
438 * text string. The string may be ANSI or Unicode, in which case
439 * the boolean isW tells us the type of the string.
441 * The name of the function tell what type of strings it expects:
442 * W: Unicode, T: ANSI/Unicode - function of isW
445 static inline BOOL
is_textW(LPCWSTR text
)
447 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
450 static inline BOOL
is_textT(LPCWSTR text
, BOOL isW
)
452 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
453 return is_textW(text
);
456 static inline int textlenT(LPCWSTR text
, BOOL isW
)
458 return !is_textT(text
, isW
) ? 0 :
459 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
462 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
465 if (isSrcW
) lstrcpynW(dest
, src
, max
);
466 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
468 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
469 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
472 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
474 LPWSTR wstr
= (LPWSTR
)text
;
476 if (!isW
&& is_textT(text
, isW
))
478 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
479 wstr
= Alloc(len
* sizeof(WCHAR
));
480 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
482 TRACE(" wstr=%s\n", text
== LPSTR_TEXTCALLBACKW
? "(callback)" : debugstr_w(wstr
));
486 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
488 if (!isW
&& is_textT(wstr
, isW
)) Free (wstr
);
492 * dest is a pointer to a Unicode string
493 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
495 static BOOL
textsetptrT(LPWSTR
*dest
, LPWSTR src
, BOOL isW
)
499 if (src
== LPSTR_TEXTCALLBACKW
)
501 if (is_textW(*dest
)) Free(*dest
);
502 *dest
= LPSTR_TEXTCALLBACKW
;
506 LPWSTR pszText
= textdupTtoW(src
, isW
);
507 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
508 bResult
= Str_SetPtrW(dest
, pszText
);
509 textfreeT(pszText
, isW
);
515 * compares a Unicode to a Unicode/ANSI text string
517 static inline int textcmpWT(LPCWSTR aw
, LPCWSTR bt
, BOOL isW
)
519 if (!aw
) return bt
? -1 : 0;
520 if (!bt
) return aw
? 1 : 0;
521 if (aw
== LPSTR_TEXTCALLBACKW
)
522 return bt
== LPSTR_TEXTCALLBACKW
? 0 : -1;
523 if (bt
!= LPSTR_TEXTCALLBACKW
)
525 LPWSTR bw
= textdupTtoW(bt
, isW
);
526 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
534 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
538 n
= min(min(n
, strlenW(s1
)), strlenW(s2
));
539 res
= CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
);
540 return res
? res
- sizeof(WCHAR
) : res
;
543 /******** Debugging functions *****************************************/
545 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
547 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
548 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
551 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
553 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
554 n
= min(textlenT(text
, isW
), n
);
555 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
558 static char* debug_getbuf(void)
560 static int index
= 0;
561 static char buffers
[DEBUG_BUFFERS
][DEBUG_BUFFER_SIZE
];
562 return buffers
[index
++ % DEBUG_BUFFERS
];
565 static inline const char* debugrange(const RANGE
*lprng
)
567 if (!lprng
) return "(null)";
568 return wine_dbg_sprintf("[%d, %d)", lprng
->lower
, lprng
->upper
);
571 static const char* debugscrollinfo(const SCROLLINFO
*pScrollInfo
)
573 char* buf
= debug_getbuf(), *text
= buf
;
574 int len
, size
= DEBUG_BUFFER_SIZE
;
576 if (pScrollInfo
== NULL
) return "(null)";
577 len
= snprintf(buf
, size
, "{cbSize=%d, ", pScrollInfo
->cbSize
);
578 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
579 if (pScrollInfo
->fMask
& SIF_RANGE
)
580 len
= snprintf(buf
, size
, "nMin=%d, nMax=%d, ", pScrollInfo
->nMin
, pScrollInfo
->nMax
);
582 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
583 if (pScrollInfo
->fMask
& SIF_PAGE
)
584 len
= snprintf(buf
, size
, "nPage=%u, ", pScrollInfo
->nPage
);
586 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
587 if (pScrollInfo
->fMask
& SIF_POS
)
588 len
= snprintf(buf
, size
, "nPos=%d, ", pScrollInfo
->nPos
);
590 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
591 if (pScrollInfo
->fMask
& SIF_TRACKPOS
)
592 len
= snprintf(buf
, size
, "nTrackPos=%d, ", pScrollInfo
->nTrackPos
);
594 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
597 buf
= text
+ strlen(text
);
599 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
603 static const char* debugnmlistview(const NMLISTVIEW
*plvnm
)
605 if (!plvnm
) return "(null)";
606 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
607 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
608 plvnm
->iItem
, plvnm
->iSubItem
, plvnm
->uNewState
, plvnm
->uOldState
,
609 plvnm
->uChanged
, wine_dbgstr_point(&plvnm
->ptAction
), plvnm
->lParam
);
612 static const char* debuglvitem_t(const LVITEMW
*lpLVItem
, BOOL isW
)
614 char* buf
= debug_getbuf(), *text
= buf
;
615 int len
, size
= DEBUG_BUFFER_SIZE
;
617 if (lpLVItem
== NULL
) return "(null)";
618 len
= snprintf(buf
, size
, "{iItem=%d, iSubItem=%d, ", lpLVItem
->iItem
, lpLVItem
->iSubItem
);
619 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
620 if (lpLVItem
->mask
& LVIF_STATE
)
621 len
= snprintf(buf
, size
, "state=%x, stateMask=%x, ", lpLVItem
->state
, lpLVItem
->stateMask
);
623 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
624 if (lpLVItem
->mask
& LVIF_TEXT
)
625 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem
->pszText
, isW
, 80), lpLVItem
->cchTextMax
);
627 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
628 if (lpLVItem
->mask
& LVIF_IMAGE
)
629 len
= snprintf(buf
, size
, "iImage=%d, ", lpLVItem
->iImage
);
631 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
632 if (lpLVItem
->mask
& LVIF_PARAM
)
633 len
= snprintf(buf
, size
, "lParam=%lx, ", lpLVItem
->lParam
);
635 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
636 if (lpLVItem
->mask
& LVIF_INDENT
)
637 len
= snprintf(buf
, size
, "iIndent=%d, ", lpLVItem
->iIndent
);
639 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
642 buf
= text
+ strlen(text
);
644 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
648 static const char* debuglvcolumn_t(const LVCOLUMNW
*lpColumn
, BOOL isW
)
650 char* buf
= debug_getbuf(), *text
= buf
;
651 int len
, size
= DEBUG_BUFFER_SIZE
;
653 if (lpColumn
== NULL
) return "(null)";
654 len
= snprintf(buf
, size
, "{");
655 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
656 if (lpColumn
->mask
& LVCF_SUBITEM
)
657 len
= snprintf(buf
, size
, "iSubItem=%d, ", lpColumn
->iSubItem
);
659 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
660 if (lpColumn
->mask
& LVCF_FMT
)
661 len
= snprintf(buf
, size
, "fmt=%x, ", lpColumn
->fmt
);
663 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
664 if (lpColumn
->mask
& LVCF_WIDTH
)
665 len
= snprintf(buf
, size
, "cx=%d, ", lpColumn
->cx
);
667 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
668 if (lpColumn
->mask
& LVCF_TEXT
)
669 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn
->pszText
, isW
, 80), lpColumn
->cchTextMax
);
671 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
672 if (lpColumn
->mask
& LVCF_IMAGE
)
673 len
= snprintf(buf
, size
, "iImage=%d, ", lpColumn
->iImage
);
675 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
676 if (lpColumn
->mask
& LVCF_ORDER
)
677 len
= snprintf(buf
, size
, "iOrder=%d, ", lpColumn
->iOrder
);
679 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
682 buf
= text
+ strlen(text
);
684 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
688 static const char* debuglvhittestinfo(const LVHITTESTINFO
*lpht
)
690 if (!lpht
) return "(null)";
692 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
693 wine_dbgstr_point(&lpht
->pt
), lpht
->flags
, lpht
->iItem
, lpht
->iSubItem
);
696 /* Return the corresponding text for a given scroll value */
697 static inline LPCSTR
debugscrollcode(int nScrollCode
)
701 case SB_LINELEFT
: return "SB_LINELEFT";
702 case SB_LINERIGHT
: return "SB_LINERIGHT";
703 case SB_PAGELEFT
: return "SB_PAGELEFT";
704 case SB_PAGERIGHT
: return "SB_PAGERIGHT";
705 case SB_THUMBPOSITION
: return "SB_THUMBPOSITION";
706 case SB_THUMBTRACK
: return "SB_THUMBTRACK";
707 case SB_ENDSCROLL
: return "SB_ENDSCROLL";
708 case SB_INTERNAL
: return "SB_INTERNAL";
709 default: return "unknown";
714 /******** Notification functions i************************************/
716 static LRESULT
notify_forward_header(LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
718 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
719 (WPARAM
)lpnmh
->hdr
.idFrom
, (LPARAM
)lpnmh
);
722 static LRESULT
notify_hdr(LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
726 TRACE("(code=%d)\n", code
);
728 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
729 pnmh
->idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
731 result
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
732 (WPARAM
)pnmh
->idFrom
, (LPARAM
)pnmh
);
734 TRACE(" <= %ld\n", result
);
739 static inline BOOL
notify(LISTVIEW_INFO
*infoPtr
, INT code
)
742 HWND hwnd
= infoPtr
->hwndSelf
;
743 notify_hdr(infoPtr
, code
, &nmh
);
744 return IsWindow(hwnd
);
747 static inline void notify_itemactivate(LISTVIEW_INFO
*infoPtr
, LVHITTESTINFO
*htInfo
)
758 item
.mask
= LVIF_PARAM
|LVIF_STATE
;
759 item
.iItem
= htInfo
->iItem
;
761 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) {
762 nmia
.lParam
= item
.lParam
;
763 nmia
.uOldState
= item
.state
;
764 nmia
.uNewState
= item
.state
| LVIS_ACTIVATING
;
765 nmia
.uChanged
= LVIF_STATE
;
768 nmia
.iItem
= htInfo
->iItem
;
769 nmia
.iSubItem
= htInfo
->iSubItem
;
770 nmia
.ptAction
= htInfo
->pt
;
772 if (GetKeyState(VK_SHIFT
) & 0x8000) nmia
.uKeyFlags
|= LVKF_SHIFT
;
773 if (GetKeyState(VK_CONTROL
) & 0x8000) nmia
.uKeyFlags
|= LVKF_CONTROL
;
774 if (GetKeyState(VK_MENU
) & 0x8000) nmia
.uKeyFlags
|= LVKF_ALT
;
776 notify_hdr(infoPtr
, LVN_ITEMACTIVATE
, (LPNMHDR
)&nmia
);
779 static inline LRESULT
notify_listview(LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
781 TRACE("(code=%d, plvnm=%s)\n", code
, debugnmlistview(plvnm
));
782 return notify_hdr(infoPtr
, code
, (LPNMHDR
)plvnm
);
785 static BOOL
notify_click(LISTVIEW_INFO
*infoPtr
, INT code
, LVHITTESTINFO
*lvht
)
789 HWND hwnd
= infoPtr
->hwndSelf
;
791 TRACE("code=%d, lvht=%s\n", code
, debuglvhittestinfo(lvht
));
792 ZeroMemory(&nmlv
, sizeof(nmlv
));
793 nmlv
.iItem
= lvht
->iItem
;
794 nmlv
.iSubItem
= lvht
->iSubItem
;
795 nmlv
.ptAction
= lvht
->pt
;
796 item
.mask
= LVIF_PARAM
;
797 item
.iItem
= lvht
->iItem
;
799 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
800 notify_listview(infoPtr
, code
, &nmlv
);
801 return IsWindow(hwnd
);
804 static BOOL
notify_deleteitem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
808 HWND hwnd
= infoPtr
->hwndSelf
;
810 ZeroMemory(&nmlv
, sizeof (NMLISTVIEW
));
812 item
.mask
= LVIF_PARAM
;
815 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
816 notify_listview(infoPtr
, LVN_DELETEITEM
, &nmlv
);
817 return IsWindow(hwnd
);
820 static int get_ansi_notification(INT unicodeNotificationCode
)
822 switch (unicodeNotificationCode
)
824 case LVN_BEGINLABELEDITW
: return LVN_BEGINLABELEDITA
;
825 case LVN_ENDLABELEDITW
: return LVN_ENDLABELEDITA
;
826 case LVN_GETDISPINFOW
: return LVN_GETDISPINFOA
;
827 case LVN_SETDISPINFOW
: return LVN_SETDISPINFOA
;
828 case LVN_ODFINDITEMW
: return LVN_ODFINDITEMA
;
829 case LVN_GETINFOTIPW
: return LVN_GETINFOTIPA
;
831 ERR("unknown notification %x\n", unicodeNotificationCode
);
837 Send notification. depends on dispinfoW having same
838 structure as dispinfoA.
839 infoPtr : listview struct
840 notificationCode : *Unicode* notification code
841 pdi : dispinfo structure (can be unicode or ansi)
842 isW : TRUE if dispinfo is Unicode
844 static BOOL
notify_dispinfoT(LISTVIEW_INFO
*infoPtr
, INT notificationCode
, LPNMLVDISPINFOW pdi
, BOOL isW
)
846 BOOL bResult
= FALSE
;
847 BOOL convertToAnsi
= FALSE
, convertToUnicode
= FALSE
;
848 INT cchTempBufMax
= 0, savCchTextMax
= 0, realNotifCode
;
849 LPWSTR pszTempBuf
= NULL
, savPszText
= NULL
;
851 if ((pdi
->item
.mask
& LVIF_TEXT
) && is_textT(pdi
->item
.pszText
, isW
))
853 convertToAnsi
= (isW
&& infoPtr
->notifyFormat
== NFR_ANSI
);
854 convertToUnicode
= (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
);
857 if (convertToAnsi
|| convertToUnicode
)
859 if (notificationCode
!= LVN_GETDISPINFOW
)
861 cchTempBufMax
= convertToUnicode
?
862 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
863 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
867 cchTempBufMax
= pdi
->item
.cchTextMax
;
868 *pdi
->item
.pszText
= 0; /* make sure we don't process garbage */
871 pszTempBuf
= Alloc( (convertToUnicode
? sizeof(WCHAR
) : sizeof(CHAR
)) * cchTempBufMax
);
872 if (!pszTempBuf
) return FALSE
;
874 if (convertToUnicode
)
875 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
876 pszTempBuf
, cchTempBufMax
);
878 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) pszTempBuf
,
879 cchTempBufMax
, NULL
, NULL
);
881 savCchTextMax
= pdi
->item
.cchTextMax
;
882 savPszText
= pdi
->item
.pszText
;
883 pdi
->item
.pszText
= pszTempBuf
;
884 pdi
->item
.cchTextMax
= cchTempBufMax
;
887 if (infoPtr
->notifyFormat
== NFR_ANSI
)
888 realNotifCode
= get_ansi_notification(notificationCode
);
890 realNotifCode
= notificationCode
;
891 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi
->item
, infoPtr
->notifyFormat
!= NFR_ANSI
));
892 bResult
= notify_hdr(infoPtr
, realNotifCode
, &pdi
->hdr
);
894 if (convertToUnicode
|| convertToAnsi
)
896 if (convertToUnicode
) /* note : pointer can be changed by app ! */
897 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) savPszText
,
898 savCchTextMax
, NULL
, NULL
);
900 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
901 savPszText
, savCchTextMax
);
902 pdi
->item
.pszText
= savPszText
; /* restores our buffer */
903 pdi
->item
.cchTextMax
= savCchTextMax
;
909 static void customdraw_fill(NMLVCUSTOMDRAW
*lpnmlvcd
, LISTVIEW_INFO
*infoPtr
, HDC hdc
,
910 const RECT
*rcBounds
, const LVITEMW
*lplvItem
)
912 ZeroMemory(lpnmlvcd
, sizeof(NMLVCUSTOMDRAW
));
913 lpnmlvcd
->nmcd
.hdc
= hdc
;
914 lpnmlvcd
->nmcd
.rc
= *rcBounds
;
915 lpnmlvcd
->clrTextBk
= infoPtr
->clrTextBk
;
916 lpnmlvcd
->clrText
= infoPtr
->clrText
;
917 if (!lplvItem
) return;
918 lpnmlvcd
->nmcd
.dwItemSpec
= lplvItem
->iItem
+ 1;
919 lpnmlvcd
->iSubItem
= lplvItem
->iSubItem
;
920 if (lplvItem
->state
& LVIS_SELECTED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_SELECTED
;
921 if (lplvItem
->state
& LVIS_FOCUSED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_FOCUS
;
922 if (lplvItem
->iItem
== infoPtr
->nHotItem
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_HOT
;
923 lpnmlvcd
->nmcd
.lItemlParam
= lplvItem
->lParam
;
926 static inline DWORD
notify_customdraw (LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, NMLVCUSTOMDRAW
*lpnmlvcd
)
928 BOOL isForItem
= (lpnmlvcd
->nmcd
.dwItemSpec
!= 0);
931 lpnmlvcd
->nmcd
.dwDrawStage
= dwDrawStage
;
932 if (isForItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_ITEM
;
933 if (lpnmlvcd
->iSubItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_SUBITEM
;
934 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
--;
935 result
= notify_hdr(infoPtr
, NM_CUSTOMDRAW
, &lpnmlvcd
->nmcd
.hdr
);
936 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
++;
940 static void prepaint_setup (LISTVIEW_INFO
*infoPtr
, HDC hdc
, NMLVCUSTOMDRAW
*lpnmlvcd
)
942 /* apprently, for selected items, we have to override the returned values */
943 if (lpnmlvcd
->nmcd
.uItemState
& CDIS_SELECTED
)
947 lpnmlvcd
->clrTextBk
= comctl32_color
.clrHighlight
;
948 lpnmlvcd
->clrText
= comctl32_color
.clrHighlightText
;
950 else if (infoPtr
->dwStyle
& LVS_SHOWSELALWAYS
)
952 lpnmlvcd
->clrTextBk
= comctl32_color
.clr3dFace
;
953 lpnmlvcd
->clrText
= comctl32_color
.clrBtnText
;
957 /* Set the text attributes */
958 if (lpnmlvcd
->clrTextBk
!= CLR_NONE
)
960 SetBkMode(hdc
, OPAQUE
);
961 if (lpnmlvcd
->clrTextBk
== CLR_DEFAULT
)
962 SetBkColor(hdc
, infoPtr
->clrTextBkDefault
);
964 SetBkColor(hdc
,lpnmlvcd
->clrTextBk
);
967 SetBkMode(hdc
, TRANSPARENT
);
968 SetTextColor(hdc
, lpnmlvcd
->clrText
);
971 static inline DWORD
notify_postpaint (LISTVIEW_INFO
*infoPtr
, NMLVCUSTOMDRAW
*lpnmlvcd
)
973 return notify_customdraw(infoPtr
, CDDS_POSTPAINT
, lpnmlvcd
);
976 /******** Item iterator functions **********************************/
978 static RANGES
ranges_create(int count
);
979 static void ranges_destroy(RANGES ranges
);
980 static BOOL
ranges_add(RANGES ranges
, RANGE range
);
981 static BOOL
ranges_del(RANGES ranges
, RANGE range
);
982 static void ranges_dump(RANGES ranges
);
984 static inline BOOL
ranges_additem(RANGES ranges
, INT nItem
)
986 RANGE range
= { nItem
, nItem
+ 1 };
988 return ranges_add(ranges
, range
);
991 static inline BOOL
ranges_delitem(RANGES ranges
, INT nItem
)
993 RANGE range
= { nItem
, nItem
+ 1 };
995 return ranges_del(ranges
, range
);
999 * ITERATOR DOCUMENTATION
1001 * The iterator functions allow for easy, and convenient iteration
1002 * over items of iterest in the list. Typically, you create a
1003 * iterator, use it, and destroy it, as such:
1006 * iterator_xxxitems(&i, ...);
1007 * while (iterator_{prev,next}(&i)
1009 * //code which uses i.nItem
1011 * iterator_destroy(&i);
1013 * where xxx is either: framed, or visible.
1014 * Note that it is important that the code destroys the iterator
1015 * after it's done with it, as the creation of the iterator may
1016 * allocate memory, which thus needs to be freed.
1018 * You can iterate both forwards, and backwards through the list,
1019 * by using iterator_next or iterator_prev respectively.
1021 * Lower numbered items are draw on top of higher number items in
1022 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1023 * items may overlap). So, to test items, you should use
1025 * which lists the items top to bottom (in Z-order).
1026 * For drawing items, you should use
1028 * which lists the items bottom to top (in Z-order).
1029 * If you keep iterating over the items after the end-of-items
1030 * marker (-1) is returned, the iterator will start from the
1031 * beginning. Typically, you don't need to test for -1,
1032 * because iterator_{next,prev} will return TRUE if more items
1033 * are to be iterated over, or FALSE otherwise.
1035 * Note: the iterator is defined to be bidirectional. That is,
1036 * any number of prev followed by any number of next, or
1037 * five versa, should leave the iterator at the same item:
1038 * prev * n, next * n = next * n, prev * n
1040 * The iterator has a notion of an out-of-order, special item,
1041 * which sits at the start of the list. This is used in
1042 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1043 * which needs to be first, as it may overlap other items.
1045 * The code is a bit messy because we have:
1046 * - a special item to deal with
1047 * - simple range, or composite range
1049 * If you find bugs, or want to add features, please make sure you
1050 * always check/modify *both* iterator_prev, and iterator_next.
1054 * This function iterates through the items in increasing order,
1055 * but prefixed by the special item, then -1. That is:
1056 * special, 1, 2, 3, ..., n, -1.
1057 * Each item is listed only once.
1059 static inline BOOL
iterator_next(ITERATOR
* i
)
1063 i
->nItem
= i
->nSpecial
;
1064 if (i
->nItem
!= -1) return TRUE
;
1066 if (i
->nItem
== i
->nSpecial
)
1068 if (i
->ranges
) i
->index
= 0;
1074 if (i
->nItem
== i
->nSpecial
) i
->nItem
++;
1075 if (i
->nItem
< i
->range
.upper
) return TRUE
;
1080 if (i
->index
< DPA_GetPtrCount(i
->ranges
->hdpa
))
1081 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, i
->index
++);
1084 else if (i
->nItem
>= i
->range
.upper
) goto end
;
1086 i
->nItem
= i
->range
.lower
;
1087 if (i
->nItem
>= 0) goto testitem
;
1094 * This function iterates through the items in decreasing order,
1095 * followed by the special item, then -1. That is:
1096 * n, n-1, ..., 3, 2, 1, special, -1.
1097 * Each item is listed only once.
1099 static inline BOOL
iterator_prev(ITERATOR
* i
)
1106 if (i
->ranges
) i
->index
= DPA_GetPtrCount(i
->ranges
->hdpa
);
1109 if (i
->nItem
== i
->nSpecial
)
1117 if (i
->nItem
== i
->nSpecial
) i
->nItem
--;
1118 if (i
->nItem
>= i
->range
.lower
) return TRUE
;
1124 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, --i
->index
);
1127 else if (!start
&& i
->nItem
< i
->range
.lower
) goto end
;
1129 i
->nItem
= i
->range
.upper
;
1130 if (i
->nItem
> 0) goto testitem
;
1132 return (i
->nItem
= i
->nSpecial
) != -1;
1135 static RANGE
iterator_range(ITERATOR
* i
)
1139 if (!i
->ranges
) return i
->range
;
1141 if (DPA_GetPtrCount(i
->ranges
->hdpa
) > 0)
1143 range
.lower
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, 0)).lower
;
1144 range
.upper
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, DPA_GetPtrCount(i
->ranges
->hdpa
) - 1)).upper
;
1146 else range
.lower
= range
.upper
= 0;
1152 * Releases resources associated with this ierator.
1154 static inline void iterator_destroy(ITERATOR
* i
)
1156 ranges_destroy(i
->ranges
);
1160 * Create an empty iterator.
1162 static inline BOOL
iterator_empty(ITERATOR
* i
)
1164 ZeroMemory(i
, sizeof(*i
));
1165 i
->nItem
= i
->nSpecial
= i
->range
.lower
= i
->range
.upper
= -1;
1170 * Create an iterator over a range.
1172 static inline BOOL
iterator_rangeitems(ITERATOR
* i
, RANGE range
)
1180 * Create an iterator over a bunch of ranges.
1181 * Please note that the iterator will take ownership of the ranges,
1182 * and will free them upon destruction.
1184 static inline BOOL
iterator_rangesitems(ITERATOR
* i
, RANGES ranges
)
1192 * Creates an iterator over the items which intersect lprc.
1194 static BOOL
iterator_frameditems(ITERATOR
* i
, LISTVIEW_INFO
* infoPtr
, const RECT
*lprc
)
1196 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1197 RECT frame
= *lprc
, rcItem
, rcTemp
;
1200 /* in case we fail, we want to return an empty iterator */
1201 if (!iterator_empty(i
)) return FALSE
;
1203 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1205 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc
));
1206 OffsetRect(&frame
, -Origin
.x
, -Origin
.y
);
1208 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
1212 if (uView
== LVS_ICON
&& infoPtr
->nFocusedItem
!= -1)
1214 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcItem
);
1215 if (IntersectRect(&rcTemp
, &rcItem
, lprc
))
1216 i
->nSpecial
= infoPtr
->nFocusedItem
;
1218 if (!(iterator_rangesitems(i
, ranges_create(50)))) return FALSE
;
1219 /* to do better here, we need to have PosX, and PosY sorted */
1220 TRACE("building icon ranges:\n");
1221 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
1223 rcItem
.left
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1224 rcItem
.top
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1225 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1226 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1227 if (IntersectRect(&rcTemp
, &rcItem
, &frame
))
1228 ranges_additem(i
->ranges
, nItem
);
1232 else if (uView
== LVS_REPORT
)
1236 if (frame
.left
>= infoPtr
->nItemWidth
) return TRUE
;
1237 if (frame
.top
>= infoPtr
->nItemHeight
* infoPtr
->nItemCount
) return TRUE
;
1239 range
.lower
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1240 range
.upper
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, infoPtr
->nItemCount
- 1) + 1;
1241 if (range
.upper
<= range
.lower
) return TRUE
;
1242 if (!iterator_rangeitems(i
, range
)) return FALSE
;
1243 TRACE(" report=%s\n", debugrange(&i
->range
));
1247 INT nPerCol
= max((infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
, 1);
1248 INT nFirstRow
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1249 INT nLastRow
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, nPerCol
- 1);
1250 INT nFirstCol
= max(frame
.left
/ infoPtr
->nItemWidth
, 0);
1251 INT nLastCol
= min((frame
.right
- 1) / infoPtr
->nItemWidth
, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1252 INT lower
= nFirstCol
* nPerCol
+ nFirstRow
;
1256 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1257 nPerCol
, nFirstRow
, nLastRow
, nFirstCol
, nLastCol
, lower
);
1259 if (nLastCol
< nFirstCol
|| nLastRow
< nFirstRow
) return TRUE
;
1261 if (!(iterator_rangesitems(i
, ranges_create(nLastCol
- nFirstCol
+ 1)))) return FALSE
;
1262 TRACE("building list ranges:\n");
1263 for (nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++)
1265 item_range
.lower
= nCol
* nPerCol
+ nFirstRow
;
1266 if(item_range
.lower
>= infoPtr
->nItemCount
) break;
1267 item_range
.upper
= min(nCol
* nPerCol
+ nLastRow
+ 1, infoPtr
->nItemCount
);
1268 TRACE(" list=%s\n", debugrange(&item_range
));
1269 ranges_add(i
->ranges
, item_range
);
1277 * Creates an iterator over the items which intersect the visible region of hdc.
1279 static BOOL
iterator_visibleitems(ITERATOR
*i
, LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1281 POINT Origin
, Position
;
1282 RECT rcItem
, rcClip
;
1285 rgntype
= GetClipBox(hdc
, &rcClip
);
1286 if (rgntype
== NULLREGION
) return iterator_empty(i
);
1287 if (!iterator_frameditems(i
, infoPtr
, &rcClip
)) return FALSE
;
1288 if (rgntype
== SIMPLEREGION
) return TRUE
;
1290 /* first deal with the special item */
1291 if (i
->nSpecial
!= -1)
1293 LISTVIEW_GetItemBox(infoPtr
, i
->nSpecial
, &rcItem
);
1294 if (!RectVisible(hdc
, &rcItem
)) i
->nSpecial
= -1;
1297 /* if we can't deal with the region, we'll just go with the simple range */
1298 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1299 TRACE("building visible range:\n");
1300 if (!i
->ranges
&& i
->range
.lower
< i
->range
.upper
)
1302 if (!(i
->ranges
= ranges_create(50))) return TRUE
;
1303 if (!ranges_add(i
->ranges
, i
->range
))
1305 ranges_destroy(i
->ranges
);
1311 /* now delete the invisible items from the list */
1312 while(iterator_next(i
))
1314 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
1315 rcItem
.left
= Position
.x
+ Origin
.x
;
1316 rcItem
.top
= Position
.y
+ Origin
.y
;
1317 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1318 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1319 if (!RectVisible(hdc
, &rcItem
))
1320 ranges_delitem(i
->ranges
, i
->nItem
);
1322 /* the iterator should restart on the next iterator_next */
1328 /******** Misc helper functions ************************************/
1330 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
1331 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
1333 if (isW
) return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
1334 else return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
1337 static inline BOOL
is_autoarrange(LISTVIEW_INFO
*infoPtr
)
1339 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1341 return ((infoPtr
->dwStyle
& LVS_AUTOARRANGE
) || infoPtr
->bAutoarrange
) &&
1342 (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
);
1345 /******** Internal API functions ************************************/
1347 static inline COLUMN_INFO
* LISTVIEW_GetColumnInfo(LISTVIEW_INFO
*infoPtr
, INT nSubItem
)
1349 static COLUMN_INFO mainItem
;
1351 if (nSubItem
== 0 && DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0) return &mainItem
;
1352 assert (nSubItem
>= 0 && nSubItem
< DPA_GetPtrCount(infoPtr
->hdpaColumns
));
1353 return (COLUMN_INFO
*)DPA_GetPtr(infoPtr
->hdpaColumns
, nSubItem
);
1356 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO
*infoPtr
, INT nSubItem
, RECT
*lprc
)
1358 *lprc
= LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->rcHeader
;
1361 static inline BOOL
LISTVIEW_GetItemW(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
1363 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
1366 /* Listview invalidation functions: use _only_ these functions to invalidate */
1368 static inline BOOL
is_redrawing(LISTVIEW_INFO
*infoPtr
)
1370 return infoPtr
->bRedraw
;
1373 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO
*infoPtr
, const RECT
* rect
)
1375 if(!is_redrawing(infoPtr
)) return;
1376 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect
));
1377 InvalidateRect(infoPtr
->hwndSelf
, rect
, TRUE
);
1380 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1384 if(!is_redrawing(infoPtr
)) return;
1385 LISTVIEW_GetItemBox(infoPtr
, nItem
, &rcBox
);
1386 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1389 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
)
1391 POINT Origin
, Position
;
1394 if(!is_redrawing(infoPtr
)) return;
1395 assert ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
);
1396 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1397 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
1398 LISTVIEW_GetHeaderRect(infoPtr
, nSubItem
, &rcBox
);
1400 rcBox
.bottom
= infoPtr
->nItemHeight
;
1401 OffsetRect(&rcBox
, Origin
.x
+ Position
.x
, Origin
.y
+ Position
.y
);
1402 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1405 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO
*infoPtr
)
1407 LISTVIEW_InvalidateRect(infoPtr
, NULL
);
1410 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
1414 if(!is_redrawing(infoPtr
)) return;
1415 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
1416 rcCol
.top
= infoPtr
->rcList
.top
;
1417 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
1418 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
1423 * Retrieves the number of items that can fit vertically in the client area.
1426 * [I] infoPtr : valid pointer to the listview structure
1429 * Number of items per row.
1431 static inline INT
LISTVIEW_GetCountPerRow(LISTVIEW_INFO
*infoPtr
)
1433 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1435 return max(nListWidth
/infoPtr
->nItemWidth
, 1);
1440 * Retrieves the number of items that can fit horizontally in the client
1444 * [I] infoPtr : valid pointer to the listview structure
1447 * Number of items per column.
1449 static inline INT
LISTVIEW_GetCountPerColumn(LISTVIEW_INFO
*infoPtr
)
1451 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1453 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
1457 /*************************************************************************
1458 * LISTVIEW_ProcessLetterKeys
1460 * Processes keyboard messages generated by pressing the letter keys
1462 * What this does is perform a case insensitive search from the
1463 * current position with the following quirks:
1464 * - If two chars or more are pressed in quick succession we search
1465 * for the corresponding string (e.g. 'abc').
1466 * - If there is a delay we wipe away the current search string and
1467 * restart with just that char.
1468 * - If the user keeps pressing the same character, whether slowly or
1469 * fast, so that the search string is entirely composed of this
1470 * character ('aaaaa' for instance), then we search for first item
1471 * that starting with that character.
1472 * - If the user types the above character in quick succession, then
1473 * we must also search for the corresponding string ('aaaaa'), and
1474 * go to that string if there is a match.
1477 * [I] hwnd : handle to the window
1478 * [I] charCode : the character code, the actual character
1479 * [I] keyData : key data
1487 * - The current implementation has a list of characters it will
1488 * accept and it ignores averything else. In particular it will
1489 * ignore accentuated characters which seems to match what
1490 * Windows does. But I'm not sure it makes sense to follow
1492 * - We don't sound a beep when the search fails.
1496 * TREEVIEW_ProcessLetterKeys
1498 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
1503 WCHAR buffer
[MAX_PATH
];
1504 DWORD lastKeyPressTimestamp
= infoPtr
->lastKeyPressTimestamp
;
1506 /* simple parameter checking */
1507 if (!charCode
|| !keyData
) return 0;
1509 /* only allow the valid WM_CHARs through */
1510 if (!isalnum(charCode
) &&
1511 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
1512 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
1513 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
1514 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
1515 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
1516 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
1517 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
1518 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
1519 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
1522 /* if there's one item or less, there is no where to go */
1523 if (infoPtr
->nItemCount
<= 1) return 0;
1525 /* update the search parameters */
1526 infoPtr
->lastKeyPressTimestamp
= GetTickCount();
1527 if (infoPtr
->lastKeyPressTimestamp
- lastKeyPressTimestamp
< KEY_DELAY
) {
1528 if (infoPtr
->nSearchParamLength
< MAX_PATH
)
1529 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++]=charCode
;
1530 if (infoPtr
->charCode
!= charCode
)
1531 infoPtr
->charCode
= charCode
= 0;
1533 infoPtr
->charCode
=charCode
;
1534 infoPtr
->szSearchParam
[0]=charCode
;
1535 infoPtr
->nSearchParamLength
=1;
1536 /* Redundant with the 1 char string */
1540 /* and search from the current position */
1542 if (infoPtr
->nFocusedItem
>= 0) {
1543 endidx
=infoPtr
->nFocusedItem
;
1545 /* if looking for single character match,
1546 * then we must always move forward
1548 if (infoPtr
->nSearchParamLength
== 1)
1551 endidx
=infoPtr
->nItemCount
;
1555 if (idx
== infoPtr
->nItemCount
) {
1556 if (endidx
== infoPtr
->nItemCount
|| endidx
== 0)
1562 item
.mask
= LVIF_TEXT
;
1565 item
.pszText
= buffer
;
1566 item
.cchTextMax
= MAX_PATH
;
1567 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
1569 /* check for a match */
1570 if (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,infoPtr
->nSearchParamLength
) == 0) {
1573 } else if ( (charCode
!= 0) && (nItem
== -1) && (nItem
!= infoPtr
->nFocusedItem
) &&
1574 (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,1) == 0) ) {
1575 /* This would work but we must keep looking for a longer match */
1579 } while (idx
!= endidx
);
1582 LISTVIEW_KeySelection(infoPtr
, nItem
);
1587 /*************************************************************************
1588 * LISTVIEW_UpdateHeaderSize [Internal]
1590 * Function to resize the header control
1593 * [I] hwnd : handle to a window
1594 * [I] nNewScrollPos : scroll pos to set
1599 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
1604 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
1606 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
1607 point
[0].x
= winRect
.left
;
1608 point
[0].y
= winRect
.top
;
1609 point
[1].x
= winRect
.right
;
1610 point
[1].y
= winRect
.bottom
;
1612 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
1613 point
[0].x
= -nNewScrollPos
;
1614 point
[1].x
+= nNewScrollPos
;
1616 SetWindowPos(infoPtr
->hwndHeader
,0,
1617 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
1618 SWP_NOZORDER
| SWP_NOACTIVATE
);
1623 * Update the scrollbars. This functions should be called whenever
1624 * the content, size or view changes.
1627 * [I] infoPtr : valid pointer to the listview structure
1632 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO
*infoPtr
)
1634 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1635 SCROLLINFO horzInfo
, vertInfo
;
1637 if ((infoPtr
->dwStyle
& LVS_NOSCROLL
) || !is_redrawing(infoPtr
)) return;
1639 ZeroMemory(&horzInfo
, sizeof(SCROLLINFO
));
1640 horzInfo
.cbSize
= sizeof(SCROLLINFO
);
1641 horzInfo
.nPage
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1643 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1644 if (uView
== LVS_LIST
)
1646 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
1647 horzInfo
.nMax
= (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
;
1649 /* scroll by at least one column per page */
1650 if(horzInfo
.nPage
< infoPtr
->nItemWidth
)
1651 horzInfo
.nPage
= infoPtr
->nItemWidth
;
1653 horzInfo
.nPage
/= infoPtr
->nItemWidth
;
1655 else if (uView
== LVS_REPORT
)
1657 horzInfo
.nMax
= infoPtr
->nItemWidth
;
1659 else /* LVS_ICON, or LVS_SMALLICON */
1663 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) horzInfo
.nMax
= rcView
.right
- rcView
.left
;
1666 horzInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1667 horzInfo
.nMax
= max(horzInfo
.nMax
- 1, 0);
1668 SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
, TRUE
);
1669 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo
));
1671 /* Setting the horizontal scroll can change the listview size
1672 * (and potentially everything else) so we need to recompute
1673 * everything again for the vertical scroll
1676 ZeroMemory(&vertInfo
, sizeof(SCROLLINFO
));
1677 vertInfo
.cbSize
= sizeof(SCROLLINFO
);
1678 vertInfo
.nPage
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1680 if (uView
== LVS_REPORT
)
1682 vertInfo
.nMax
= infoPtr
->nItemCount
;
1684 /* scroll by at least one page */
1685 if(vertInfo
.nPage
< infoPtr
->nItemHeight
)
1686 vertInfo
.nPage
= infoPtr
->nItemHeight
;
1688 vertInfo
.nPage
/= infoPtr
->nItemHeight
;
1690 else if (uView
!= LVS_LIST
) /* LVS_ICON, or LVS_SMALLICON */
1694 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) vertInfo
.nMax
= rcView
.bottom
- rcView
.top
;
1697 vertInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1698 vertInfo
.nMax
= max(vertInfo
.nMax
- 1, 0);
1699 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &vertInfo
, TRUE
);
1700 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo
));
1702 /* Update the Header Control */
1703 if (uView
== LVS_REPORT
)
1705 horzInfo
.fMask
= SIF_POS
;
1706 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
);
1707 LISTVIEW_UpdateHeaderSize(infoPtr
, horzInfo
.nPos
);
1714 * Shows/hides the focus rectangle.
1717 * [I] infoPtr : valid pointer to the listview structure
1718 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1723 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO
*infoPtr
, BOOL fShow
)
1725 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1728 TRACE("fShow=%d, nItem=%d\n", fShow
, infoPtr
->nFocusedItem
);
1730 if (infoPtr
->nFocusedItem
< 0) return;
1732 /* we need some gymnastics in ICON mode to handle large items */
1733 if ( (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
1737 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcBox
);
1738 if ((rcBox
.bottom
- rcBox
.top
) > infoPtr
->nItemHeight
)
1740 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1745 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) return;
1747 /* for some reason, owner draw should work only in report mode */
1748 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
1753 item
.iItem
= infoPtr
->nFocusedItem
;
1755 item
.mask
= LVIF_PARAM
;
1756 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto done
;
1758 ZeroMemory(&dis
, sizeof(dis
));
1759 dis
.CtlType
= ODT_LISTVIEW
;
1760 dis
.CtlID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1761 dis
.itemID
= item
.iItem
;
1762 dis
.itemAction
= ODA_FOCUS
;
1763 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
1764 dis
.hwndItem
= infoPtr
->hwndSelf
;
1766 LISTVIEW_GetItemBox(infoPtr
, dis
.itemID
, &dis
.rcItem
);
1767 dis
.itemData
= item
.lParam
;
1769 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
1773 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
1776 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1780 * Invalidates all visible selected items.
1782 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO
*infoPtr
)
1786 iterator_frameditems(&i
, infoPtr
, &infoPtr
->rcList
);
1787 while(iterator_next(&i
))
1789 if (LISTVIEW_GetItemState(infoPtr
, i
.nItem
, LVIS_SELECTED
))
1790 LISTVIEW_InvalidateItem(infoPtr
, i
.nItem
);
1792 iterator_destroy(&i
);
1797 * DESCRIPTION: [INTERNAL]
1798 * Computes an item's (left,top) corner, relative to rcView.
1799 * That is, the position has NOT been made relative to the Origin.
1800 * This is deliberate, to avoid computing the Origin over, and
1801 * over again, when this function is call in a loop. Instead,
1802 * one ca factor the computation of the Origin before the loop,
1803 * and offset the value retured by this function, on every iteration.
1806 * [I] infoPtr : valid pointer to the listview structure
1807 * [I] nItem : item number
1808 * [O] lpptOrig : item top, left corner
1813 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
1815 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1817 assert(nItem
>= 0 && nItem
< infoPtr
->nItemCount
);
1819 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1821 lpptPosition
->x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1822 lpptPosition
->y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1824 else if (uView
== LVS_LIST
)
1826 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
1827 lpptPosition
->x
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
1828 lpptPosition
->y
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
1830 else /* LVS_REPORT */
1832 lpptPosition
->x
= 0;
1833 lpptPosition
->y
= nItem
* infoPtr
->nItemHeight
;
1838 * DESCRIPTION: [INTERNAL]
1839 * Compute the rectangles of an item. This is to localize all
1840 * the computations in one place. If you are not interested in some
1841 * of these values, simply pass in a NULL -- the fucntion is smart
1842 * enough to compute only what's necessary. The function computes
1843 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1844 * one, the BOX rectangle. This rectangle is very cheap to compute,
1845 * and is guaranteed to contain all the other rectangles. Computing
1846 * the ICON rect is also cheap, but all the others are potentaily
1847 * expensive. This gives an easy and effective optimization when
1848 * searching (like point inclusion, or rectangle intersection):
1849 * first test against the BOX, and if TRUE, test agains the desired
1851 * If the function does not have all the necessary information
1852 * to computed the requested rectangles, will crash with a
1853 * failed assertion. This is done so we catch all programming
1854 * errors, given that the function is called only from our code.
1856 * We have the following 'special' meanings for a few fields:
1857 * * If LVIS_FOCUSED is set, we assume the item has the focus
1858 * This is important in ICON mode, where it might get a larger
1859 * then usual rectange
1861 * Please note that subitem support works only in REPORT mode.
1864 * [I] infoPtr : valid pointer to the listview structure
1865 * [I] lpLVItem : item to compute the measures for
1866 * [O] lprcBox : ptr to Box rectangle
1867 * The internal LVIR_BOX rectangle
1868 * [0] lprcState : ptr to State icon rectangle
1869 * The internal LVIR_STATE rectangle
1870 * [O] lprcIcon : ptr to Icon rectangle
1871 * Same as LVM_GETITEMRECT with LVIR_ICON
1872 * [O] lprcLabel : ptr to Label rectangle
1873 * Same as LVM_GETITEMRECT with LVIR_LABEL
1878 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
,
1879 LPRECT lprcBox
, LPRECT lprcState
,
1880 LPRECT lprcIcon
, LPRECT lprcLabel
)
1882 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1883 BOOL doState
= FALSE
, doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
1884 RECT Box
, State
, Icon
, Label
;
1885 COLUMN_INFO
*lpColumnInfo
= NULL
;
1887 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem
, TRUE
));
1889 /* Be smart and try to figure out the minimum we have to do */
1890 if (lpLVItem
->iSubItem
) assert(uView
== LVS_REPORT
);
1891 if (uView
== LVS_ICON
&& (lprcBox
|| lprcLabel
))
1893 assert((lpLVItem
->mask
& LVIF_STATE
) && (lpLVItem
->stateMask
& LVIS_FOCUSED
));
1894 if (lpLVItem
->state
& LVIS_FOCUSED
) oversizedBox
= doLabel
= TRUE
;
1896 if (lprcLabel
) doLabel
= TRUE
;
1897 if (doLabel
|| lprcIcon
) doIcon
= TRUE
;
1898 if (doIcon
|| lprcState
) doState
= TRUE
;
1900 /************************************************************/
1901 /* compute the box rectangle (it should be cheap to do) */
1902 /************************************************************/
1903 if (lpLVItem
->iSubItem
|| uView
== LVS_REPORT
)
1904 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpLVItem
->iSubItem
);
1906 if (lpLVItem
->iSubItem
)
1908 Box
= lpColumnInfo
->rcHeader
;
1913 Box
.right
= infoPtr
->nItemWidth
;
1916 Box
.bottom
= infoPtr
->nItemHeight
;
1918 /************************************************************/
1919 /* compute STATEICON bounding box */
1920 /************************************************************/
1923 if (uView
== LVS_ICON
)
1925 State
.left
= Box
.left
- infoPtr
->iconStateSize
.cx
- 2;
1926 if (infoPtr
->himlNormal
)
1927 State
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
1928 State
.top
= Box
.top
+ infoPtr
->iconSize
.cy
- infoPtr
->iconStateSize
.cy
+ 4;
1932 /* we need the ident in report mode, if we don't have it, we fail */
1933 State
.left
= Box
.left
;
1934 if (uView
== LVS_REPORT
)
1936 if (lpLVItem
->iSubItem
== 0)
1938 State
.left
+= REPORT_MARGINX
;
1939 assert(lpLVItem
->mask
& LVIF_INDENT
);
1940 State
.left
+= infoPtr
->iconSize
.cx
* lpLVItem
->iIndent
;
1943 State
.top
= Box
.top
;
1945 State
.right
= State
.left
;
1946 State
.bottom
= State
.top
;
1947 if (infoPtr
->himlState
&& lpLVItem
->iSubItem
== 0)
1949 State
.right
+= infoPtr
->iconStateSize
.cx
;
1950 State
.bottom
+= infoPtr
->iconStateSize
.cy
;
1952 if (lprcState
) *lprcState
= State
;
1953 TRACE(" - state=%s\n", wine_dbgstr_rect(&State
));
1955 else State
.right
= 0;
1957 /************************************************************/
1958 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1959 /************************************************************/
1962 if (uView
== LVS_ICON
)
1964 Icon
.left
= Box
.left
;
1965 if (infoPtr
->himlNormal
)
1966 Icon
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
1967 Icon
.top
= Box
.top
+ ICON_TOP_PADDING
;
1968 Icon
.right
= Icon
.left
;
1969 Icon
.bottom
= Icon
.top
;
1970 if (infoPtr
->himlNormal
)
1972 Icon
.right
+= infoPtr
->iconSize
.cx
;
1973 Icon
.bottom
+= infoPtr
->iconSize
.cy
;
1976 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1978 Icon
.left
= State
.right
;
1980 Icon
.right
= Icon
.left
;
1981 if (infoPtr
->himlSmall
&&
1982 (!lpColumnInfo
|| lpLVItem
->iSubItem
== 0 || (lpColumnInfo
->fmt
& LVCFMT_IMAGE
) ||
1983 ((infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
) && lpLVItem
->iImage
!= I_IMAGECALLBACK
)))
1984 Icon
.right
+= infoPtr
->iconSize
.cx
;
1985 Icon
.bottom
= Icon
.top
+ infoPtr
->nItemHeight
;
1987 if(lprcIcon
) *lprcIcon
= Icon
;
1988 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon
));
1990 else Icon
.right
= 0;
1992 /************************************************************/
1993 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1994 /************************************************************/
1997 SIZE labelSize
= { 0, 0 };
1999 /* calculate how far to the right can the label strech */
2000 Label
.right
= Box
.right
;
2001 if (uView
== LVS_REPORT
)
2003 if (lpLVItem
->iSubItem
== 0) Label
= lpColumnInfo
->rcHeader
;
2006 if (lpLVItem
->iSubItem
|| ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && uView
== LVS_REPORT
))
2008 labelSize
.cx
= infoPtr
->nItemWidth
;
2009 labelSize
.cy
= infoPtr
->nItemHeight
;
2013 /* we need the text in non owner draw mode */
2014 assert(lpLVItem
->mask
& LVIF_TEXT
);
2015 if (is_textT(lpLVItem
->pszText
, TRUE
))
2017 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2018 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2019 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2023 /* compute rough rectangle where the label will go */
2024 SetRectEmpty(&rcText
);
2025 rcText
.right
= infoPtr
->nItemWidth
- TRAILING_LABEL_PADDING
;
2026 rcText
.bottom
= infoPtr
->nItemHeight
;
2027 if (uView
== LVS_ICON
)
2028 rcText
.bottom
-= ICON_TOP_PADDING
+ infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2030 /* now figure out the flags */
2031 if (uView
== LVS_ICON
)
2032 uFormat
= oversizedBox
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
;
2034 uFormat
= LV_SL_DT_FLAGS
;
2036 DrawTextW (hdc
, lpLVItem
->pszText
, -1, &rcText
, uFormat
| DT_CALCRECT
);
2038 labelSize
.cx
= min(rcText
.right
- rcText
.left
+ TRAILING_LABEL_PADDING
, infoPtr
->nItemWidth
);
2039 labelSize
.cy
= rcText
.bottom
- rcText
.top
;
2041 SelectObject(hdc
, hOldFont
);
2042 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2046 if (uView
== LVS_ICON
)
2048 Label
.left
= Box
.left
+ (infoPtr
->nItemWidth
- labelSize
.cx
) / 2;
2049 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
2050 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2051 Label
.right
= Label
.left
+ labelSize
.cx
;
2052 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2053 if (!oversizedBox
&& labelSize
.cy
> infoPtr
->ntmHeight
)
2055 labelSize
.cy
= min(Box
.bottom
- Label
.top
, labelSize
.cy
);
2056 labelSize
.cy
/= infoPtr
->ntmHeight
;
2057 labelSize
.cy
= max(labelSize
.cy
, 1);
2058 labelSize
.cy
*= infoPtr
->ntmHeight
;
2060 Label
.bottom
= Label
.top
+ labelSize
.cy
+ HEIGHT_PADDING
;
2062 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2064 Label
.left
= Icon
.right
;
2065 Label
.top
= Box
.top
;
2066 Label
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2067 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2070 if (lprcLabel
) *lprcLabel
= Label
;
2071 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label
));
2074 /* Fix the Box if necessary */
2077 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
2078 else *lprcBox
= Box
;
2080 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box
));
2084 * DESCRIPTION: [INTERNAL]
2087 * [I] infoPtr : valid pointer to the listview structure
2088 * [I] nItem : item number
2089 * [O] lprcBox : ptr to Box rectangle
2094 static void LISTVIEW_GetItemBox(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprcBox
)
2096 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2097 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2098 POINT Position
, Origin
;
2101 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
2102 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
2104 /* Be smart and try to figure out the minimum we have to do */
2106 if (uView
== LVS_ICON
&& infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
2107 lvItem
.mask
|= LVIF_TEXT
;
2108 lvItem
.iItem
= nItem
;
2109 lvItem
.iSubItem
= 0;
2110 lvItem
.pszText
= szDispText
;
2111 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2112 if (lvItem
.mask
) LISTVIEW_GetItemW(infoPtr
, &lvItem
);
2113 if (uView
== LVS_ICON
)
2115 lvItem
.mask
|= LVIF_STATE
;
2116 lvItem
.stateMask
= LVIS_FOCUSED
;
2117 lvItem
.state
= (lvItem
.mask
& LVIF_TEXT
? LVIS_FOCUSED
: 0);
2119 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprcBox
, 0, 0, 0);
2121 OffsetRect(lprcBox
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
2127 * Returns the current icon position, and advances it along the top.
2128 * The returned position is not offset by Origin.
2131 * [I] infoPtr : valid pointer to the listview structure
2132 * [O] lpPos : will get the current icon position
2137 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2139 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2141 *lpPos
= infoPtr
->currIconPos
;
2143 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2144 if (infoPtr
->currIconPos
.x
+ infoPtr
->nItemWidth
<= nListWidth
) return;
2146 infoPtr
->currIconPos
.x
= 0;
2147 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2153 * Returns the current icon position, and advances it down the left edge.
2154 * The returned position is not offset by Origin.
2157 * [I] infoPtr : valid pointer to the listview structure
2158 * [O] lpPos : will get the current icon position
2163 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2165 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2167 *lpPos
= infoPtr
->currIconPos
;
2169 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2170 if (infoPtr
->currIconPos
.y
+ infoPtr
->nItemHeight
<= nListHeight
) return;
2172 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2173 infoPtr
->currIconPos
.y
= 0;
2179 * Moves an icon to the specified position.
2180 * It takes care of invalidating the item, etc.
2183 * [I] infoPtr : valid pointer to the listview structure
2184 * [I] nItem : the item to move
2185 * [I] lpPos : the new icon position
2186 * [I] isNew : flags the item as being new
2192 static BOOL
LISTVIEW_MoveIconTo(LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*lppt
, BOOL isNew
)
2198 old
.x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2199 old
.y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2201 if (lppt
->x
== old
.x
&& lppt
->y
== old
.y
) return TRUE
;
2202 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2205 /* Allocating a POINTER for every item is too resource intensive,
2206 * so we'll keep the (x,y) in different arrays */
2207 if (!DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)(LONG_PTR
)lppt
->x
)) return FALSE
;
2208 if (!DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)(LONG_PTR
)lppt
->y
)) return FALSE
;
2210 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2217 * Arranges listview items in icon display mode.
2220 * [I] infoPtr : valid pointer to the listview structure
2221 * [I] nAlignCode : alignment code
2227 static BOOL
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
2229 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2230 void (*next_pos
)(LISTVIEW_INFO
*, LPPOINT
);
2234 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
) return FALSE
;
2236 TRACE("nAlignCode=%d\n", nAlignCode
);
2238 if (nAlignCode
== LVA_DEFAULT
)
2240 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
) nAlignCode
= LVA_ALIGNLEFT
;
2241 else nAlignCode
= LVA_ALIGNTOP
;
2246 case LVA_ALIGNLEFT
: next_pos
= LISTVIEW_NextIconPosLeft
; break;
2247 case LVA_ALIGNTOP
: next_pos
= LISTVIEW_NextIconPosTop
; break;
2248 case LVA_SNAPTOGRID
: next_pos
= LISTVIEW_NextIconPosTop
; break; /* FIXME */
2249 default: return FALSE
;
2252 infoPtr
->bAutoarrange
= TRUE
;
2253 infoPtr
->currIconPos
.x
= infoPtr
->currIconPos
.y
= 0;
2254 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2256 next_pos(infoPtr
, &pos
);
2257 LISTVIEW_MoveIconTo(infoPtr
, i
, &pos
, FALSE
);
2265 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2268 * [I] infoPtr : valid pointer to the listview structure
2269 * [O] lprcView : bounding rectangle
2275 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2279 SetRectEmpty(lprcView
);
2281 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
2285 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2287 x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, i
);
2288 y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, i
);
2289 lprcView
->right
= max(lprcView
->right
, x
);
2290 lprcView
->bottom
= max(lprcView
->bottom
, y
);
2292 if (infoPtr
->nItemCount
> 0)
2294 lprcView
->right
+= infoPtr
->nItemWidth
;
2295 lprcView
->bottom
+= infoPtr
->nItemHeight
;
2300 y
= LISTVIEW_GetCountPerColumn(infoPtr
);
2301 x
= infoPtr
->nItemCount
/ y
;
2302 if (infoPtr
->nItemCount
% y
) x
++;
2303 lprcView
->right
= x
* infoPtr
->nItemWidth
;
2304 lprcView
->bottom
= y
* infoPtr
->nItemHeight
;
2308 lprcView
->right
= infoPtr
->nItemWidth
;
2309 lprcView
->bottom
= infoPtr
->nItemCount
* infoPtr
->nItemHeight
;
2316 * Retrieves the bounding rectangle of all the items.
2319 * [I] infoPtr : valid pointer to the listview structure
2320 * [O] lprcView : bounding rectangle
2326 static BOOL
LISTVIEW_GetViewRect(LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2330 TRACE("(lprcView=%p)\n", lprcView
);
2332 if (!lprcView
) return FALSE
;
2334 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
2335 LISTVIEW_GetAreaRect(infoPtr
, lprcView
);
2336 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
2338 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView
));
2345 * Retrieves the subitem pointer associated with the subitem index.
2348 * [I] hdpaSubItems : DPA handle for a specific item
2349 * [I] nSubItem : index of subitem
2352 * SUCCESS : subitem pointer
2355 static SUBITEM_INFO
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
, INT nSubItem
)
2357 SUBITEM_INFO
*lpSubItem
;
2360 /* we should binary search here if need be */
2361 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
2363 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
2364 if (lpSubItem
->iSubItem
== nSubItem
)
2374 * Caclulates the desired item width.
2377 * [I] infoPtr : valid pointer to the listview structure
2380 * The desired item width.
2382 static INT
LISTVIEW_CalculateItemWidth(LISTVIEW_INFO
*infoPtr
)
2384 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2387 TRACE("uView=%d\n", uView
);
2389 if (uView
== LVS_ICON
)
2390 nItemWidth
= infoPtr
->iconSpacing
.cx
;
2391 else if (uView
== LVS_REPORT
)
2395 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2397 LISTVIEW_GetHeaderRect(infoPtr
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, &rcHeader
);
2398 nItemWidth
= rcHeader
.right
;
2401 else /* LVS_SMALLICON, or LVS_LIST */
2405 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2406 nItemWidth
= max(LISTVIEW_GetLabelWidth(infoPtr
, i
), nItemWidth
);
2408 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
2409 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
2411 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
+ WIDTH_PADDING
);
2414 return max(nItemWidth
, 1);
2419 * Caclulates the desired item height.
2422 * [I] infoPtr : valid pointer to the listview structure
2425 * The desired item height.
2427 static INT
LISTVIEW_CalculateItemHeight(LISTVIEW_INFO
*infoPtr
)
2429 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2432 TRACE("uView=%d\n", uView
);
2434 if (uView
== LVS_ICON
)
2435 nItemHeight
= infoPtr
->iconSpacing
.cy
;
2438 nItemHeight
= infoPtr
->ntmHeight
;
2439 if (infoPtr
->himlState
)
2440 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
2441 if (infoPtr
->himlSmall
)
2442 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
2443 if (infoPtr
->himlState
|| infoPtr
->himlSmall
)
2444 nItemHeight
+= HEIGHT_PADDING
;
2445 if (infoPtr
->nMeasureItemHeight
> 0)
2446 nItemHeight
= infoPtr
->nMeasureItemHeight
;
2449 return max(nItemHeight
, 1);
2454 * Updates the width, and height of an item.
2457 * [I] infoPtr : valid pointer to the listview structure
2462 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO
*infoPtr
)
2464 infoPtr
->nItemWidth
= LISTVIEW_CalculateItemWidth(infoPtr
);
2465 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
2471 * Retrieves and saves important text metrics info for the current
2475 * [I] infoPtr : valid pointer to the listview structure
2478 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
2480 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2481 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2482 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2486 if (GetTextMetricsW(hdc
, &tm
))
2488 infoPtr
->ntmHeight
= tm
.tmHeight
;
2489 infoPtr
->ntmMaxCharWidth
= tm
.tmMaxCharWidth
;
2492 if (GetTextExtentPoint32A(hdc
, "...", 3, &sz
))
2493 infoPtr
->nEllipsisWidth
= sz
.cx
;
2495 SelectObject(hdc
, hOldFont
);
2496 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2498 TRACE("tmHeight=%d\n", infoPtr
->ntmHeight
);
2503 * A compare function for ranges
2506 * [I] range1 : pointer to range 1;
2507 * [I] range2 : pointer to range 2;
2511 * > 0 : if range 1 > range 2
2512 * < 0 : if range 2 > range 1
2513 * = 0 : if range intersects range 2
2515 static INT CALLBACK
ranges_cmp(LPVOID range1
, LPVOID range2
, LPARAM flags
)
2519 if (((RANGE
*)range1
)->upper
<= ((RANGE
*)range2
)->lower
)
2521 else if (((RANGE
*)range2
)->upper
<= ((RANGE
*)range1
)->lower
)
2526 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE
*)range1
), debugrange((RANGE
*)range2
), cmp
);
2532 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2534 #define ranges_check(ranges, desc) do { } while(0)
2537 static void ranges_assert(RANGES ranges
, LPCSTR desc
, const char *func
, int line
)
2542 TRACE("*** Checking %s:%d:%s ***\n", func
, line
, desc
);
2544 assert (DPA_GetPtrCount(ranges
->hdpa
) >= 0);
2545 ranges_dump(ranges
);
2546 prev
= (RANGE
*)DPA_GetPtr(ranges
->hdpa
, 0);
2547 if (DPA_GetPtrCount(ranges
->hdpa
) > 0)
2548 assert (prev
->lower
>= 0 && prev
->lower
< prev
->upper
);
2549 for (i
= 1; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2551 curr
= (RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
);
2552 assert (prev
->upper
<= curr
->lower
);
2553 assert (curr
->lower
< curr
->upper
);
2556 TRACE("--- Done checking---\n");
2559 static RANGES
ranges_create(int count
)
2561 RANGES ranges
= (RANGES
)Alloc(sizeof(struct tagRANGES
));
2562 if (!ranges
) return NULL
;
2563 ranges
->hdpa
= DPA_Create(count
);
2564 if (ranges
->hdpa
) return ranges
;
2569 static void ranges_clear(RANGES ranges
)
2573 for(i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2574 Free(DPA_GetPtr(ranges
->hdpa
, i
));
2575 DPA_DeleteAllPtrs(ranges
->hdpa
);
2579 static void ranges_destroy(RANGES ranges
)
2581 if (!ranges
) return;
2582 ranges_clear(ranges
);
2583 DPA_Destroy(ranges
->hdpa
);
2587 static RANGES
ranges_clone(RANGES ranges
)
2592 if (!(clone
= ranges_create(DPA_GetPtrCount(ranges
->hdpa
)))) goto fail
;
2594 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2596 RANGE
*newrng
= (RANGE
*)Alloc(sizeof(RANGE
));
2597 if (!newrng
) goto fail
;
2598 *newrng
= *((RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
));
2599 DPA_SetPtr(clone
->hdpa
, i
, newrng
);
2604 TRACE ("clone failed\n");
2605 ranges_destroy(clone
);
2609 static RANGES
ranges_diff(RANGES ranges
, RANGES sub
)
2613 for (i
= 0; i
< DPA_GetPtrCount(sub
->hdpa
); i
++)
2614 ranges_del(ranges
, *((RANGE
*)DPA_GetPtr(sub
->hdpa
, i
)));
2619 static void ranges_dump(RANGES ranges
)
2623 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2624 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges
->hdpa
, i
)));
2627 static inline BOOL
ranges_contain(RANGES ranges
, INT nItem
)
2629 RANGE srchrng
= { nItem
, nItem
+ 1 };
2631 TRACE("(nItem=%d)\n", nItem
);
2632 ranges_check(ranges
, "before contain");
2633 return DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
) != -1;
2636 static INT
ranges_itemcount(RANGES ranges
)
2640 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2642 RANGE
*sel
= DPA_GetPtr(ranges
->hdpa
, i
);
2643 count
+= sel
->upper
- sel
->lower
;
2649 static BOOL
ranges_shift(RANGES ranges
, INT nItem
, INT delta
, INT nUpper
)
2651 RANGE srchrng
= { nItem
, nItem
+ 1 }, *chkrng
;
2654 index
= DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2655 if (index
== -1) return TRUE
;
2657 for (; index
< DPA_GetPtrCount(ranges
->hdpa
); index
++)
2659 chkrng
= DPA_GetPtr(ranges
->hdpa
, index
);
2660 if (chkrng
->lower
>= nItem
)
2661 chkrng
->lower
= max(min(chkrng
->lower
+ delta
, nUpper
- 1), 0);
2662 if (chkrng
->upper
> nItem
)
2663 chkrng
->upper
= max(min(chkrng
->upper
+ delta
, nUpper
), 0);
2668 static BOOL
ranges_add(RANGES ranges
, RANGE range
)
2673 TRACE("(%s)\n", debugrange(&range
));
2674 ranges_check(ranges
, "before add");
2676 /* try find overlapping regions first */
2677 srchrgn
.lower
= range
.lower
- 1;
2678 srchrgn
.upper
= range
.upper
+ 1;
2679 index
= DPA_Search(ranges
->hdpa
, &srchrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
);
2685 TRACE("Adding new range\n");
2687 /* create the brand new range to insert */
2688 newrgn
= (RANGE
*)Alloc(sizeof(RANGE
));
2689 if(!newrgn
) goto fail
;
2692 /* figure out where to insert it */
2693 index
= DPA_Search(ranges
->hdpa
, newrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2694 TRACE("index=%d\n", index
);
2695 if (index
== -1) index
= 0;
2697 /* and get it over with */
2698 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2706 RANGE
*chkrgn
, *mrgrgn
;
2707 INT fromindex
, mergeindex
;
2709 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2710 TRACE("Merge with %s @%d\n", debugrange(chkrgn
), index
);
2712 chkrgn
->lower
= min(range
.lower
, chkrgn
->lower
);
2713 chkrgn
->upper
= max(range
.upper
, chkrgn
->upper
);
2715 TRACE("New range %s @%d\n", debugrange(chkrgn
), index
);
2717 /* merge now common anges */
2719 srchrgn
.lower
= chkrgn
->lower
- 1;
2720 srchrgn
.upper
= chkrgn
->upper
+ 1;
2724 mergeindex
= DPA_Search(ranges
->hdpa
, &srchrgn
, fromindex
, ranges_cmp
, 0, 0);
2725 if (mergeindex
== -1) break;
2726 if (mergeindex
== index
)
2728 fromindex
= index
+ 1;
2732 TRACE("Merge with index %i\n", mergeindex
);
2734 mrgrgn
= DPA_GetPtr(ranges
->hdpa
, mergeindex
);
2735 chkrgn
->lower
= min(chkrgn
->lower
, mrgrgn
->lower
);
2736 chkrgn
->upper
= max(chkrgn
->upper
, mrgrgn
->upper
);
2738 DPA_DeletePtr(ranges
->hdpa
, mergeindex
);
2739 if (mergeindex
< index
) index
--;
2743 ranges_check(ranges
, "after add");
2747 ranges_check(ranges
, "failed add");
2751 static BOOL
ranges_del(RANGES ranges
, RANGE range
)
2756 TRACE("(%s)\n", debugrange(&range
));
2757 ranges_check(ranges
, "before del");
2759 /* we don't use DPAS_SORTED here, since we need *
2760 * to find the first overlapping range */
2761 index
= DPA_Search(ranges
->hdpa
, &range
, 0, ranges_cmp
, 0, 0);
2764 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2766 TRACE("Matches range %s @%d\n", debugrange(chkrgn
), index
);
2768 /* case 1: Same range */
2769 if ( (chkrgn
->upper
== range
.upper
) &&
2770 (chkrgn
->lower
== range
.lower
) )
2772 DPA_DeletePtr(ranges
->hdpa
, index
);
2775 /* case 2: engulf */
2776 else if ( (chkrgn
->upper
<= range
.upper
) &&
2777 (chkrgn
->lower
>= range
.lower
) )
2779 DPA_DeletePtr(ranges
->hdpa
, index
);
2781 /* case 3: overlap upper */
2782 else if ( (chkrgn
->upper
<= range
.upper
) &&
2783 (chkrgn
->lower
< range
.lower
) )
2785 chkrgn
->upper
= range
.lower
;
2787 /* case 4: overlap lower */
2788 else if ( (chkrgn
->upper
> range
.upper
) &&
2789 (chkrgn
->lower
>= range
.lower
) )
2791 chkrgn
->lower
= range
.upper
;
2794 /* case 5: fully internal */
2797 RANGE tmprgn
= *chkrgn
, *newrgn
;
2799 if (!(newrgn
= (RANGE
*)Alloc(sizeof(RANGE
)))) goto fail
;
2800 newrgn
->lower
= chkrgn
->lower
;
2801 newrgn
->upper
= range
.lower
;
2802 chkrgn
->lower
= range
.upper
;
2803 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2812 index
= DPA_Search(ranges
->hdpa
, &range
, index
, ranges_cmp
, 0, 0);
2815 ranges_check(ranges
, "after del");
2819 ranges_check(ranges
, "failed del");
2825 * Removes all selection ranges
2828 * [I] infoPtr : valid pointer to the listview structure
2829 * [I] toSkip : item range to skip removing the selection
2835 static BOOL
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO
*infoPtr
, RANGES toSkip
)
2844 lvItem
.stateMask
= LVIS_SELECTED
;
2846 /* need to clone the DPA because callbacks can change it */
2847 if (!(clone
= ranges_clone(infoPtr
->selectionRanges
))) return FALSE
;
2848 iterator_rangesitems(&i
, ranges_diff(clone
, toSkip
));
2849 while(iterator_next(&i
))
2850 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &lvItem
);
2851 /* note that the iterator destructor will free the cloned range */
2852 iterator_destroy(&i
);
2857 static inline BOOL
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2861 if (!(toSkip
= ranges_create(1))) return FALSE
;
2862 if (nItem
!= -1) ranges_additem(toSkip
, nItem
);
2863 LISTVIEW_DeselectAllSkipItems(infoPtr
, toSkip
);
2864 ranges_destroy(toSkip
);
2868 static inline BOOL
LISTVIEW_DeselectAll(LISTVIEW_INFO
*infoPtr
)
2870 return LISTVIEW_DeselectAllSkipItem(infoPtr
, -1);
2875 * Retrieves the number of items that are marked as selected.
2878 * [I] infoPtr : valid pointer to the listview structure
2881 * Number of items selected.
2883 static INT
LISTVIEW_GetSelectedCount(LISTVIEW_INFO
*infoPtr
)
2885 INT nSelectedCount
= 0;
2887 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
2890 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2892 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
2897 nSelectedCount
= ranges_itemcount(infoPtr
->selectionRanges
);
2899 TRACE("nSelectedCount=%d\n", nSelectedCount
);
2900 return nSelectedCount
;
2905 * Manages the item focus.
2908 * [I] infoPtr : valid pointer to the listview structure
2909 * [I] nItem : item index
2912 * TRUE : focused item changed
2913 * FALSE : focused item has NOT changed
2915 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2917 INT oldFocus
= infoPtr
->nFocusedItem
;
2920 if (nItem
== infoPtr
->nFocusedItem
) return FALSE
;
2922 lvItem
.state
= nItem
== -1 ? 0 : LVIS_FOCUSED
;
2923 lvItem
.stateMask
= LVIS_FOCUSED
;
2924 LISTVIEW_SetItemState(infoPtr
, nItem
== -1 ? infoPtr
->nFocusedItem
: nItem
, &lvItem
);
2926 return oldFocus
!= infoPtr
->nFocusedItem
;
2929 /* Helper function for LISTVIEW_ShiftIndices *only* */
2930 static INT
shift_item(LISTVIEW_INFO
*infoPtr
, INT nShiftItem
, INT nItem
, INT direction
)
2932 if (nShiftItem
< nItem
) return nShiftItem
;
2934 if (nShiftItem
> nItem
) return nShiftItem
+ direction
;
2936 if (direction
> 0) return nShiftItem
+ direction
;
2938 return min(nShiftItem
, infoPtr
->nItemCount
- 1);
2943 * Updates the various indices after an item has been inserted or deleted.
2946 * [I] infoPtr : valid pointer to the listview structure
2947 * [I] nItem : item index
2948 * [I] direction : Direction of shift, +1 or -1.
2953 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
2958 /* temporarily disable change notification while shifting items */
2959 bOldChange
= infoPtr
->bDoChangeNotify
;
2960 infoPtr
->bDoChangeNotify
= FALSE
;
2962 TRACE("Shifting %iu, %i steps\n", nItem
, direction
);
2964 ranges_shift(infoPtr
->selectionRanges
, nItem
, direction
, infoPtr
->nItemCount
);
2966 assert(abs(direction
) == 1);
2968 infoPtr
->nSelectionMark
= shift_item(infoPtr
, infoPtr
->nSelectionMark
, nItem
, direction
);
2970 nNewFocus
= shift_item(infoPtr
, infoPtr
->nFocusedItem
, nItem
, direction
);
2971 if (nNewFocus
!= infoPtr
->nFocusedItem
)
2972 LISTVIEW_SetItemFocus(infoPtr
, nNewFocus
);
2974 /* But we are not supposed to modify nHotItem! */
2976 infoPtr
->bDoChangeNotify
= bOldChange
;
2982 * Adds a block of selections.
2985 * [I] infoPtr : valid pointer to the listview structure
2986 * [I] nItem : item index
2989 * Whether the window is still valid.
2991 static BOOL
LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2993 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
2994 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
2995 HWND hwndSelf
= infoPtr
->hwndSelf
;
2996 NMLVODSTATECHANGE nmlv
;
3001 /* Temporarily disable change notification
3002 * If the control is LVS_OWNERDATA, we need to send
3003 * only one LVN_ODSTATECHANGED notification.
3004 * See MSDN documentation for LVN_ITEMCHANGED.
3006 bOldChange
= infoPtr
->bDoChangeNotify
;
3007 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3009 if (nFirst
== -1) nFirst
= nItem
;
3011 item
.state
= LVIS_SELECTED
;
3012 item
.stateMask
= LVIS_SELECTED
;
3014 for (i
= nFirst
; i
<= nLast
; i
++)
3015 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
3017 ZeroMemory(&nmlv
, sizeof(nmlv
));
3018 nmlv
.iFrom
= nFirst
;
3021 nmlv
.uOldState
= item
.state
;
3023 notify_hdr(infoPtr
, LVN_ODSTATECHANGED
, (LPNMHDR
)&nmlv
);
3024 if (!IsWindow(hwndSelf
))
3026 infoPtr
->bDoChangeNotify
= bOldChange
;
3033 * Sets a single group selection.
3036 * [I] infoPtr : valid pointer to the listview structure
3037 * [I] nItem : item index
3042 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3044 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3049 if (!(selection
= ranges_create(100))) return;
3051 item
.state
= LVIS_SELECTED
;
3052 item
.stateMask
= LVIS_SELECTED
;
3054 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
3056 if (infoPtr
->nSelectionMark
== -1)
3058 infoPtr
->nSelectionMark
= nItem
;
3059 ranges_additem(selection
, nItem
);
3065 sel
.lower
= min(infoPtr
->nSelectionMark
, nItem
);
3066 sel
.upper
= max(infoPtr
->nSelectionMark
, nItem
) + 1;
3067 ranges_add(selection
, sel
);
3072 RECT rcItem
, rcSel
, rcSelMark
;
3075 rcItem
.left
= LVIR_BOUNDS
;
3076 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return;
3077 rcSelMark
.left
= LVIR_BOUNDS
;
3078 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) return;
3079 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
3080 iterator_frameditems(&i
, infoPtr
, &rcSel
);
3081 while(iterator_next(&i
))
3083 LISTVIEW_GetItemPosition(infoPtr
, i
.nItem
, &ptItem
);
3084 if (PtInRect(&rcSel
, ptItem
)) ranges_additem(selection
, i
.nItem
);
3086 iterator_destroy(&i
);
3089 LISTVIEW_DeselectAllSkipItems(infoPtr
, selection
);
3090 iterator_rangesitems(&i
, selection
);
3091 while(iterator_next(&i
))
3092 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &item
);
3093 /* this will also destroy the selection */
3094 iterator_destroy(&i
);
3096 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3101 * Sets a single selection.
3104 * [I] infoPtr : valid pointer to the listview structure
3105 * [I] nItem : item index
3110 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3114 TRACE("nItem=%d\n", nItem
);
3116 LISTVIEW_DeselectAllSkipItem(infoPtr
, nItem
);
3118 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3119 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3120 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3122 infoPtr
->nSelectionMark
= nItem
;
3127 * Set selection(s) with keyboard.
3130 * [I] infoPtr : valid pointer to the listview structure
3131 * [I] nItem : item index
3134 * SUCCESS : TRUE (needs to be repainted)
3135 * FAILURE : FALSE (nothing has changed)
3137 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3139 /* FIXME: pass in the state */
3140 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
3141 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
3142 BOOL bResult
= FALSE
;
3144 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem
, wShift
, wCtrl
);
3145 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3147 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
3150 LISTVIEW_SetSelection(infoPtr
, nItem
);
3157 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3162 lvItem
.state
= ~LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3163 lvItem
.stateMask
= LVIS_SELECTED
;
3164 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3166 if (lvItem
.state
& LVIS_SELECTED
)
3167 infoPtr
->nSelectionMark
= nItem
;
3169 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3174 LISTVIEW_SetSelection(infoPtr
, nItem
);
3177 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3180 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3184 static BOOL
LISTVIEW_GetItemAtPt(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, POINT pt
)
3186 LVHITTESTINFO lvHitTestInfo
;
3188 ZeroMemory(&lvHitTestInfo
, sizeof(lvHitTestInfo
));
3189 lvHitTestInfo
.pt
.x
= pt
.x
;
3190 lvHitTestInfo
.pt
.y
= pt
.y
;
3192 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
3194 lpLVItem
->mask
= LVIF_PARAM
;
3195 lpLVItem
->iItem
= lvHitTestInfo
.iItem
;
3196 lpLVItem
->iSubItem
= 0;
3198 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
3203 * Called when the mouse is being actively tracked and has hovered for a specified
3207 * [I] infoPtr : valid pointer to the listview structure
3208 * [I] fwKeys : key indicator
3209 * [I] x,y : mouse position
3212 * 0 if the message was processed, non-zero if there was an error
3215 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3216 * over the item for a certain period of time.
3219 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, WORD fwKyes
, INT x
, INT y
)
3221 if (infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)
3229 if (LISTVIEW_GetItemAtPt(infoPtr
, &item
, pt
))
3230 LISTVIEW_SetSelection(infoPtr
, item
.iItem
);
3238 * Called whenever WM_MOUSEMOVE is received.
3241 * [I] infoPtr : valid pointer to the listview structure
3242 * [I] fwKeys : key indicator
3243 * [I] x,y : mouse position
3246 * 0 if the message is processed, non-zero if there was an error
3248 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3250 TRACKMOUSEEVENT trackinfo
;
3252 if (!(fwKeys
& MK_LBUTTON
))
3253 infoPtr
->bLButtonDown
= FALSE
;
3255 if (infoPtr
->bLButtonDown
&& DragDetect(infoPtr
->hwndSelf
, infoPtr
->ptClickPos
))
3257 LVHITTESTINFO lvHitTestInfo
;
3260 lvHitTestInfo
.pt
= infoPtr
->ptClickPos
;
3261 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
3263 ZeroMemory(&nmlv
, sizeof(nmlv
));
3264 nmlv
.iItem
= lvHitTestInfo
.iItem
;
3265 nmlv
.ptAction
= infoPtr
->ptClickPos
;
3267 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
3272 infoPtr
->bLButtonDown
= FALSE
;
3274 /* see if we are supposed to be tracking mouse hovering */
3275 if(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) {
3276 /* fill in the trackinfo struct */
3277 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3278 trackinfo
.dwFlags
= TME_QUERY
;
3279 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
3280 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
3282 /* see if we are already tracking this hwnd */
3283 _TrackMouseEvent(&trackinfo
);
3285 if(!(trackinfo
.dwFlags
& TME_HOVER
)) {
3286 trackinfo
.dwFlags
= TME_HOVER
;
3288 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3289 _TrackMouseEvent(&trackinfo
);
3298 * Tests wheather the item is assignable to a list with style lStyle
3300 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
3302 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
3303 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
3304 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
3312 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3315 * [I] infoPtr : valid pointer to the listview structure
3316 * [I] lpLVItem : valid pointer to new item atttributes
3317 * [I] isNew : the item being set is being inserted
3318 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3319 * [O] bChanged : will be set to TRUE if the item really changed
3325 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
3327 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3335 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
3337 if (lpLVItem
->mask
== 0) return TRUE
;
3339 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
3341 /* a virtual listview we stores only selection and focus */
3342 if (lpLVItem
->mask
& ~LVIF_STATE
)
3348 HDPA hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3349 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
3353 /* we need to get the lParam and state of the item */
3354 item
.iItem
= lpLVItem
->iItem
;
3355 item
.iSubItem
= lpLVItem
->iSubItem
;
3356 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
3357 item
.stateMask
= ~0;
3360 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
3362 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
3363 /* determine what fields will change */
3364 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
))
3365 uChanged
|= LVIF_STATE
;
3367 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
3368 uChanged
|= LVIF_IMAGE
;
3370 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
3371 uChanged
|= LVIF_PARAM
;
3373 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
3374 uChanged
|= LVIF_INDENT
;
3376 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
3377 uChanged
|= LVIF_TEXT
;
3379 TRACE("uChanged=0x%x\n", uChanged
);
3380 if (!uChanged
) return TRUE
;
3383 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
3384 nmlv
.iItem
= lpLVItem
->iItem
;
3385 nmlv
.uNewState
= (item
.state
& ~lpLVItem
->stateMask
) | (lpLVItem
->state
& lpLVItem
->stateMask
);
3386 nmlv
.uOldState
= item
.state
;
3387 nmlv
.uChanged
= uChanged
;
3388 nmlv
.lParam
= item
.lParam
;
3390 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3391 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3393 if(lpItem
&& !isNew
&& infoPtr
->bDoChangeNotify
)
3395 HWND hwndSelf
= infoPtr
->hwndSelf
;
3397 if (notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
3399 if (!IsWindow(hwndSelf
))
3403 /* copy information */
3404 if (lpLVItem
->mask
& LVIF_TEXT
)
3405 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3407 if (lpLVItem
->mask
& LVIF_IMAGE
)
3408 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
3410 if (lpLVItem
->mask
& LVIF_PARAM
)
3411 lpItem
->lParam
= lpLVItem
->lParam
;
3413 if (lpLVItem
->mask
& LVIF_INDENT
)
3414 lpItem
->iIndent
= lpLVItem
->iIndent
;
3416 if (uChanged
& LVIF_STATE
)
3418 if (lpItem
&& (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& ~(LVIS_FOCUSED
| LVIS_SELECTED
)))
3420 lpItem
->state
&= ~lpLVItem
->stateMask
;
3421 lpItem
->state
|= (lpLVItem
->state
& lpLVItem
->stateMask
);
3423 if (lpLVItem
->state
& lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3425 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
3426 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3428 else if (lpLVItem
->stateMask
& LVIS_SELECTED
)
3429 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3431 /* if we are asked to change focus, and we manage it, do it */
3432 if (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
3434 if (lpLVItem
->state
& LVIS_FOCUSED
)
3436 LISTVIEW_SetItemFocus(infoPtr
, -1);
3437 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
3438 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, uView
== LVS_LIST
);
3440 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
3441 infoPtr
->nFocusedItem
= -1;
3445 /* if we're inserting the item, we're done */
3446 if (isNew
) return TRUE
;
3448 /* send LVN_ITEMCHANGED notification */
3449 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
3450 if (infoPtr
->bDoChangeNotify
) notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
3457 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3460 * [I] infoPtr : valid pointer to the listview structure
3461 * [I] lpLVItem : valid pointer to new subitem atttributes
3462 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3463 * [O] bChanged : will be set to TRUE if the item really changed
3469 static BOOL
set_sub_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
3472 SUBITEM_INFO
*lpSubItem
;
3474 /* we do not support subitems for virtual listviews */
3475 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
3477 /* set subitem only if column is present */
3478 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
3480 /* First do some sanity checks */
3481 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
)) return FALSE
;
3482 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
))) return TRUE
;
3484 /* get the subitem structure, and create it if not there */
3485 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3486 assert (hdpaSubItems
);
3488 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
3491 SUBITEM_INFO
*tmpSubItem
;
3494 lpSubItem
= (SUBITEM_INFO
*)Alloc(sizeof(SUBITEM_INFO
));
3495 if (!lpSubItem
) return FALSE
;
3496 /* we could binary search here, if need be...*/
3497 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
3499 tmpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
3500 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
3502 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
3507 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
3508 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
3512 if (lpLVItem
->mask
& LVIF_IMAGE
)
3513 if (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
)
3515 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
3519 if (lpLVItem
->mask
& LVIF_TEXT
)
3520 if (lpSubItem
->hdr
.pszText
!= lpLVItem
->pszText
)
3522 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3531 * Sets item attributes.
3534 * [I] infoPtr : valid pointer to the listview structure
3535 * [I] lpLVItem : new item atttributes
3536 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3542 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
3544 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3545 HWND hwndSelf
= infoPtr
->hwndSelf
;
3546 LPWSTR pszText
= NULL
;
3547 BOOL bResult
, bChanged
= FALSE
;
3549 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
3551 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
3554 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3555 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_textW(lpLVItem
->pszText
))
3557 pszText
= lpLVItem
->pszText
;
3558 ((LVITEMW
*)lpLVItem
)->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
3561 /* actually set the fields */
3562 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
3564 if (lpLVItem
->iSubItem
)
3565 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
3567 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
3568 if (!IsWindow(hwndSelf
))
3571 /* redraw item, if necessary */
3572 if (bChanged
&& !infoPtr
->bIsDrawing
)
3574 /* this little optimization eliminates some nasty flicker */
3575 if ( uView
== LVS_REPORT
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
3576 (!(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) || lpLVItem
->iSubItem
) )
3577 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
3579 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
3584 textfreeT(lpLVItem
->pszText
, isW
);
3585 ((LVITEMW
*)lpLVItem
)->pszText
= pszText
;
3593 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3596 * [I] infoPtr : valid pointer to the listview structure
3601 static INT
LISTVIEW_GetTopIndex(LISTVIEW_INFO
*infoPtr
)
3603 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3605 SCROLLINFO scrollInfo
;
3607 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3608 scrollInfo
.fMask
= SIF_POS
;
3610 if (uView
== LVS_LIST
)
3612 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
3613 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
3615 else if (uView
== LVS_REPORT
)
3617 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3618 nItem
= scrollInfo
.nPos
;
3622 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3623 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
3626 TRACE("nItem=%d\n", nItem
);
3634 * Erases the background of the given rectangle
3637 * [I] infoPtr : valid pointer to the listview structure
3638 * [I] hdc : device context handle
3639 * [I] lprcBox : clipping rectangle
3645 static inline BOOL
LISTVIEW_FillBkgnd(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
3647 if (!infoPtr
->hBkBrush
) return FALSE
;
3649 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, wine_dbgstr_rect(lprcBox
), infoPtr
->hBkBrush
);
3651 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
3659 * [I] infoPtr : valid pointer to the listview structure
3660 * [I] hdc : device context handle
3661 * [I] nItem : item index
3662 * [I] nSubItem : subitem index
3663 * [I] pos : item position in client coordinates
3664 * [I] cdmode : custom draw mode
3670 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, INT nSubItem
, POINT pos
, DWORD cdmode
)
3672 UINT uFormat
, uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3673 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
3674 static const WCHAR szCallback
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3675 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
3676 RECT
* lprcFocus
, rcSelect
, rcBox
, rcState
, rcIcon
, rcLabel
;
3677 NMLVCUSTOMDRAW nmlvcd
;
3682 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc
, nItem
, nSubItem
, wine_dbgstr_point(&pos
));
3684 /* get information needed for drawing the item */
3685 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
;
3686 if (nSubItem
== 0) lvItem
.mask
|= LVIF_STATE
| LVIF_PARAM
;
3687 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
3688 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
;
3689 lvItem
.iItem
= nItem
;
3690 lvItem
.iSubItem
= nSubItem
;
3693 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3694 lvItem
.pszText
= szDispText
;
3695 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
3696 if (nSubItem
> 0 && (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3697 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3698 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= (LPWSTR
)szCallback
;
3699 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3701 /* now check if we need to update the focus rectangle */
3702 lprcFocus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
3704 if (!lprcFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
3705 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcState
, &rcIcon
, &rcLabel
);
3706 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
3707 OffsetRect(&rcState
, pos
.x
, pos
.y
);
3708 OffsetRect(&rcIcon
, pos
.x
, pos
.y
);
3709 OffsetRect(&rcLabel
, pos
.x
, pos
.y
);
3710 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3711 wine_dbgstr_rect(&rcBox
), wine_dbgstr_rect(&rcState
),
3712 wine_dbgstr_rect(&rcIcon
), wine_dbgstr_rect(&rcLabel
));
3714 /* fill in the custom draw structure */
3715 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
3717 hOldFont
= GetCurrentObject(hdc
, OBJ_FONT
);
3718 if (nSubItem
> 0) cdmode
= infoPtr
->cditemmode
;
3719 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3720 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3721 if (nSubItem
== 0) infoPtr
->cditemmode
= cdsubitemmode
;
3722 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3723 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3724 if (nSubItem
== 0 && cdsubitemmode
== CDRF_NOTIFYITEMDRAW
)
3726 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
3727 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3729 if (nSubItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
3730 prepaint_setup(infoPtr
, hdc
, &nmlvcd
);
3732 /* in full row select, subitems, will just use main item's colors */
3733 if (nSubItem
&& uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3734 nmlvcd
.clrTextBk
= CLR_NONE
;
3737 if (infoPtr
->himlState
&& !IsRectEmpty(&rcState
))
3739 UINT uStateImage
= STATEIMAGEINDEX(lvItem
.state
);
3742 TRACE("uStateImage=%d\n", uStateImage
);
3743 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
, rcState
.left
, rcState
.top
, ILD_NORMAL
);
3748 himl
= (uView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
3749 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
))
3751 TRACE("iImage=%d\n", lvItem
.iImage
);
3752 ImageList_Draw(himl
, lvItem
.iImage
, hdc
, rcIcon
.left
, rcIcon
.top
,
3753 (lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
) ? ILD_SELECTED
: ILD_NORMAL
);
3756 /* Don't bother painting item being edited */
3757 if (infoPtr
->hwndEdit
&& nItem
== infoPtr
->nEditLabelItem
&& nSubItem
== 0) goto postpaint
;
3759 /* draw the selection background, if we're drawing the main item */
3763 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3764 rcSelect
.right
= rcBox
.right
;
3766 if (nmlvcd
.clrTextBk
!= CLR_NONE
)
3767 ExtTextOutW(hdc
, rcSelect
.left
, rcSelect
.top
, ETO_OPAQUE
, &rcSelect
, 0, 0, 0);
3768 if(lprcFocus
) *lprcFocus
= rcSelect
;
3771 /* figure out the text drawing flags */
3772 uFormat
= (uView
== LVS_ICON
? (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
3773 if (uView
== LVS_ICON
)
3774 uFormat
= (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
3777 switch (LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
3779 case LVCFMT_RIGHT
: uFormat
|= DT_RIGHT
; break;
3780 case LVCFMT_CENTER
: uFormat
|= DT_CENTER
; break;
3781 default: uFormat
|= DT_LEFT
;
3784 if (!(uFormat
& (DT_RIGHT
| DT_CENTER
)))
3786 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
3787 else rcLabel
.left
+= LABEL_HOR_PADDING
;
3789 else if (uFormat
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
3790 DrawTextW(hdc
, lvItem
.pszText
, -1, &rcLabel
, uFormat
);
3793 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
3794 notify_postpaint(infoPtr
, &nmlvcd
);
3795 if (cdsubitemmode
& CDRF_NEWFONT
)
3796 SelectObject(hdc
, hOldFont
);
3802 * Draws listview items when in owner draw mode.
3805 * [I] infoPtr : valid pointer to the listview structure
3806 * [I] hdc : device context handle
3811 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3813 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
3814 DWORD cditemmode
= CDRF_DODEFAULT
;
3815 NMLVCUSTOMDRAW nmlvcd
;
3816 POINT Origin
, Position
;
3822 ZeroMemory(&dis
, sizeof(dis
));
3824 /* Get scroll info once before loop */
3825 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3827 /* iterate through the invalidated rows */
3828 while(iterator_next(i
))
3830 item
.iItem
= i
->nItem
;
3832 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
3833 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
3834 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
3836 dis
.CtlType
= ODT_LISTVIEW
;
3838 dis
.itemID
= item
.iItem
;
3839 dis
.itemAction
= ODA_DRAWENTIRE
;
3841 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
3842 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
3843 dis
.hwndItem
= infoPtr
->hwndSelf
;
3845 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
3846 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
3847 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
3848 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
3849 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
3850 dis
.itemData
= item
.lParam
;
3852 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), wine_dbgstr_rect(&dis
.rcItem
));
3855 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3856 * structure for the rest. of the paint cycle
3858 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
3859 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3860 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3862 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
3864 prepaint_setup (infoPtr
, hdc
, &nmlvcd
);
3865 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
3868 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3869 notify_postpaint(infoPtr
, &nmlvcd
);
3875 * Draws listview items when in report display mode.
3878 * [I] infoPtr : valid pointer to the listview structure
3879 * [I] hdc : device context handle
3880 * [I] cdmode : custom draw mode
3885 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3888 RECT rcClip
, rcItem
;
3889 POINT Origin
, Position
;
3895 /* figure out what to draw */
3896 rgntype
= GetClipBox(hdc
, &rcClip
);
3897 if (rgntype
== NULLREGION
) return;
3899 /* Get scroll info once before loop */
3900 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3902 /* narrow down the columns we need to paint */
3903 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
3905 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
3906 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
3908 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
3910 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
3911 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
3913 iterator_rangeitems(&j
, colRange
);
3915 /* in full row select, we _have_ to draw the main item */
3916 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
3919 /* iterate through the invalidated rows */
3920 while(iterator_next(i
))
3922 /* iterate through the invalidated columns */
3923 while(iterator_next(&j
))
3925 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
3926 Position
.x
+= Origin
.x
;
3927 Position
.y
+= Origin
.y
;
3929 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
3931 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
3933 rcItem
.bottom
= infoPtr
->nItemHeight
;
3934 OffsetRect(&rcItem
, Position
.x
, Position
.y
);
3935 if (!RectVisible(hdc
, &rcItem
)) continue;
3938 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, j
.nItem
, Position
, cdmode
);
3941 iterator_destroy(&j
);
3946 * Draws listview items when in list display mode.
3949 * [I] infoPtr : valid pointer to the listview structure
3950 * [I] hdc : device context handle
3951 * [I] cdmode : custom draw mode
3956 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3958 POINT Origin
, Position
;
3960 /* Get scroll info once before loop */
3961 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3963 while(iterator_prev(i
))
3965 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
3966 Position
.x
+= Origin
.x
;
3967 Position
.y
+= Origin
.y
;
3969 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, 0, Position
, cdmode
);
3976 * Draws listview items.
3979 * [I] infoPtr : valid pointer to the listview structure
3980 * [I] hdc : device context handle
3985 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
3987 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3988 COLORREF oldTextColor
, oldClrTextBk
, oldClrText
;
3989 NMLVCUSTOMDRAW nmlvcd
;
3996 LISTVIEW_DUMP(infoPtr
);
3998 infoPtr
->bIsDrawing
= TRUE
;
4000 /* save dc values we're gonna trash while drawing */
4001 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
4002 oldBkMode
= GetBkMode(hdc
);
4003 infoPtr
->clrTextBkDefault
= GetBkColor(hdc
);
4004 oldTextColor
= GetTextColor(hdc
);
4006 oldClrTextBk
= infoPtr
->clrTextBk
;
4007 oldClrText
= infoPtr
->clrText
;
4009 infoPtr
->cditemmode
= CDRF_DODEFAULT
;
4011 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
4012 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
4013 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4014 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
4015 prepaint_setup(infoPtr
, hdc
, &nmlvcd
);
4017 /* Use these colors to draw the items */
4018 infoPtr
->clrTextBk
= nmlvcd
.clrTextBk
;
4019 infoPtr
->clrText
= nmlvcd
.clrText
;
4021 /* nothing to draw */
4022 if(infoPtr
->nItemCount
== 0) goto enddraw
;
4024 /* figure out what we need to draw */
4025 iterator_visibleitems(&i
, infoPtr
, hdc
);
4027 /* send cache hint notification */
4028 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4030 RANGE range
= iterator_range(&i
);
4033 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
4034 nmlv
.iFrom
= range
.lower
;
4035 nmlv
.iTo
= range
.upper
- 1;
4036 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
4039 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
4040 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
4043 if (uView
== LVS_REPORT
)
4044 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
4045 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4046 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
4048 /* if we have a focus rect, draw it */
4049 if (infoPtr
->bFocus
)
4050 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
4052 iterator_destroy(&i
);
4055 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
4056 notify_postpaint(infoPtr
, &nmlvcd
);
4058 infoPtr
->clrTextBk
= oldClrTextBk
;
4059 infoPtr
->clrText
= oldClrText
;
4061 SelectObject(hdc
, hOldFont
);
4062 SetBkMode(hdc
, oldBkMode
);
4063 SetBkColor(hdc
, infoPtr
->clrTextBkDefault
);
4064 SetTextColor(hdc
, oldTextColor
);
4065 infoPtr
->bIsDrawing
= FALSE
;
4071 * Calculates the approximate width and height of a given number of items.
4074 * [I] infoPtr : valid pointer to the listview structure
4075 * [I] nItemCount : number of items
4076 * [I] wWidth : width
4077 * [I] wHeight : height
4080 * Returns a DWORD. The width in the low word and the height in high word.
4082 static DWORD
LISTVIEW_ApproximateViewRect(LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
4083 WORD wWidth
, WORD wHeight
)
4085 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4086 INT nItemCountPerColumn
= 1;
4087 INT nColumnCount
= 0;
4088 DWORD dwViewRect
= 0;
4090 if (nItemCount
== -1)
4091 nItemCount
= infoPtr
->nItemCount
;
4093 if (uView
== LVS_LIST
)
4095 if (wHeight
== 0xFFFF)
4097 /* use current height */
4098 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4101 if (wHeight
< infoPtr
->nItemHeight
)
4102 wHeight
= infoPtr
->nItemHeight
;
4106 if (infoPtr
->nItemHeight
> 0)
4108 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
4109 if (nItemCountPerColumn
== 0)
4110 nItemCountPerColumn
= 1;
4112 if (nItemCount
% nItemCountPerColumn
!= 0)
4113 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
4115 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
4119 /* Microsoft padding magic */
4120 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
4121 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
4123 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4125 else if (uView
== LVS_REPORT
)
4129 if (infoPtr
->nItemCount
> 0)
4131 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
4132 wWidth
= rcBox
.right
- rcBox
.left
;
4133 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
4137 /* use current height and width */
4138 if (wHeight
== 0xffff)
4139 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4140 if (wWidth
== 0xffff)
4141 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
4144 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4146 else if (uView
== LVS_SMALLICON
)
4147 FIXME("uView == LVS_SMALLICON: not implemented\n");
4148 else if (uView
== LVS_ICON
)
4149 FIXME("uView == LVS_ICON: not implemented\n");
4157 * Create a drag image list for the specified item.
4160 * [I] infoPtr : valid pointer to the listview structure
4161 * [I] iItem : index of item
4162 * [O] lppt : Upperr-left corner of the image
4165 * Returns a handle to the image list if successful, NULL otherwise.
4167 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
4173 HBITMAP hbmp
, hOldbmp
;
4174 HIMAGELIST dragList
= 0;
4175 TRACE("iItem=%d Count=%d\n", iItem
, infoPtr
->nItemCount
);
4177 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
)
4180 rcItem
.left
= LVIR_BOUNDS
;
4181 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
4184 lppt
->x
= rcItem
.left
;
4185 lppt
->y
= rcItem
.top
;
4187 size
.cx
= rcItem
.right
- rcItem
.left
;
4188 size
.cy
= rcItem
.bottom
- rcItem
.top
;
4190 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
4191 hdc
= CreateCompatibleDC(hdcOrig
);
4192 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
4193 hOldbmp
= SelectObject(hdc
, hbmp
);
4195 rcItem
.left
= rcItem
.top
= 0;
4196 rcItem
.right
= size
.cx
;
4197 rcItem
.bottom
= size
.cy
;
4198 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
4201 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, 0, pos
, infoPtr
->cditemmode
))
4203 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
4204 SelectObject(hdc
, hOldbmp
);
4205 ImageList_Add(dragList
, hbmp
, 0);
4208 SelectObject(hdc
, hOldbmp
);
4212 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
4214 TRACE("ret=%p\n", dragList
);
4222 * Removes all listview items and subitems.
4225 * [I] infoPtr : valid pointer to the listview structure
4231 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
)
4234 HDPA hdpaSubItems
= NULL
;
4241 /* we do it directly, to avoid notifications */
4242 ranges_clear(infoPtr
->selectionRanges
);
4243 infoPtr
->nSelectionMark
= -1;
4244 infoPtr
->nFocusedItem
= -1;
4245 SetRectEmpty(&infoPtr
->rcFocus
);
4246 /* But we are supposed to leave nHotItem as is! */
4249 /* send LVN_DELETEALLITEMS notification */
4250 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4252 bSuppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
4254 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
4256 /* send LVN_DELETEITEM notification, if not suppressed */
4257 if (!bSuppress
) notify_deleteitem(infoPtr
, i
);
4258 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4260 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
4261 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
4263 hdrItem
= (ITEMHDR
*)DPA_GetPtr(hdpaSubItems
, j
);
4264 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4267 DPA_Destroy(hdpaSubItems
);
4268 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
4270 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
4271 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
4272 infoPtr
->nItemCount
--;
4275 LISTVIEW_UpdateScroll(infoPtr
);
4277 LISTVIEW_InvalidateList(infoPtr
);
4284 * Scrolls, and updates the columns, when a column is changing width.
4287 * [I] infoPtr : valid pointer to the listview structure
4288 * [I] nColumn : column to scroll
4289 * [I] dx : amount of scroll, in pixels
4294 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
4296 COLUMN_INFO
*lpColumnInfo
;
4301 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
4302 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
4303 rcCol
= lpColumnInfo
->rcHeader
;
4304 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
4305 rcCol
.left
= rcCol
.right
;
4307 /* ajust the other columns */
4308 for (nCol
= nColumn
; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
4310 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
4311 lpColumnInfo
->rcHeader
.left
+= dx
;
4312 lpColumnInfo
->rcHeader
.right
+= dx
;
4315 /* do not update screen if not in report mode */
4316 if (!is_redrawing(infoPtr
) || (infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return;
4318 /* if we have a focus, must first erase the focus rect */
4319 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
4321 /* Need to reset the item width when inserting a new column */
4322 infoPtr
->nItemWidth
+= dx
;
4324 LISTVIEW_UpdateScroll(infoPtr
);
4325 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
4327 /* scroll to cover the deleted column, and invalidate for redraw */
4328 rcOld
= infoPtr
->rcList
;
4329 rcOld
.left
= ptOrigin
.x
+ rcCol
.left
+ dx
;
4330 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4332 /* we can restore focus now */
4333 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
4338 * Removes a column from the listview control.
4341 * [I] infoPtr : valid pointer to the listview structure
4342 * [I] nColumn : column index
4348 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4352 TRACE("nColumn=%d\n", nColumn
);
4354 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0
4355 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4357 /* While the MSDN specifically says that column zero should not be deleted,
4358 what actually happens is that the column itself is deleted but no items or subitems
4362 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
4364 if (!Header_DeleteItem(infoPtr
->hwndHeader
, nColumn
))
4367 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
4368 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
4370 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
4372 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
4374 INT nItem
, nSubItem
, i
;
4376 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
4378 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
4381 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4383 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
4384 if (lpSubItem
->iSubItem
== nColumn
)
4387 lpDelItem
= lpSubItem
;
4389 else if (lpSubItem
->iSubItem
> nColumn
)
4391 lpSubItem
->iSubItem
--;
4395 /* if we found our subitem, zapp it */
4399 if (is_textW(lpDelItem
->hdr
.pszText
))
4400 Free(lpDelItem
->hdr
.pszText
);
4405 /* free dpa memory */
4406 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
4411 /* update the other column info */
4412 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
4413 LISTVIEW_InvalidateList(infoPtr
);
4415 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
4422 * Invalidates the listview after an item's insertion or deletion.
4425 * [I] infoPtr : valid pointer to the listview structure
4426 * [I] nItem : item index
4427 * [I] dir : -1 if deleting, 1 if inserting
4432 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
4434 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4435 INT nPerCol
, nItemCol
, nItemRow
;
4439 /* if we don't refresh, what's the point of scrolling? */
4440 if (!is_redrawing(infoPtr
)) return;
4442 assert (abs(dir
) == 1);
4444 /* arrange icons if autoarrange is on */
4445 if (is_autoarrange(infoPtr
))
4447 BOOL arrange
= TRUE
;
4448 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
4449 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
4450 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4453 /* scrollbars need updating */
4454 LISTVIEW_UpdateScroll(infoPtr
);
4456 /* figure out the item's position */
4457 if (uView
== LVS_REPORT
)
4458 nPerCol
= infoPtr
->nItemCount
+ 1;
4459 else if (uView
== LVS_LIST
)
4460 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
4461 else /* LVS_ICON, or LVS_SMALLICON */
4464 nItemCol
= nItem
/ nPerCol
;
4465 nItemRow
= nItem
% nPerCol
;
4466 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4468 /* move the items below up a slot */
4469 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
4470 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
4471 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
4472 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4473 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4474 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
4475 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4477 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll
), wine_dbgstr_rect(&infoPtr
->rcList
));
4478 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4479 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4482 /* report has only that column, so we're done */
4483 if (uView
== LVS_REPORT
) return;
4485 /* now for LISTs, we have to deal with the columns to the right */
4486 rcScroll
.left
= (nItemCol
+ 1) * infoPtr
->nItemWidth
;
4488 rcScroll
.right
= (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
4489 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4490 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4491 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4492 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4493 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4498 * Removes an item from the listview control.
4501 * [I] infoPtr : valid pointer to the listview structure
4502 * [I] nItem : item index
4508 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
4510 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4513 TRACE("(nItem=%d)\n", nItem
);
4515 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
4517 /* remove selection, and focus */
4519 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4520 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
4522 /* send LVN_DELETEITEM notification. */
4523 if (!notify_deleteitem(infoPtr
, nItem
)) return FALSE
;
4525 /* we need to do this here, because we'll be deleting stuff */
4526 if (uView
== LVS_SMALLICON
|| uView
== LVS_ICON
)
4527 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4529 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4535 hdpaSubItems
= (HDPA
)DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
4536 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4538 hdrItem
= (ITEMHDR
*)DPA_GetPtr(hdpaSubItems
, i
);
4539 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4542 DPA_Destroy(hdpaSubItems
);
4545 if (uView
== LVS_SMALLICON
|| uView
== LVS_ICON
)
4547 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
4548 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
4551 infoPtr
->nItemCount
--;
4552 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
4554 /* now is the invalidation fun */
4555 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
4562 * Callback implementation for editlabel control
4565 * [I] infoPtr : valid pointer to the listview structure
4566 * [I] pszText : modified text
4567 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4573 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, LPWSTR pszText
, BOOL isW
)
4575 HWND hwndSelf
= infoPtr
->hwndSelf
;
4576 NMLVDISPINFOW dispInfo
;
4578 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
4580 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4581 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4582 dispInfo
.item
.iItem
= infoPtr
->nEditLabelItem
;
4583 dispInfo
.item
.iSubItem
= 0;
4584 dispInfo
.item
.stateMask
= ~0;
4585 if (!LISTVIEW_GetItemW(infoPtr
, &dispInfo
.item
)) return FALSE
;
4586 /* add the text from the edit in */
4587 dispInfo
.item
.mask
|= LVIF_TEXT
;
4588 dispInfo
.item
.pszText
= pszText
;
4589 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4591 /* Do we need to update the Item Text */
4592 if (!notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
)) return FALSE
;
4593 if (!IsWindow(hwndSelf
))
4595 if (!pszText
) return TRUE
;
4597 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4599 HDPA hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nEditLabelItem
);
4600 ITEM_INFO
* lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
4601 if (lpItem
&& lpItem
->hdr
.pszText
== LPSTR_TEXTCALLBACKW
)
4603 LISTVIEW_InvalidateItem(infoPtr
, infoPtr
->nEditLabelItem
);
4608 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4609 dispInfo
.item
.mask
= LVIF_TEXT
;
4610 dispInfo
.item
.iItem
= infoPtr
->nEditLabelItem
;
4611 dispInfo
.item
.iSubItem
= 0;
4612 dispInfo
.item
.pszText
= pszText
;
4613 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4614 return LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
4619 * Begin in place editing of specified list view item
4622 * [I] infoPtr : valid pointer to the listview structure
4623 * [I] nItem : item index
4624 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4630 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
4632 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
4633 NMLVDISPINFOW dispInfo
;
4635 HWND hwndSelf
= infoPtr
->hwndSelf
;
4637 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
4639 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
4640 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
4642 infoPtr
->nEditLabelItem
= nItem
;
4644 /* Is the EditBox still there, if so remove it */
4645 if(infoPtr
->hwndEdit
!= 0)
4647 SetFocus(infoPtr
->hwndSelf
);
4648 infoPtr
->hwndEdit
= 0;
4651 LISTVIEW_SetSelection(infoPtr
, nItem
);
4652 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
4653 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4655 rect
.left
= LVIR_LABEL
;
4656 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
4658 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4659 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4660 dispInfo
.item
.iItem
= nItem
;
4661 dispInfo
.item
.iSubItem
= 0;
4662 dispInfo
.item
.stateMask
= ~0;
4663 dispInfo
.item
.pszText
= szDispText
;
4664 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
4665 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
4667 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, WS_VISIBLE
,
4668 rect
.left
-2, rect
.top
-1, 0, rect
.bottom
- rect
.top
+2, isW
);
4669 if (!infoPtr
->hwndEdit
) return 0;
4671 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
4673 if (!IsWindow(hwndSelf
))
4675 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
4676 infoPtr
->hwndEdit
= 0;
4680 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
4681 SetFocus(infoPtr
->hwndEdit
);
4682 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
4683 return infoPtr
->hwndEdit
;
4689 * Ensures the specified item is visible, scrolling into view if necessary.
4692 * [I] infoPtr : valid pointer to the listview structure
4693 * [I] nItem : item index
4694 * [I] bPartial : partially or entirely visible
4700 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
4702 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4703 INT nScrollPosHeight
= 0;
4704 INT nScrollPosWidth
= 0;
4705 INT nHorzAdjust
= 0;
4706 INT nVertAdjust
= 0;
4709 RECT rcItem
, rcTemp
;
4711 rcItem
.left
= LVIR_BOUNDS
;
4712 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
4714 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
4716 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
4718 /* scroll left/right, but in LVS_REPORT mode */
4719 if (uView
== LVS_LIST
)
4720 nScrollPosWidth
= infoPtr
->nItemWidth
;
4721 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
4722 nScrollPosWidth
= 1;
4724 if (rcItem
.left
< infoPtr
->rcList
.left
)
4727 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
4732 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
4736 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
4738 /* scroll up/down, but not in LVS_LIST mode */
4739 if (uView
== LVS_REPORT
)
4740 nScrollPosHeight
= infoPtr
->nItemHeight
;
4741 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
4742 nScrollPosHeight
= 1;
4744 if (rcItem
.top
< infoPtr
->rcList
.top
)
4747 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
4752 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
4756 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
4758 if (nScrollPosWidth
)
4760 INT diff
= nHorzDiff
/ nScrollPosWidth
;
4761 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
4762 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
4765 if (nScrollPosHeight
)
4767 INT diff
= nVertDiff
/ nScrollPosHeight
;
4768 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
4769 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
4777 * Searches for an item with specific characteristics.
4780 * [I] hwnd : window handle
4781 * [I] nStart : base item index
4782 * [I] lpFindInfo : item information to look for
4785 * SUCCESS : index of item
4788 static INT
LISTVIEW_FindItemW(LISTVIEW_INFO
*infoPtr
, INT nStart
,
4789 const LVFINDINFOW
*lpFindInfo
)
4791 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4792 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
4793 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
4794 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
4795 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
4796 POINT Position
, Destination
;
4799 if (!lpFindInfo
|| nItem
< 0) return -1;
4802 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
))
4804 lvItem
.mask
|= LVIF_TEXT
;
4805 lvItem
.pszText
= szDispText
;
4806 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4809 if (lpFindInfo
->flags
& LVFI_WRAP
)
4812 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
4813 (uView
== LVS_ICON
|| uView
==LVS_SMALLICON
))
4818 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4819 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
4820 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
4821 switch(lpFindInfo
->vkDirection
)
4823 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
4824 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
4825 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
4826 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
4827 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
4828 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
4829 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
4831 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
4832 Destination
.x
= rcArea
.right
;
4833 Destination
.y
= rcArea
.bottom
;
4835 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
4839 else Destination
.x
= Destination
.y
= 0;
4841 /* if LVFI_PARAM is specified, all other flags are ignored */
4842 if (lpFindInfo
->flags
& LVFI_PARAM
)
4844 lvItem
.mask
|= LVIF_PARAM
;
4846 lvItem
.mask
&= ~LVIF_TEXT
;
4850 for (; nItem
< nLast
; nItem
++)
4852 lvItem
.iItem
= nItem
;
4853 lvItem
.iSubItem
= 0;
4854 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
4856 if (lvItem
.mask
& LVIF_PARAM
)
4858 if (lpFindInfo
->lParam
== lvItem
.lParam
)
4864 if (lvItem
.mask
& LVIF_TEXT
)
4866 if (lpFindInfo
->flags
& LVFI_PARTIAL
)
4868 if (strstrW(lvItem
.pszText
, lpFindInfo
->psz
) == NULL
) continue;
4872 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
4876 if (!bNearest
) return nItem
;
4878 /* This is very inefficient. To do a good job here,
4879 * we need a sorted array of (x,y) item positions */
4880 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
4882 /* compute the distance^2 to the destination */
4883 xdist
= Destination
.x
- Position
.x
;
4884 ydist
= Destination
.y
- Position
.y
;
4885 dist
= xdist
* xdist
+ ydist
* ydist
;
4887 /* remember the distance, and item if it's closer */
4891 nNearestItem
= nItem
;
4898 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
4903 return nNearestItem
;
4908 * Searches for an item with specific characteristics.
4911 * [I] hwnd : window handle
4912 * [I] nStart : base item index
4913 * [I] lpFindInfo : item information to look for
4916 * SUCCESS : index of item
4919 static INT
LISTVIEW_FindItemA(LISTVIEW_INFO
*infoPtr
, INT nStart
,
4920 const LVFINDINFOA
*lpFindInfo
)
4922 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
);
4926 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
4927 if (hasText
) fiw
.psz
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
4928 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
4929 if (hasText
) textfreeT((LPWSTR
)fiw
.psz
, FALSE
);
4935 * Retrieves the background image of the listview control.
4938 * [I] infoPtr : valid pointer to the listview structure
4939 * [O] lpBkImage : background image attributes
4945 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4947 /* FIXME (listview, "empty stub!\n"); */
4953 * Retrieves column attributes.
4956 * [I] infoPtr : valid pointer to the listview structure
4957 * [I] nColumn : column index
4958 * [IO] lpColumn : column information
4959 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4960 * otherwise it is in fact a LPLVCOLUMNA
4966 static BOOL
LISTVIEW_GetColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
4968 COLUMN_INFO
*lpColumnInfo
;
4971 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4972 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
4974 /* initialize memory */
4975 ZeroMemory(&hdi
, sizeof(hdi
));
4977 if (lpColumn
->mask
& LVCF_TEXT
)
4979 hdi
.mask
|= HDI_TEXT
;
4980 hdi
.pszText
= lpColumn
->pszText
;
4981 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
4984 if (lpColumn
->mask
& LVCF_IMAGE
)
4985 hdi
.mask
|= HDI_IMAGE
;
4987 if (lpColumn
->mask
& LVCF_ORDER
)
4988 hdi
.mask
|= HDI_ORDER
;
4990 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
4992 if (lpColumn
->mask
& LVCF_FMT
)
4993 lpColumn
->fmt
= lpColumnInfo
->fmt
;
4995 if (lpColumn
->mask
& LVCF_WIDTH
)
4996 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
4998 if (lpColumn
->mask
& LVCF_IMAGE
)
4999 lpColumn
->iImage
= hdi
.iImage
;
5001 if (lpColumn
->mask
& LVCF_ORDER
)
5002 lpColumn
->iOrder
= hdi
.iOrder
;
5008 static BOOL
LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
5015 /* FIXME: little hack */
5016 for (i
= 0; i
< iCount
; i
++)
5024 * Retrieves the column width.
5027 * [I] infoPtr : valid pointer to the listview structure
5028 * [I] int : column index
5031 * SUCCESS : column width
5034 static INT
LISTVIEW_GetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
5036 INT nColumnWidth
= 0;
5039 TRACE("nColumn=%d\n", nColumn
);
5041 /* we have a 'column' in LIST and REPORT mode only */
5042 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
)
5045 nColumnWidth
= infoPtr
->nItemWidth
;
5048 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
5049 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
5050 nColumnWidth
= rcHeader
.right
- rcHeader
.left
;
5054 TRACE("nColumnWidth=%d\n", nColumnWidth
);
5055 return nColumnWidth
;
5060 * In list or report display mode, retrieves the number of items that can fit
5061 * vertically in the visible area. In icon or small icon display mode,
5062 * retrieves the total number of visible items.
5065 * [I] infoPtr : valid pointer to the listview structure
5068 * Number of fully visible items.
5070 static INT
LISTVIEW_GetCountPerPage(LISTVIEW_INFO
*infoPtr
)
5072 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
5076 return infoPtr
->nItemCount
;
5078 return LISTVIEW_GetCountPerColumn(infoPtr
);
5080 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
5088 * Retrieves an image list handle.
5091 * [I] infoPtr : valid pointer to the listview structure
5092 * [I] nImageList : image list identifier
5095 * SUCCESS : image list handle
5098 static HIMAGELIST
LISTVIEW_GetImageList(LISTVIEW_INFO
*infoPtr
, INT nImageList
)
5102 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
5103 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
5104 case LVSIL_STATE
: return infoPtr
->himlState
;
5109 /* LISTVIEW_GetISearchString */
5113 * Retrieves item attributes.
5116 * [I] hwnd : window handle
5117 * [IO] lpLVItem : item info
5118 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5119 * if FALSE, the lpLVItem is a LPLVITEMA.
5122 * This is the internal 'GetItem' interface -- it tries to
5123 * be smart and avoid text copies, if possible, by modifying
5124 * lpLVItem->pszText to point to the text string. Please note
5125 * that this is not always possible (e.g. OWNERDATA), so on
5126 * entry you *must* supply valid values for pszText, and cchTextMax.
5127 * The only difference to the documented interface is that upon
5128 * return, you should use *only* the lpLVItem->pszText, rather than
5129 * the buffer pointer you provided on input. Most code already does
5130 * that, so it's not a problem.
5131 * For the two cases when the text must be copied (that is,
5132 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5138 static BOOL
LISTVIEW_GetItemT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5140 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
5141 NMLVDISPINFOW dispInfo
;
5147 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
5149 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5152 if (lpLVItem
->mask
== 0) return TRUE
;
5154 /* make a local copy */
5155 isubitem
= lpLVItem
->iSubItem
;
5157 /* a quick optimization if all we're asked is the focus state
5158 * these queries are worth optimising since they are common,
5159 * and can be answered in constant time, without the heavy accesses */
5160 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
5161 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
5163 lpLVItem
->state
= 0;
5164 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5165 lpLVItem
->state
|= LVIS_FOCUSED
;
5169 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5171 /* if the app stores all the data, handle it separately */
5172 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5174 dispInfo
.item
.state
= 0;
5176 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5177 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) || infoPtr
->uCallbackMask
)
5179 /* NOTE: copy only fields which we _know_ are initialized, some apps
5180 * depend on the uninitialized fields being 0 */
5181 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
5182 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5183 dispInfo
.item
.iSubItem
= isubitem
;
5184 if (lpLVItem
->mask
& LVIF_TEXT
)
5186 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5187 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5189 if (lpLVItem
->mask
& LVIF_STATE
)
5190 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
5191 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5192 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
5193 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
5195 /* full size structure expected - _WIN32IE >= 0x560 */
5196 *lpLVItem
= dispInfo
.item
;
5198 else if (lpLVItem
->mask
& LVIF_INDENT
)
5200 /* indent member expected - _WIN32IE >= 0x300 */
5201 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
5205 /* minimal structure expected */
5206 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
5208 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
5211 /* make sure lParam is zeroed out */
5212 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
5214 /* we store only a little state, so if we're not asked, we're done */
5215 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
5217 /* if focus is handled by us, report it */
5218 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5220 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5221 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5222 lpLVItem
->state
|= LVIS_FOCUSED
;
5225 /* and do the same for selection, if we handle it */
5226 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5228 lpLVItem
->state
&= ~LVIS_SELECTED
;
5229 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5230 lpLVItem
->state
|= LVIS_SELECTED
;
5236 /* find the item and subitem structures before we proceed */
5237 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
5238 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
5243 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
5244 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
5247 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
5252 pItemHdr
= &lpItem
->hdr
;
5254 /* Do we need to query the state from the app? */
5255 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
5257 dispInfo
.item
.mask
|= LVIF_STATE
;
5258 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
5261 /* Do we need to enquire about the image? */
5262 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
5263 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
5265 dispInfo
.item
.mask
|= LVIF_IMAGE
;
5266 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
5269 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5270 if ((lpLVItem
->mask
& LVIF_TEXT
) && !is_textW(pItemHdr
->pszText
))
5272 dispInfo
.item
.mask
|= LVIF_TEXT
;
5273 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5274 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5275 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
5276 *dispInfo
.item
.pszText
= '\0';
5279 /* If we don't have all the requested info, query the application */
5280 if (dispInfo
.item
.mask
!= 0)
5282 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5283 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
5284 dispInfo
.item
.lParam
= lpItem
->lParam
;
5285 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5286 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
5289 /* we should not store values for subitems */
5290 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
5292 /* Now, handle the iImage field */
5293 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
5295 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
5296 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
5297 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
5299 else if (lpLVItem
->mask
& LVIF_IMAGE
)
5301 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
5302 lpLVItem
->iImage
= pItemHdr
->iImage
;
5304 lpLVItem
->iImage
= 0;
5307 /* The pszText field */
5308 if (dispInfo
.item
.mask
& LVIF_TEXT
)
5310 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
5311 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
5313 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
5315 else if (lpLVItem
->mask
& LVIF_TEXT
)
5317 if (isW
) lpLVItem
->pszText
= pItemHdr
->pszText
;
5318 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
5321 /* if this is a subitem, we're done */
5322 if (isubitem
) return TRUE
;
5324 /* Next is the lParam field */
5325 if (dispInfo
.item
.mask
& LVIF_PARAM
)
5327 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
5328 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
5329 lpItem
->lParam
= dispInfo
.item
.lParam
;
5331 else if (lpLVItem
->mask
& LVIF_PARAM
)
5332 lpLVItem
->lParam
= lpItem
->lParam
;
5334 /* ... the state field (this one is different due to uCallbackmask) */
5335 if (lpLVItem
->mask
& LVIF_STATE
)
5337 lpLVItem
->state
= lpItem
->state
;
5338 if (dispInfo
.item
.mask
& LVIF_STATE
)
5340 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
5341 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
5343 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5345 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5346 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5347 lpLVItem
->state
|= LVIS_FOCUSED
;
5349 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5351 lpLVItem
->state
&= ~LVIS_SELECTED
;
5352 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5353 lpLVItem
->state
|= LVIS_SELECTED
;
5357 /* and last, but not least, the indent field */
5358 if (lpLVItem
->mask
& LVIF_INDENT
)
5359 lpLVItem
->iIndent
= lpItem
->iIndent
;
5366 * Retrieves item attributes.
5369 * [I] hwnd : window handle
5370 * [IO] lpLVItem : item info
5371 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5372 * if FALSE, the lpLVItem is a LPLVITEMA.
5375 * This is the external 'GetItem' interface -- it properly copies
5376 * the text in the provided buffer.
5382 static BOOL
LISTVIEW_GetItemExtT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5387 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5390 pszText
= lpLVItem
->pszText
;
5391 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
5392 if (bResult
&& lpLVItem
->pszText
!= pszText
)
5393 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
5394 lpLVItem
->pszText
= pszText
;
5402 * Retrieves the position (upper-left) of the listview control item.
5403 * Note that for LVS_ICON style, the upper-left is that of the icon
5404 * and not the bounding box.
5407 * [I] infoPtr : valid pointer to the listview structure
5408 * [I] nItem : item index
5409 * [O] lpptPosition : coordinate information
5415 static BOOL
LISTVIEW_GetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
5417 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5420 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
5422 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5424 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5425 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
5427 if (uView
== LVS_ICON
)
5429 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
5430 lpptPosition
->y
+= ICON_TOP_PADDING
;
5432 lpptPosition
->x
+= Origin
.x
;
5433 lpptPosition
->y
+= Origin
.y
;
5435 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition
));
5442 * Retrieves the bounding rectangle for a listview control item.
5445 * [I] infoPtr : valid pointer to the listview structure
5446 * [I] nItem : item index
5447 * [IO] lprc : bounding rectangle coordinates
5448 * lprc->left specifies the portion of the item for which the bounding
5449 * rectangle will be retrieved.
5451 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5452 * including the icon and label.
5455 * * Experiment shows that native control returns:
5456 * * width = min (48, length of text line)
5457 * * .left = position.x - (width - iconsize.cx)/2
5458 * * .right = .left + width
5459 * * height = #lines of text * ntmHeight + icon height + 8
5460 * * .top = position.y - 2
5461 * * .bottom = .top + height
5462 * * separation between items .y = itemSpacing.cy - height
5463 * * .x = itemSpacing.cx - width
5464 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5467 * * Experiment shows that native control returns:
5468 * * width = iconSize.cx + 16
5469 * * .left = position.x - (width - iconsize.cx)/2
5470 * * .right = .left + width
5471 * * height = iconSize.cy + 4
5472 * * .top = position.y - 2
5473 * * .bottom = .top + height
5474 * * separation between items .y = itemSpacing.cy - height
5475 * * .x = itemSpacing.cx - width
5476 * LVIR_LABEL Returns the bounding rectangle of the item text.
5479 * * Experiment shows that native control returns:
5480 * * width = text length
5481 * * .left = position.x - width/2
5482 * * .right = .left + width
5483 * * height = ntmH * linecount + 2
5484 * * .top = position.y + iconSize.cy + 6
5485 * * .bottom = .top + height
5486 * * separation between items .y = itemSpacing.cy - height
5487 * * .x = itemSpacing.cx - width
5488 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5489 * rectangles, but excludes columns in report view.
5496 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5497 * upon whether the window has the focus currently and on whether the item
5498 * is the one with the focus. Ensure that the control's record of which
5499 * item has the focus agrees with the items' records.
5501 static BOOL
LISTVIEW_GetItemRect(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5503 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5504 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5505 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
5506 POINT Position
, Origin
;
5510 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
5512 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5514 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5515 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5517 /* Be smart and try to figure out the minimum we have to do */
5518 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
5519 if (uView
== LVS_REPORT
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
5520 if (uView
== LVS_ICON
&& lprc
->left
!= LVIR_ICON
&&
5521 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
5522 oversizedBox
= TRUE
;
5524 /* get what we need from the item before hand, so we make
5525 * only one request. This can speed up things, if data
5526 * is stored on the app side */
5528 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
5529 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
5530 lvItem
.iItem
= nItem
;
5531 lvItem
.iSubItem
= 0;
5532 lvItem
.pszText
= szDispText
;
5533 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5534 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5535 /* we got the state already up, simulate it here, to avoid a reget */
5536 if (uView
== LVS_ICON
&& (lprc
->left
!= LVIR_ICON
))
5538 lvItem
.mask
|= LVIF_STATE
;
5539 lvItem
.stateMask
= LVIS_FOCUSED
;
5540 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
5543 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
5544 lprc
->left
= LVIR_BOUNDS
;
5548 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
);
5552 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, lprc
);
5556 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
);
5559 case LVIR_SELECTBOUNDS
:
5560 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, &label_rect
);
5561 UnionRect(lprc
, lprc
, &label_rect
);
5565 WARN("Unknown value: %ld\n", lprc
->left
);
5569 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
5571 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc
));
5578 * Retrieves the spacing between listview control items.
5581 * [I] infoPtr : valid pointer to the listview structure
5582 * [IO] lprc : rectangle to receive the output
5583 * on input, lprc->top = nSubItem
5584 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5586 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5587 * not only those of the first column.
5588 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5594 static BOOL
LISTVIEW_GetSubItemRect(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5598 INT nColumn
= lprc
->top
;
5600 if (!lprc
) return FALSE
;
5602 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem
, lprc
->top
);
5603 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5605 return LISTVIEW_GetItemRect(infoPtr
, nItem
, lprc
);
5607 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return FALSE
;
5609 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &Position
)) return FALSE
;
5611 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5614 lvItem
.iItem
= nItem
;
5615 lvItem
.iSubItem
= nColumn
;
5617 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5621 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
);
5626 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
);
5630 ERR("Unknown bounds=%ld\n", lprc
->left
);
5634 OffsetRect(lprc
, Position
.x
, Position
.y
);
5641 * Retrieves the width of a label.
5644 * [I] infoPtr : valid pointer to the listview structure
5647 * SUCCESS : string width (in pixels)
5650 static INT
LISTVIEW_GetLabelWidth(LISTVIEW_INFO
*infoPtr
, INT nItem
)
5652 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5655 TRACE("(nItem=%d)\n", nItem
);
5657 lvItem
.mask
= LVIF_TEXT
;
5658 lvItem
.iItem
= nItem
;
5659 lvItem
.iSubItem
= 0;
5660 lvItem
.pszText
= szDispText
;
5661 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5662 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5664 return LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
5669 * Retrieves the spacing between listview control items.
5672 * [I] infoPtr : valid pointer to the listview structure
5673 * [I] bSmall : flag for small or large icon
5676 * Horizontal + vertical spacing
5678 static LONG
LISTVIEW_GetItemSpacing(LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
5684 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
5688 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
5689 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
5691 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
5698 * Retrieves the state of a listview control item.
5701 * [I] infoPtr : valid pointer to the listview structure
5702 * [I] nItem : item index
5703 * [I] uMask : state mask
5706 * State specified by the mask.
5708 static UINT
LISTVIEW_GetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
5712 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
5714 lvItem
.iItem
= nItem
;
5715 lvItem
.iSubItem
= 0;
5716 lvItem
.mask
= LVIF_STATE
;
5717 lvItem
.stateMask
= uMask
;
5718 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5720 return lvItem
.state
& uMask
;
5725 * Retrieves the text of a listview control item or subitem.
5728 * [I] hwnd : window handle
5729 * [I] nItem : item index
5730 * [IO] lpLVItem : item information
5731 * [I] isW : TRUE if lpLVItem is Unicode
5734 * SUCCESS : string length
5737 static INT
LISTVIEW_GetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
5739 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
5741 lpLVItem
->mask
= LVIF_TEXT
;
5742 lpLVItem
->iItem
= nItem
;
5743 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
5745 return textlenT(lpLVItem
->pszText
, isW
);
5750 * Searches for an item based on properties + relationships.
5753 * [I] infoPtr : valid pointer to the listview structure
5754 * [I] nItem : item index
5755 * [I] uFlags : relationship flag
5758 * SUCCESS : item index
5761 static INT
LISTVIEW_GetNextItem(LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
5763 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5765 LVFINDINFOW lvFindInfo
;
5766 INT nCountPerColumn
;
5770 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
5771 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
5773 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
5775 if (uFlags
& LVNI_CUT
)
5778 if (uFlags
& LVNI_DROPHILITED
)
5779 uMask
|= LVIS_DROPHILITED
;
5781 if (uFlags
& LVNI_FOCUSED
)
5782 uMask
|= LVIS_FOCUSED
;
5784 if (uFlags
& LVNI_SELECTED
)
5785 uMask
|= LVIS_SELECTED
;
5787 /* if we're asked for the focused item, that's only one,
5788 * so it's worth optimizing */
5789 if (uFlags
& LVNI_FOCUSED
)
5791 if ((LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) != uMask
) return -1;
5792 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
5795 if (uFlags
& LVNI_ABOVE
)
5797 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
5802 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5808 /* Special case for autoarrange - move 'til the top of a list */
5809 if (is_autoarrange(infoPtr
))
5811 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5812 while (nItem
- nCountPerRow
>= 0)
5814 nItem
-= nCountPerRow
;
5815 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5820 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5821 lvFindInfo
.vkDirection
= VK_UP
;
5822 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
5823 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5825 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5830 else if (uFlags
& LVNI_BELOW
)
5832 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
5834 while (nItem
< infoPtr
->nItemCount
)
5837 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5843 /* Special case for autoarrange - move 'til the bottom of a list */
5844 if (is_autoarrange(infoPtr
))
5846 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5847 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
5849 nItem
+= nCountPerRow
;
5850 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5855 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5856 lvFindInfo
.vkDirection
= VK_DOWN
;
5857 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
5858 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5860 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5865 else if (uFlags
& LVNI_TOLEFT
)
5867 if (uView
== LVS_LIST
)
5869 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
5870 while (nItem
- nCountPerColumn
>= 0)
5872 nItem
-= nCountPerColumn
;
5873 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5877 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5879 /* Special case for autoarrange - move 'ti the beginning of a row */
5880 if (is_autoarrange(infoPtr
))
5882 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5883 while (nItem
% nCountPerRow
> 0)
5886 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5891 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5892 lvFindInfo
.vkDirection
= VK_LEFT
;
5893 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
5894 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5896 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5901 else if (uFlags
& LVNI_TORIGHT
)
5903 if (uView
== LVS_LIST
)
5905 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
5906 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
5908 nItem
+= nCountPerColumn
;
5909 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5913 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5915 /* Special case for autoarrange - move 'til the end of a row */
5916 if (is_autoarrange(infoPtr
))
5918 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5919 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
5922 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5927 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5928 lvFindInfo
.vkDirection
= VK_RIGHT
;
5929 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
5930 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5932 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5941 /* search by index */
5942 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
5944 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
5952 /* LISTVIEW_GetNumberOfWorkAreas */
5956 * Retrieves the origin coordinates when in icon or small icon display mode.
5959 * [I] infoPtr : valid pointer to the listview structure
5960 * [O] lpptOrigin : coordinate information
5965 static void LISTVIEW_GetOrigin(LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
5967 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5968 INT nHorzPos
= 0, nVertPos
= 0;
5969 SCROLLINFO scrollInfo
;
5971 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
5972 scrollInfo
.fMask
= SIF_POS
;
5974 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
5975 nHorzPos
= scrollInfo
.nPos
;
5976 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
5977 nVertPos
= scrollInfo
.nPos
;
5979 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
5981 lpptOrigin
->x
= infoPtr
->rcList
.left
;
5982 lpptOrigin
->y
= infoPtr
->rcList
.top
;
5983 if (uView
== LVS_LIST
)
5984 nHorzPos
*= infoPtr
->nItemWidth
;
5985 else if (uView
== LVS_REPORT
)
5986 nVertPos
*= infoPtr
->nItemHeight
;
5988 lpptOrigin
->x
-= nHorzPos
;
5989 lpptOrigin
->y
-= nVertPos
;
5991 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin
));
5996 * Retrieves the width of a string.
5999 * [I] hwnd : window handle
6000 * [I] lpszText : text string to process
6001 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6004 * SUCCESS : string width (in pixels)
6007 static INT
LISTVIEW_GetStringWidthT(LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
6012 if (is_textT(lpszText
, isW
))
6014 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
6015 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
6016 HFONT hOldFont
= SelectObject(hdc
, hFont
);
6019 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
6021 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
6022 SelectObject(hdc
, hOldFont
);
6023 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6025 return stringSize
.cx
;
6030 * Determines which listview item is located at the specified position.
6033 * [I] infoPtr : valid pointer to the listview structure
6034 * [IO] lpht : hit test information
6035 * [I] subitem : fill out iSubItem.
6036 * [I] select : return the index only if the hit selects the item
6039 * (mm 20001022): We must not allow iSubItem to be touched, for
6040 * an app might pass only a structure with space up to iItem!
6041 * (MS Office 97 does that for instance in the file open dialog)
6044 * SUCCESS : item index
6047 static INT
LISTVIEW_HitTest(LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
6049 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6050 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6051 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
6052 POINT Origin
, Position
, opt
;
6057 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht
->pt
), subitem
, select
);
6061 if (subitem
) lpht
->iSubItem
= 0;
6063 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
6064 lpht
->flags
|= LVHT_TOLEFT
;
6065 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
6066 lpht
->flags
|= LVHT_TORIGHT
;
6068 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
6069 lpht
->flags
|= LVHT_ABOVE
;
6070 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
6071 lpht
->flags
|= LVHT_BELOW
;
6073 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6074 if (lpht
->flags
) return -1;
6076 lpht
->flags
|= LVHT_NOWHERE
;
6078 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6080 /* first deal with the large items */
6081 rcSearch
.left
= lpht
->pt
.x
;
6082 rcSearch
.top
= lpht
->pt
.y
;
6083 rcSearch
.right
= rcSearch
.left
+ 1;
6084 rcSearch
.bottom
= rcSearch
.top
+ 1;
6086 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
6087 iterator_next(&i
); /* go to first item in the sequence */
6089 iterator_destroy(&i
);
6091 TRACE("lpht->iItem=%d\n", iItem
);
6092 if (iItem
== -1) return -1;
6094 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
6095 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
6096 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
6097 if (uView
== LVS_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
6098 lvItem
.iItem
= iItem
;
6099 lvItem
.iSubItem
= 0;
6100 lvItem
.pszText
= szDispText
;
6101 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6102 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
6103 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
6105 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcState
, &rcIcon
, &rcLabel
);
6106 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
6107 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
6108 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
6110 if (uView
== LVS_REPORT
)
6113 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6114 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds
));
6115 if (!PtInRect(&rcBounds
, opt
)) return -1;
6117 if (PtInRect(&rcIcon
, opt
))
6118 lpht
->flags
|= LVHT_ONITEMICON
;
6119 else if (PtInRect(&rcLabel
, opt
))
6120 lpht
->flags
|= LVHT_ONITEMLABEL
;
6121 else if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && PtInRect(&rcState
, opt
))
6122 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
6123 if (lpht
->flags
& LVHT_ONITEM
)
6124 lpht
->flags
&= ~LVHT_NOWHERE
;
6126 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6127 if (uView
== LVS_REPORT
&& subitem
)
6131 rcBounds
.right
= rcBounds
.left
;
6132 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
6134 rcBounds
.left
= rcBounds
.right
;
6135 rcBounds
.right
+= LISTVIEW_GetColumnWidth(infoPtr
, j
);
6136 if (PtInRect(&rcBounds
, opt
))
6144 if (select
&& !(uView
== LVS_REPORT
&&
6145 ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) ||
6146 (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))))
6148 if (uView
== LVS_REPORT
)
6150 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6151 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6153 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
6155 return lpht
->iItem
= iItem
;
6159 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6160 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6161 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6162 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6163 their own sort proc. when sending LVM_SORTITEMS.
6166 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6168 LVS_SORTXXX must be specified,
6169 LVS_OWNERDRAW is not set,
6170 <item>.pszText is not LPSTR_TEXTCALLBACK.
6172 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6173 are sorted based on item text..."
6175 static INT WINAPI
LISTVIEW_InsertCompare( LPVOID first
, LPVOID second
, LPARAM lParam
)
6177 ITEM_INFO
* lv_first
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)first
, 0 );
6178 ITEM_INFO
* lv_second
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)second
, 0 );
6179 INT cmpv
= textcmpWT(lv_first
->hdr
.pszText
, lv_second
->hdr
.pszText
, TRUE
);
6181 /* if we're sorting descending, negate the return value */
6182 return (((LISTVIEW_INFO
*)lParam
)->dwStyle
& LVS_SORTDESCENDING
) ? -cmpv
: cmpv
;
6187 * Inserts a new item in the listview control.
6190 * [I] infoPtr : valid pointer to the listview structure
6191 * [I] lpLVItem : item information
6192 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6195 * SUCCESS : new item index
6198 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
6200 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6205 BOOL is_sorted
, has_changed
;
6207 HWND hwndSelf
= infoPtr
->hwndSelf
;
6209 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6211 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
6213 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6214 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iSubItem
) return -1;
6216 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
6218 if (!(lpItem
= (ITEM_INFO
*)Alloc(sizeof(ITEM_INFO
)))) return -1;
6220 /* insert item in listview control data structure */
6221 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
6222 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
6224 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
6225 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
6227 nItem
= is_sorted
? infoPtr
->nItemCount
: min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
6228 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
6229 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
6230 if (nItem
== -1) goto fail
;
6231 infoPtr
->nItemCount
++;
6233 /* shift indices first so they don't get tangled */
6234 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
6236 /* set the item attributes */
6237 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6239 /* full size structure expected - _WIN32IE >= 0x560 */
6242 else if (lpLVItem
->mask
& LVIF_INDENT
)
6244 /* indent member expected - _WIN32IE >= 0x300 */
6245 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
6249 /* minimal structure expected */
6250 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
6253 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) item
.state
&= ~LVIS_STATEIMAGEMASK
;
6254 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
6256 /* if we're sorted, sort the list, and update the index */
6259 DPA_Sort( infoPtr
->hdpaItems
, LISTVIEW_InsertCompare
, (LPARAM
)infoPtr
);
6260 nItem
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, hdpaSubItems
);
6261 assert(nItem
!= -1);
6264 /* make room for the position, if we are in the right mode */
6265 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6267 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
6269 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
6271 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
6276 /* send LVN_INSERTITEM notification */
6277 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
6279 nmlv
.lParam
= lpItem
->lParam
;
6280 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
6281 if (!IsWindow(hwndSelf
))
6284 /* align items (set position of each item) */
6285 if ((uView
== LVS_SMALLICON
|| uView
== LVS_ICON
))
6289 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
6290 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
6292 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
6294 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
6297 /* now is the invalidation fun */
6298 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
6302 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
6303 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
6304 infoPtr
->nItemCount
--;
6306 DPA_DeletePtr(hdpaSubItems
, 0);
6307 DPA_Destroy (hdpaSubItems
);
6314 * Redraws a range of items.
6317 * [I] infoPtr : valid pointer to the listview structure
6318 * [I] nFirst : first item
6319 * [I] nLast : last item
6325 static BOOL
LISTVIEW_RedrawItems(LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
6329 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
6330 max(nFirst
, nLast
) >= infoPtr
->nItemCount
)
6333 for (i
= nFirst
; i
<= nLast
; i
++)
6334 LISTVIEW_InvalidateItem(infoPtr
, i
);
6341 * Scroll the content of a listview.
6344 * [I] infoPtr : valid pointer to the listview structure
6345 * [I] dx : horizontal scroll amount in pixels
6346 * [I] dy : vertical scroll amount in pixels
6353 * If the control is in report mode (LVS_REPORT) the control can
6354 * be scrolled only in line increments. "dy" will be rounded to the
6355 * nearest number of pixels that are a whole line. Ex: if line height
6356 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6357 * is passed the the scroll will be 0. (per MSDN 7/2002)
6359 * For: (per experimentaion with native control and CSpy ListView)
6360 * LVS_ICON dy=1 = 1 pixel (vertical only)
6362 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6364 * LVS_LIST dx=1 = 1 column (horizontal only)
6365 * but will only scroll 1 column per message
6366 * no matter what the value.
6367 * dy must be 0 or FALSE returned.
6368 * LVS_REPORT dx=1 = 1 pixel
6372 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
6374 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
) {
6376 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
6377 dy
/= infoPtr
->nItemHeight
;
6380 if (dy
!= 0) return FALSE
;
6387 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
, 0);
6388 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
, 0);
6395 * Sets the background color.
6398 * [I] infoPtr : valid pointer to the listview structure
6399 * [I] clrBk : background color
6405 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrBk
)
6407 TRACE("(clrBk=%lx)\n", clrBk
);
6409 if(infoPtr
->clrBk
!= clrBk
) {
6410 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
6411 infoPtr
->clrBk
= clrBk
;
6412 if (clrBk
== CLR_NONE
)
6413 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
6415 infoPtr
->hBkBrush
= CreateSolidBrush(clrBk
);
6416 LISTVIEW_InvalidateList(infoPtr
);
6422 /* LISTVIEW_SetBkImage */
6424 /*** Helper for {Insert,Set}ColumnT *only* */
6425 static void column_fill_hditem(LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
, const LVCOLUMNW
*lpColumn
, BOOL isW
)
6427 if (lpColumn
->mask
& LVCF_FMT
)
6429 /* format member is valid */
6430 lphdi
->mask
|= HDI_FORMAT
;
6432 /* set text alignment (leftmost column must be left-aligned) */
6433 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
6434 lphdi
->fmt
|= HDF_LEFT
;
6435 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
6436 lphdi
->fmt
|= HDF_RIGHT
;
6437 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
6438 lphdi
->fmt
|= HDF_CENTER
;
6440 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6441 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
6443 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6445 lphdi
->fmt
|= HDF_IMAGE
;
6446 lphdi
->iImage
= I_IMAGECALLBACK
;
6450 if (lpColumn
->mask
& LVCF_WIDTH
)
6452 lphdi
->mask
|= HDI_WIDTH
;
6453 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
6455 /* make it fill the remainder of the controls width */
6459 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
6461 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
6462 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
6465 /* retrieve the layout of the header */
6466 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
6467 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, wine_dbgstr_rect(&rcHeader
));
6469 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
6472 lphdi
->cxy
= lpColumn
->cx
;
6475 if (lpColumn
->mask
& LVCF_TEXT
)
6477 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
6478 lphdi
->fmt
|= HDF_STRING
;
6479 lphdi
->pszText
= lpColumn
->pszText
;
6480 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
6483 if (lpColumn
->mask
& LVCF_IMAGE
)
6485 lphdi
->mask
|= HDI_IMAGE
;
6486 lphdi
->iImage
= lpColumn
->iImage
;
6489 if (lpColumn
->mask
& LVCF_ORDER
)
6491 lphdi
->mask
|= HDI_ORDER
;
6492 lphdi
->iOrder
= lpColumn
->iOrder
;
6499 * Inserts a new column.
6502 * [I] infoPtr : valid pointer to the listview structure
6503 * [I] nColumn : column index
6504 * [I] lpColumn : column information
6505 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6508 * SUCCESS : new column index
6511 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6512 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6514 COLUMN_INFO
*lpColumnInfo
;
6518 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6520 if (!lpColumn
|| nColumn
< 0) return -1;
6521 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
6523 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6524 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6526 /* insert item in header control */
6527 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
6528 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
6529 (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6530 if (nNewColumn
== -1) return -1;
6531 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
6533 /* create our own column info */
6534 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
6535 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
6537 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
6538 if (!Header_GetItemRect(infoPtr
->hwndHeader
, nNewColumn
, &lpColumnInfo
->rcHeader
)) goto fail
;
6540 /* now we have to actually adjust the data */
6541 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
6543 SUBITEM_INFO
*lpSubItem
;
6547 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
6549 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
6550 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
6552 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
6553 if (lpSubItem
->iSubItem
>= nNewColumn
)
6554 lpSubItem
->iSubItem
++;
6559 /* make space for the new column */
6560 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
6565 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
6568 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
6576 * Sets the attributes of a header item.
6579 * [I] infoPtr : valid pointer to the listview structure
6580 * [I] nColumn : column index
6581 * [I] lpColumn : column attributes
6582 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6588 static BOOL
LISTVIEW_SetColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6589 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6591 HDITEMW hdi
, hdiget
;
6594 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6596 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6598 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6599 if (lpColumn
->mask
& LVCF_FMT
)
6601 hdi
.mask
|= HDI_FORMAT
;
6602 hdiget
.mask
= HDI_FORMAT
;
6603 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdiget
))
6604 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
6606 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6608 /* set header item attributes */
6609 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6610 if (!bResult
) return FALSE
;
6612 if (lpColumn
->mask
& LVCF_FMT
)
6614 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
6615 int oldFmt
= lpColumnInfo
->fmt
;
6617 lpColumnInfo
->fmt
= lpColumn
->fmt
;
6618 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
6620 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6621 if (uView
== LVS_REPORT
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
6630 * Sets the column order array
6633 * [I] infoPtr : valid pointer to the listview structure
6634 * [I] iCount : number of elements in column order array
6635 * [I] lpiArray : pointer to column order array
6641 static BOOL
LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
6643 FIXME("iCount %d lpiArray %p\n", iCount
, lpiArray
);
6654 * Sets the width of a column
6657 * [I] infoPtr : valid pointer to the listview structure
6658 * [I] nColumn : column index
6659 * [I] cx : column width
6665 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
6667 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6668 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
6672 TRACE("(nColumn=%d, cx=%d\n", nColumn
, cx
);
6674 /* set column width only if in report or list mode */
6675 if (uView
!= LVS_REPORT
&& uView
!= LVS_LIST
) return FALSE
;
6677 /* take care of invalid cx values */
6678 if(uView
== LVS_REPORT
&& cx
< -2) cx
= LVSCW_AUTOSIZE
;
6679 else if (uView
== LVS_LIST
&& cx
< 1) return FALSE
;
6681 /* resize all columns if in LVS_LIST mode */
6682 if(uView
== LVS_LIST
)
6684 infoPtr
->nItemWidth
= cx
;
6685 LISTVIEW_InvalidateList(infoPtr
);
6689 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6691 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
6696 lvItem
.mask
= LVIF_TEXT
;
6698 lvItem
.iSubItem
= nColumn
;
6699 lvItem
.pszText
= szDispText
;
6700 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6701 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
6703 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
6704 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
6705 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
6707 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
6708 max_cx
+= infoPtr
->iconSize
.cx
;
6709 max_cx
+= TRAILING_LABEL_PADDING
;
6712 /* autosize based on listview items width */
6713 if(cx
== LVSCW_AUTOSIZE
)
6715 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
6717 /* if iCol is the last column make it fill the remainder of the controls width */
6718 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
6723 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6724 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
6726 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
6730 /* Despite what the MS docs say, if this is not the last
6731 column, then MS resizes the column to the width of the
6732 largest text string in the column, including headers
6733 and items. This is different from LVSCW_AUTOSIZE in that
6734 LVSCW_AUTOSIZE ignores the header string length. */
6737 /* retrieve header text */
6738 hdi
.mask
= HDI_TEXT
;
6739 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
6740 hdi
.pszText
= szDispText
;
6741 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, (LPARAM
)&hdi
))
6743 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
6744 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
6747 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
6748 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
6749 /* FIXME: Take into account the header image, if one is present */
6750 SelectObject(hdc
, old_font
);
6751 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6753 cx
= max (cx
, max_cx
);
6757 if (cx
< 0) return FALSE
;
6759 /* call header to update the column change */
6760 hdi
.mask
= HDI_WIDTH
;
6762 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
6763 return Header_SetItemW(infoPtr
->hwndHeader
, nColumn
, (LPARAM
)&hdi
);
6767 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6770 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO
*infoPtr
)
6773 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
6775 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
6776 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
6779 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
6780 ILC_COLOR
| ILC_MASK
, 2, 2);
6781 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
6782 hdc
= CreateCompatibleDC(hdc_wnd
);
6783 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
6784 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
6785 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
6787 rc
.left
= rc
.top
= 0;
6788 rc
.right
= GetSystemMetrics(SM_CXSMICON
);
6789 rc
.bottom
= GetSystemMetrics(SM_CYSMICON
);
6791 hbm_orig
= SelectObject(hdc
, hbm_mask
);
6792 FillRect(hdc
, &rc
, hbr_white
);
6793 InflateRect(&rc
, -3, -3);
6794 FillRect(hdc
, &rc
, hbr_black
);
6796 SelectObject(hdc
, hbm_im
);
6797 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
6798 SelectObject(hdc
, hbm_orig
);
6799 ImageList_Add(himl
, hbm_im
, hbm_mask
);
6801 SelectObject(hdc
, hbm_im
);
6802 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
6803 SelectObject(hdc
, hbm_orig
);
6804 ImageList_Add(himl
, hbm_im
, hbm_mask
);
6806 DeleteObject(hbm_mask
);
6807 DeleteObject(hbm_im
);
6815 * Sets the extended listview style.
6818 * [I] infoPtr : valid pointer to the listview structure
6820 * [I] dwStyle : style
6823 * SUCCESS : previous style
6826 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD dwMask
, DWORD dwStyle
)
6828 DWORD dwOldStyle
= infoPtr
->dwLvExStyle
;
6832 infoPtr
->dwLvExStyle
= (dwOldStyle
& ~dwMask
) | (dwStyle
& dwMask
);
6834 infoPtr
->dwLvExStyle
= dwStyle
;
6836 if((infoPtr
->dwLvExStyle
^ dwOldStyle
) & LVS_EX_CHECKBOXES
)
6838 HIMAGELIST himl
= 0;
6839 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
6840 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
6841 LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
6849 * Sets the new hot cursor used during hot tracking and hover selection.
6852 * [I] infoPtr : valid pointer to the listview structure
6853 * [I] hCursor : the new hot cursor handle
6856 * Returns the previous hot cursor
6858 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
6860 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
6862 infoPtr
->hHotCursor
= hCursor
;
6870 * Sets the hot item index.
6873 * [I] infoPtr : valid pointer to the listview structure
6874 * [I] iIndex : index
6877 * SUCCESS : previous hot item index
6878 * FAILURE : -1 (no hot item)
6880 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
6882 INT iOldIndex
= infoPtr
->nHotItem
;
6884 infoPtr
->nHotItem
= iIndex
;
6892 * Sets the amount of time the cursor must hover over an item before it is selected.
6895 * [I] infoPtr : valid pointer to the listview structure
6896 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6899 * Returns the previous hover time
6901 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
6903 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
6905 infoPtr
->dwHoverTime
= dwHoverTime
;
6907 return oldHoverTime
;
6912 * Sets spacing for icons of LVS_ICON style.
6915 * [I] infoPtr : valid pointer to the listview structure
6916 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6917 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6920 * MAKELONG(oldcx, oldcy)
6922 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
6924 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
6925 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6927 TRACE("requested=(%d,%d)\n", cx
, cy
);
6929 /* this is supported only for LVS_ICON style */
6930 if (uView
!= LVS_ICON
) return oldspacing
;
6932 /* set to defaults, if instructed to */
6933 if (cx
== -1) cx
= GetSystemMetrics(SM_CXICONSPACING
);
6934 if (cy
== -1) cy
= GetSystemMetrics(SM_CYICONSPACING
);
6936 /* if 0 then compute width
6937 * FIXME: Should scan each item and determine max width of
6938 * icon or label, then make that the width */
6940 cx
= infoPtr
->iconSpacing
.cx
;
6942 /* if 0 then compute height */
6944 cy
= infoPtr
->iconSize
.cy
+ 2 * infoPtr
->ntmHeight
+
6945 ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_PADDING
;
6948 infoPtr
->iconSpacing
.cx
= cx
;
6949 infoPtr
->iconSpacing
.cy
= cy
;
6951 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6952 LOWORD(oldspacing
), HIWORD(oldspacing
), cx
, cy
,
6953 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
6954 infoPtr
->ntmHeight
);
6956 /* these depend on the iconSpacing */
6957 LISTVIEW_UpdateItemSize(infoPtr
);
6962 static inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL small
)
6966 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
6973 size
->cx
= GetSystemMetrics(small
? SM_CXSMICON
: SM_CXICON
);
6974 size
->cy
= GetSystemMetrics(small
? SM_CYSMICON
: SM_CYICON
);
6983 * [I] infoPtr : valid pointer to the listview structure
6984 * [I] nType : image list type
6985 * [I] himl : image list handle
6988 * SUCCESS : old image list
6991 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
6993 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6994 INT oldHeight
= infoPtr
->nItemHeight
;
6995 HIMAGELIST himlOld
= 0;
6997 TRACE("(nType=%d, himl=%p\n", nType
, himl
);
7002 himlOld
= infoPtr
->himlNormal
;
7003 infoPtr
->himlNormal
= himl
;
7004 if (uView
== LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
7005 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
7009 himlOld
= infoPtr
->himlSmall
;
7010 infoPtr
->himlSmall
= himl
;
7011 if (uView
!= LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
7015 himlOld
= infoPtr
->himlState
;
7016 infoPtr
->himlState
= himl
;
7017 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
7018 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
7022 ERR("Unknown icon type=%d\n", nType
);
7026 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
7027 if (infoPtr
->nItemHeight
!= oldHeight
)
7028 LISTVIEW_UpdateScroll(infoPtr
);
7035 * Preallocates memory (does *not* set the actual count of items !)
7038 * [I] infoPtr : valid pointer to the listview structure
7039 * [I] nItems : item count (projected number of items to allocate)
7040 * [I] dwFlags : update flags
7046 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
7048 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems
, dwFlags
);
7050 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
7052 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7053 INT nOldCount
= infoPtr
->nItemCount
;
7055 if (nItems
< nOldCount
)
7057 RANGE range
= { nItems
, nOldCount
};
7058 ranges_del(infoPtr
->selectionRanges
, range
);
7059 if (infoPtr
->nFocusedItem
>= nItems
)
7061 infoPtr
->nFocusedItem
= -1;
7062 SetRectEmpty(&infoPtr
->rcFocus
);
7066 infoPtr
->nItemCount
= nItems
;
7067 LISTVIEW_UpdateScroll(infoPtr
);
7069 /* the flags are valid only in ownerdata report and list modes */
7070 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
) dwFlags
= 0;
7072 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
7073 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
7075 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
7076 LISTVIEW_InvalidateList(infoPtr
);
7083 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7084 nFrom
= min(nOldCount
, nItems
);
7085 nTo
= max(nOldCount
, nItems
);
7087 if (uView
== LVS_REPORT
)
7090 rcErase
.top
= nFrom
* infoPtr
->nItemHeight
;
7091 rcErase
.right
= infoPtr
->nItemWidth
;
7092 rcErase
.bottom
= nTo
* infoPtr
->nItemHeight
;
7093 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7094 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7095 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7099 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
7101 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
7102 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
7103 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
7104 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7105 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7106 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7107 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7109 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7111 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7112 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7113 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7114 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7115 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7121 /* According to MSDN for non-LVS_OWNERDATA this is just
7122 * a performance issue. The control allocates its internal
7123 * data structures for the number of items specified. It
7124 * cuts down on the number of memory allocations. Therefore
7125 * we will just issue a WARN here
7127 WARN("for non-ownerdata performance option not implemented.\n");
7135 * Sets the position of an item.
7138 * [I] infoPtr : valid pointer to the listview structure
7139 * [I] nItem : item index
7140 * [I] pt : coordinate
7146 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, POINT pt
)
7148 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7151 TRACE("(nItem=%d, &pt=%s\n", nItem
, wine_dbgstr_point(&pt
));
7153 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
7154 !(uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)) return FALSE
;
7156 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7158 /* This point value seems to be an undocumented feature.
7159 * The best guess is that it means either at the origin,
7160 * or at true beginning of the list. I will assume the origin. */
7161 if ((pt
.x
== -1) && (pt
.y
== -1))
7164 if (uView
== LVS_ICON
)
7166 pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
7167 pt
.y
-= ICON_TOP_PADDING
;
7172 infoPtr
->bAutoarrange
= FALSE
;
7174 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, FALSE
);
7179 * Sets the state of one or many items.
7182 * [I] infoPtr : valid pointer to the listview structure
7183 * [I] nItem : item index
7184 * [I] lpLVItem : item or subitem info
7190 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
)
7192 BOOL bResult
= TRUE
;
7195 lvItem
.iItem
= nItem
;
7196 lvItem
.iSubItem
= 0;
7197 lvItem
.mask
= LVIF_STATE
;
7198 lvItem
.state
= lpLVItem
->state
;
7199 lvItem
.stateMask
= lpLVItem
->stateMask
;
7200 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
7204 /* apply to all items */
7205 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7206 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) bResult
= FALSE
;
7209 bResult
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
7212 * Update selection mark
7214 * Investigation on windows 2k showed that selection mark was updated
7215 * whenever a new selection was made, but if the selected item was
7216 * unselected it was not updated.
7218 * we are probably still not 100% accurate, but this at least sets the
7219 * proper selection mark when it is needed
7222 if (bResult
&& (lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) &&
7223 ((infoPtr
->nSelectionMark
== -1) || (lvItem
.iItem
<= infoPtr
->nSelectionMark
)))
7226 infoPtr
->nSelectionMark
= -1;
7227 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
7229 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
7231 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
7233 infoPtr
->nSelectionMark
= i
;
7237 else if (ranges_contain(infoPtr
->selectionRanges
, i
))
7239 infoPtr
->nSelectionMark
= i
;
7250 * Sets the text of an item or subitem.
7253 * [I] hwnd : window handle
7254 * [I] nItem : item index
7255 * [I] lpLVItem : item or subitem info
7256 * [I] isW : TRUE if input is Unicode
7262 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
7266 if (nItem
< 0 && nItem
>= infoPtr
->nItemCount
) return FALSE
;
7268 lvItem
.iItem
= nItem
;
7269 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
7270 lvItem
.mask
= LVIF_TEXT
;
7271 lvItem
.pszText
= lpLVItem
->pszText
;
7272 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
7274 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
7276 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
7281 * Set item index that marks the start of a multiple selection.
7284 * [I] infoPtr : valid pointer to the listview structure
7285 * [I] nIndex : index
7288 * Index number or -1 if there is no selection mark.
7290 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
7292 INT nOldIndex
= infoPtr
->nSelectionMark
;
7294 TRACE("(nIndex=%d)\n", nIndex
);
7296 infoPtr
->nSelectionMark
= nIndex
;
7303 * Sets the text background color.
7306 * [I] infoPtr : valid pointer to the listview structure
7307 * [I] clrTextBk : text background color
7313 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrTextBk
)
7315 TRACE("(clrTextBk=%lx)\n", clrTextBk
);
7317 if (infoPtr
->clrTextBk
!= clrTextBk
)
7319 infoPtr
->clrTextBk
= clrTextBk
;
7320 LISTVIEW_InvalidateList(infoPtr
);
7328 * Sets the text foreground color.
7331 * [I] infoPtr : valid pointer to the listview structure
7332 * [I] clrText : text color
7338 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF clrText
)
7340 TRACE("(clrText=%lx)\n", clrText
);
7342 if (infoPtr
->clrText
!= clrText
)
7344 infoPtr
->clrText
= clrText
;
7345 LISTVIEW_InvalidateList(infoPtr
);
7353 * Determines which listview item is located at the specified position.
7356 * [I] infoPtr : valid pointer to the listview structure
7357 * [I] hwndNewToolTip : handle to new ToolTip
7362 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
7364 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
7365 infoPtr
->hwndToolTip
= hwndNewToolTip
;
7366 return hwndOldToolTip
;
7369 /* LISTVIEW_SetUnicodeFormat */
7370 /* LISTVIEW_SetWorkAreas */
7374 * Callback internally used by LISTVIEW_SortItems()
7377 * [I] first : pointer to first ITEM_INFO to compare
7378 * [I] second : pointer to second ITEM_INFO to compare
7379 * [I] lParam : HWND of control
7382 * if first comes before second : negative
7383 * if first comes after second : positive
7384 * if first and second are equivalent : zero
7386 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
7388 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
7389 ITEM_INFO
* lv_first
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)first
, 0 );
7390 ITEM_INFO
* lv_second
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)second
, 0 );
7392 /* Forward the call to the client defined callback */
7393 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
7398 * Sorts the listview items.
7401 * [I] infoPtr : valid pointer to the listview structure
7402 * [I] pfnCompare : application-defined value
7403 * [I] lParamSort : pointer to comparision callback
7409 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
, LPARAM lParamSort
)
7411 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7414 LPVOID selectionMarkItem
;
7418 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
7420 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
7422 if (!pfnCompare
) return FALSE
;
7423 if (!infoPtr
->hdpaItems
) return FALSE
;
7425 /* if there are 0 or 1 items, there is no need to sort */
7426 if (infoPtr
->nItemCount
< 2) return TRUE
;
7428 if (infoPtr
->nFocusedItem
>= 0)
7430 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
7431 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
7432 if (lpItem
) lpItem
->state
|= LVIS_FOCUSED
;
7434 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7435 /* clear the lpItem->state for non-selected ones */
7436 /* remove the selection ranges */
7438 infoPtr
->pfnCompare
= pfnCompare
;
7439 infoPtr
->lParamSort
= lParamSort
;
7440 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
7442 /* Adjust selections and indices so that they are the way they should
7443 * be after the sort (otherwise, the list items move around, but
7444 * whatever is at the item's previous original position will be
7447 selectionMarkItem
=(infoPtr
->nSelectionMark
>=0)?DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
):NULL
;
7448 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
7450 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
7451 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
7453 if (lpItem
->state
& LVIS_SELECTED
)
7455 item
.state
= LVIS_SELECTED
;
7456 item
.stateMask
= LVIS_SELECTED
;
7457 LISTVIEW_SetItemState(infoPtr
, i
, &item
);
7459 if (lpItem
->state
& LVIS_FOCUSED
)
7461 infoPtr
->nFocusedItem
= i
;
7462 lpItem
->state
&= ~LVIS_FOCUSED
;
7465 if (selectionMarkItem
!= NULL
)
7466 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
7467 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7469 /* refresh the display */
7470 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
)
7471 LISTVIEW_InvalidateList(infoPtr
);
7478 * Update theme handle after a theme change.
7481 * [I] infoPtr : valid pointer to the listview structure
7485 * FAILURE : something else
7487 static LRESULT
LISTVIEW_ThemeChanged(LISTVIEW_INFO
*infoPtr
)
7489 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
7490 CloseThemeData(theme
);
7491 OpenThemeData(infoPtr
->hwndSelf
, themeClass
);
7497 * Updates an items or rearranges the listview control.
7500 * [I] infoPtr : valid pointer to the listview structure
7501 * [I] nItem : item index
7507 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
7509 TRACE("(nItem=%d)\n", nItem
);
7511 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
7513 /* rearrange with default alignment style */
7514 if (is_autoarrange(infoPtr
))
7515 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
7517 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
7525 * Creates the listview control.
7528 * [I] hwnd : window handle
7529 * [I] lpcs : the create parameters
7535 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
7537 LISTVIEW_INFO
*infoPtr
;
7538 UINT uView
= lpcs
->style
& LVS_TYPEMASK
;
7541 TRACE("(lpcs=%p)\n", lpcs
);
7543 /* initialize info pointer */
7544 infoPtr
= (LISTVIEW_INFO
*)Alloc(sizeof(LISTVIEW_INFO
));
7545 if (!infoPtr
) return -1;
7547 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
7549 infoPtr
->hwndSelf
= hwnd
;
7550 infoPtr
->dwStyle
= lpcs
->style
;
7551 /* determine the type of structures to use */
7552 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
7553 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
7554 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
7556 /* initialize color information */
7557 infoPtr
->clrBk
= CLR_NONE
;
7558 infoPtr
->clrText
= comctl32_color
.clrWindowText
;
7559 infoPtr
->clrTextBk
= CLR_DEFAULT
;
7560 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
7562 /* set default values */
7563 infoPtr
->nFocusedItem
= -1;
7564 infoPtr
->nSelectionMark
= -1;
7565 infoPtr
->nHotItem
= -1;
7566 infoPtr
->bRedraw
= TRUE
;
7567 infoPtr
->bNoItemMetrics
= TRUE
;
7568 infoPtr
->bDoChangeNotify
= TRUE
;
7569 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
7570 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
7571 infoPtr
->nEditLabelItem
= -1;
7572 infoPtr
->dwHoverTime
= -1; /* default system hover time */
7573 infoPtr
->nMeasureItemHeight
= 0;
7575 /* get default font (icon title) */
7576 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
7577 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
7578 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
7579 LISTVIEW_SaveTextMetrics(infoPtr
);
7582 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
,
7583 WS_CHILD
| HDS_HORZ
| (DWORD
)((LVS_NOSORTHEADER
& lpcs
->style
)?0:HDS_BUTTONS
),
7584 0, 0, 0, 0, hwnd
, NULL
,
7585 lpcs
->hInstance
, NULL
);
7586 if (!infoPtr
->hwndHeader
) goto fail
;
7588 /* set header unicode format */
7589 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, (WPARAM
)TRUE
, (LPARAM
)NULL
);
7591 /* set header font */
7592 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, (LPARAM
)TRUE
);
7594 /* allocate memory for the data structure */
7595 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
7596 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
7597 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
7598 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
7599 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
7601 /* initialize the icon sizes */
7602 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, uView
!= LVS_ICON
);
7603 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
7605 /* init item size to avoid division by 0 */
7606 LISTVIEW_UpdateItemSize (infoPtr
);
7608 if (uView
== LVS_REPORT
)
7610 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
))
7612 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
7616 /* set HDS_HIDDEN flag to hide the header bar */
7617 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
,
7618 GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
) | HDS_HIDDEN
);
7622 OpenThemeData(hwnd
, themeClass
);
7627 DestroyWindow(infoPtr
->hwndHeader
);
7628 ranges_destroy(infoPtr
->selectionRanges
);
7629 DPA_Destroy(infoPtr
->hdpaItems
);
7630 DPA_Destroy(infoPtr
->hdpaPosX
);
7631 DPA_Destroy(infoPtr
->hdpaPosY
);
7632 DPA_Destroy(infoPtr
->hdpaColumns
);
7639 * Destroys the listview control.
7642 * [I] infoPtr : valid pointer to the listview structure
7648 static LRESULT
LISTVIEW_Destroy(LISTVIEW_INFO
*infoPtr
)
7650 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
7651 CloseThemeData(theme
);
7657 * Enables the listview control.
7660 * [I] infoPtr : valid pointer to the listview structure
7661 * [I] bEnable : specifies whether to enable or disable the window
7667 static BOOL
LISTVIEW_Enable(LISTVIEW_INFO
*infoPtr
, BOOL bEnable
)
7669 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
7670 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
7676 * Erases the background of the listview control.
7679 * [I] infoPtr : valid pointer to the listview structure
7680 * [I] hdc : device context handle
7686 static inline BOOL
LISTVIEW_EraseBkgnd(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
7690 TRACE("(hdc=%p)\n", hdc
);
7692 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
7694 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
7700 * Helper function for LISTVIEW_[HV]Scroll *only*.
7701 * Performs vertical/horizontal scrolling by a give amount.
7704 * [I] infoPtr : valid pointer to the listview structure
7705 * [I] dx : amount of horizontal scroll
7706 * [I] dy : amount of vertical scroll
7708 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
7710 /* now we can scroll the list */
7711 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
7712 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
7713 /* if we have focus, adjust rect */
7714 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
7715 UpdateWindow(infoPtr
->hwndSelf
);
7720 * Performs vertical scrolling.
7723 * [I] infoPtr : valid pointer to the listview structure
7724 * [I] nScrollCode : scroll code
7725 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7726 * [I] hScrollWnd : scrollbar control window handle
7732 * SB_LINEUP/SB_LINEDOWN:
7733 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7734 * for LVS_REPORT is 1 line
7735 * for LVS_LIST cannot occur
7738 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
7739 INT nScrollDiff
, HWND hScrollWnd
)
7741 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7742 INT nOldScrollPos
, nNewScrollPos
;
7743 SCROLLINFO scrollInfo
;
7746 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
7747 debugscrollcode(nScrollCode
), nScrollDiff
);
7749 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
7751 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7752 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
7754 is_an_icon
= ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
));
7756 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
7758 nOldScrollPos
= scrollInfo
.nPos
;
7759 switch (nScrollCode
)
7765 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
7769 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
7773 nScrollDiff
= -scrollInfo
.nPage
;
7777 nScrollDiff
= scrollInfo
.nPage
;
7780 case SB_THUMBPOSITION
:
7782 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
7789 /* quit right away if pos isn't changing */
7790 if (nScrollDiff
== 0) return 0;
7792 /* calculate new position, and handle overflows */
7793 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
7794 if (nScrollDiff
> 0) {
7795 if (nNewScrollPos
< nOldScrollPos
||
7796 nNewScrollPos
> scrollInfo
.nMax
)
7797 nNewScrollPos
= scrollInfo
.nMax
;
7799 if (nNewScrollPos
> nOldScrollPos
||
7800 nNewScrollPos
< scrollInfo
.nMin
)
7801 nNewScrollPos
= scrollInfo
.nMin
;
7804 /* set the new position, and reread in case it changed */
7805 scrollInfo
.fMask
= SIF_POS
;
7806 scrollInfo
.nPos
= nNewScrollPos
;
7807 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
7809 /* carry on only if it really changed */
7810 if (nNewScrollPos
== nOldScrollPos
) return 0;
7812 /* now adjust to client coordinates */
7813 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
7814 if (uView
== LVS_REPORT
) nScrollDiff
*= infoPtr
->nItemHeight
;
7816 /* and scroll the window */
7817 scroll_list(infoPtr
, 0, nScrollDiff
);
7824 * Performs horizontal scrolling.
7827 * [I] infoPtr : valid pointer to the listview structure
7828 * [I] nScrollCode : scroll code
7829 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7830 * [I] hScrollWnd : scrollbar control window handle
7836 * SB_LINELEFT/SB_LINERIGHT:
7837 * for LVS_ICON, LVS_SMALLICON 1 pixel
7838 * for LVS_REPORT is 1 pixel
7839 * for LVS_LIST is 1 column --> which is a 1 because the
7840 * scroll is based on columns not pixels
7843 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
7844 INT nScrollDiff
, HWND hScrollWnd
)
7846 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7847 INT nOldScrollPos
, nNewScrollPos
;
7848 SCROLLINFO scrollInfo
;
7850 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
7851 debugscrollcode(nScrollCode
), nScrollDiff
);
7853 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
7855 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7856 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
7858 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
7860 nOldScrollPos
= scrollInfo
.nPos
;
7862 switch (nScrollCode
)
7876 nScrollDiff
= -scrollInfo
.nPage
;
7880 nScrollDiff
= scrollInfo
.nPage
;
7883 case SB_THUMBPOSITION
:
7885 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
7892 /* quit right away if pos isn't changing */
7893 if (nScrollDiff
== 0) return 0;
7895 /* calculate new position, and handle overflows */
7896 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
7897 if (nScrollDiff
> 0) {
7898 if (nNewScrollPos
< nOldScrollPos
||
7899 nNewScrollPos
> scrollInfo
.nMax
)
7900 nNewScrollPos
= scrollInfo
.nMax
;
7902 if (nNewScrollPos
> nOldScrollPos
||
7903 nNewScrollPos
< scrollInfo
.nMin
)
7904 nNewScrollPos
= scrollInfo
.nMin
;
7907 /* set the new position, and reread in case it changed */
7908 scrollInfo
.fMask
= SIF_POS
;
7909 scrollInfo
.nPos
= nNewScrollPos
;
7910 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
7912 /* carry on only if it really changed */
7913 if (nNewScrollPos
== nOldScrollPos
) return 0;
7915 if(uView
== LVS_REPORT
)
7916 LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
7918 /* now adjust to client coordinates */
7919 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
7920 if (uView
== LVS_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
7922 /* and scroll the window */
7923 scroll_list(infoPtr
, nScrollDiff
, 0);
7928 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
7930 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7931 INT gcWheelDelta
= 0;
7932 INT pulScrollLines
= 3;
7933 SCROLLINFO scrollInfo
;
7935 TRACE("(wheelDelta=%d)\n", wheelDelta
);
7937 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
7938 gcWheelDelta
-= wheelDelta
;
7940 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7941 scrollInfo
.fMask
= SIF_POS
;
7948 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7949 * should be fixed in the future.
7951 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (gcWheelDelta
< 0) ?
7952 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
, 0);
7956 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
7958 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
7959 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
7960 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, cLineScroll
, 0);
7965 LISTVIEW_HScroll(infoPtr
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0, 0);
7976 * [I] infoPtr : valid pointer to the listview structure
7977 * [I] nVirtualKey : virtual key
7978 * [I] lKeyData : key data
7983 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
7985 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7986 HWND hwndSelf
= infoPtr
->hwndSelf
;
7988 NMLVKEYDOWN nmKeyDown
;
7990 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey
, lKeyData
);
7992 /* send LVN_KEYDOWN notification */
7993 nmKeyDown
.wVKey
= nVirtualKey
;
7994 nmKeyDown
.flags
= 0;
7995 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
7996 if (!IsWindow(hwndSelf
))
7999 switch (nVirtualKey
)
8002 nItem
= infoPtr
->nFocusedItem
;
8006 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
8008 if (!notify(infoPtr
, NM_RETURN
)) return 0;
8009 if (!notify(infoPtr
, LVN_ITEMACTIVATE
)) return 0;
8014 if (infoPtr
->nItemCount
> 0)
8019 if (infoPtr
->nItemCount
> 0)
8020 nItem
= infoPtr
->nItemCount
- 1;
8024 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
8028 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
8032 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
8036 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
8040 if (uView
== LVS_REPORT
)
8042 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8043 if (infoPtr
->nFocusedItem
== topidx
)
8044 nItem
= topidx
- LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
8049 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
8050 * LISTVIEW_GetCountPerRow(infoPtr
);
8051 if(nItem
< 0) nItem
= 0;
8055 if (uView
== LVS_REPORT
)
8057 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8058 INT cnt
= LISTVIEW_GetCountPerColumn(infoPtr
);
8059 if (infoPtr
->nFocusedItem
== topidx
+ cnt
- 1)
8060 nItem
= infoPtr
->nFocusedItem
+ cnt
- 1;
8062 nItem
= topidx
+ cnt
- 1;
8065 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
8066 * LISTVIEW_GetCountPerRow(infoPtr
);
8067 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
8071 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
|| nVirtualKey
== VK_SPACE
))
8072 LISTVIEW_KeySelection(infoPtr
, nItem
);
8082 * [I] infoPtr : valid pointer to the listview structure
8087 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
8091 /* if we did not have the focus, there's nothing to do */
8092 if (!infoPtr
->bFocus
) return 0;
8094 /* send NM_KILLFOCUS notification */
8095 if (!notify(infoPtr
, NM_KILLFOCUS
)) return 0;
8097 /* if we have a focus rectagle, get rid of it */
8098 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
8100 /* set window focus flag */
8101 infoPtr
->bFocus
= FALSE
;
8103 /* invalidate the selected items before reseting focus flag */
8104 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8111 * Processes double click messages (left mouse button).
8114 * [I] infoPtr : valid pointer to the listview structure
8115 * [I] wKey : key flag
8116 * [I] x,y : mouse coordinate
8121 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8123 LVHITTESTINFO htInfo
;
8125 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8127 /* send NM_RELEASEDCAPTURE notification */
8128 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8133 /* send NM_DBLCLK notification */
8134 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
8135 if (!notify_click(infoPtr
, NM_DBLCLK
, &htInfo
)) return 0;
8137 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8138 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
8145 * Processes mouse down messages (left mouse button).
8148 * infoPtr [I ] valid pointer to the listview structure
8149 * wKey [I ] key flag
8150 * x,y [I ] mouse coordinate
8155 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8157 LVHITTESTINFO lvHitTestInfo
;
8158 static BOOL bGroupSelect
= TRUE
;
8159 POINT pt
= { x
, y
};
8162 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8164 /* send NM_RELEASEDCAPTURE notification */
8165 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8167 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
8169 /* set left button down flag and record the click position */
8170 infoPtr
->bLButtonDown
= TRUE
;
8171 infoPtr
->ptClickPos
= pt
;
8173 lvHitTestInfo
.pt
.x
= x
;
8174 lvHitTestInfo
.pt
.y
= y
;
8176 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8177 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt
), nItem
);
8178 infoPtr
->nEditLabelItem
= -1;
8179 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8181 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
8183 DWORD state
= STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_STATEIMAGEMASK
));
8184 if(state
== 1 || state
== 2)
8188 lvitem
.state
= INDEXTOSTATEIMAGEMASK(state
);
8189 lvitem
.stateMask
= LVIS_STATEIMAGEMASK
;
8190 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvitem
);
8195 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
8197 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8198 infoPtr
->nEditLabelItem
= nItem
;
8200 LISTVIEW_SetSelection(infoPtr
, nItem
);
8204 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
8208 if (!LISTVIEW_AddGroupSelection(infoPtr
, nItem
)) return 0;
8209 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8210 infoPtr
->nSelectionMark
= nItem
;
8216 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
8217 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8219 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
8220 infoPtr
->nSelectionMark
= nItem
;
8223 else if (wKey
& MK_CONTROL
)
8227 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
8229 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
8230 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8231 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
8232 infoPtr
->nSelectionMark
= nItem
;
8234 else if (wKey
& MK_SHIFT
)
8236 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
8240 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8241 infoPtr
->nEditLabelItem
= nItem
;
8243 /* set selection (clears other pre-existing selections) */
8244 LISTVIEW_SetSelection(infoPtr
, nItem
);
8250 /* remove all selections */
8251 LISTVIEW_DeselectAll(infoPtr
);
8260 * Processes mouse up messages (left mouse button).
8263 * infoPtr [I ] valid pointer to the listview structure
8264 * wKey [I ] key flag
8265 * x,y [I ] mouse coordinate
8270 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8272 LVHITTESTINFO lvHitTestInfo
;
8274 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8276 if (!infoPtr
->bLButtonDown
) return 0;
8278 lvHitTestInfo
.pt
.x
= x
;
8279 lvHitTestInfo
.pt
.y
= y
;
8281 /* send NM_CLICK notification */
8282 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8283 if (!notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
)) return 0;
8285 /* set left button flag */
8286 infoPtr
->bLButtonDown
= FALSE
;
8288 /* if we clicked on a selected item, edit the label */
8289 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
8290 LISTVIEW_EditLabelT(infoPtr
, lvHitTestInfo
.iItem
, TRUE
);
8297 * Destroys the listview control (called after WM_DESTROY).
8300 * [I] infoPtr : valid pointer to the listview structure
8305 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
8309 /* delete all items */
8310 LISTVIEW_DeleteAllItems(infoPtr
);
8312 /* destroy data structure */
8313 DPA_Destroy(infoPtr
->hdpaItems
);
8314 DPA_Destroy(infoPtr
->hdpaPosX
);
8315 DPA_Destroy(infoPtr
->hdpaPosY
);
8316 DPA_Destroy(infoPtr
->hdpaColumns
);
8317 ranges_destroy(infoPtr
->selectionRanges
);
8319 /* destroy image lists */
8320 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
8322 if (infoPtr
->himlNormal
)
8323 ImageList_Destroy(infoPtr
->himlNormal
);
8324 if (infoPtr
->himlSmall
)
8325 ImageList_Destroy(infoPtr
->himlSmall
);
8326 if (infoPtr
->himlState
)
8327 ImageList_Destroy(infoPtr
->himlState
);
8330 /* destroy font, bkgnd brush */
8332 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
8333 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
8335 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
8337 /* free listview info pointer*/
8345 * Handles notifications from header.
8348 * [I] infoPtr : valid pointer to the listview structure
8349 * [I] nCtrlId : control identifier
8350 * [I] lpnmh : notification information
8355 static LRESULT
LISTVIEW_HeaderNotification(LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
8357 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8358 HWND hwndSelf
= infoPtr
->hwndSelf
;
8360 TRACE("(lpnmh=%p)\n", lpnmh
);
8362 if (!lpnmh
|| lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
8364 switch (lpnmh
->hdr
.code
)
8366 case HDN_ITEMCHANGINGW
:
8367 case HDN_ITEMCHANGINGA
:
8368 return notify_forward_header(infoPtr
, lpnmh
);
8369 case HDN_ITEMCHANGEDW
:
8370 case HDN_ITEMCHANGEDA
:
8371 notify_forward_header(infoPtr
, lpnmh
);
8372 if (!IsWindow(hwndSelf
))
8378 COLUMN_INFO
*lpColumnInfo
;
8381 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8385 hdi
.mask
= HDI_WIDTH
;
8386 if (!Header_GetItemW(infoPtr
->hwndHeader
, lpnmh
->iItem
, (LPARAM
)&hdi
)) return 0;
8390 cxy
= lpnmh
->pitem
->cxy
;
8392 /* determine how much we change since the last know position */
8393 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8394 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
8397 lpColumnInfo
->rcHeader
.right
+= dx
;
8398 LISTVIEW_ScrollColumns(infoPtr
, lpnmh
->iItem
+ 1, dx
);
8399 LISTVIEW_UpdateItemSize(infoPtr
);
8400 if (uView
== LVS_REPORT
&& is_redrawing(infoPtr
))
8403 RECT rcCol
= lpColumnInfo
->rcHeader
;
8405 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
8406 OffsetRect(&rcCol
, ptOrigin
.x
, 0);
8408 rcCol
.top
= infoPtr
->rcList
.top
;
8409 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
8411 /* resizing left-aligned columns leaves most of the left side untouched */
8412 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
8414 INT nMaxDirty
= infoPtr
->nEllipsisWidth
+ infoPtr
->ntmMaxCharWidth
+ dx
;
8415 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- nMaxDirty
);
8418 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
8424 case HDN_ITEMCLICKW
:
8425 case HDN_ITEMCLICKA
:
8427 /* Handle sorting by Header Column */
8430 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
8432 nmlv
.iSubItem
= lpnmh
->iItem
;
8433 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
8437 case HDN_DIVIDERDBLCLICKW
:
8438 case HDN_DIVIDERDBLCLICKA
:
8439 LISTVIEW_SetColumnWidth(infoPtr
, lpnmh
->iItem
, LVSCW_AUTOSIZE
);
8448 * Paint non-client area of control.
8451 * [I] infoPtr : valid pointer to the listview structureof the sender
8452 * [I] region : update region
8455 * TRUE - frame was painted
8456 * FALSE - call default window proc
8458 static BOOL
LISTVIEW_NCPaint(LISTVIEW_INFO
*infoPtr
, HRGN region
)
8460 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
8464 int cxEdge
= GetSystemMetrics (SM_CXEDGE
),
8465 cyEdge
= GetSystemMetrics (SM_CYEDGE
);
8467 if (!theme
) return FALSE
;
8469 GetWindowRect(infoPtr
->hwndSelf
, &r
);
8471 cliprgn
= CreateRectRgn (r
.left
+ cxEdge
, r
.top
+ cyEdge
,
8472 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
8473 if (region
!= (HRGN
)1)
8474 CombineRgn (cliprgn
, cliprgn
, region
, RGN_AND
);
8475 OffsetRect(&r
, -r
.left
, -r
.top
);
8477 dc
= GetDCEx(infoPtr
->hwndSelf
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
8478 OffsetRect(&r
, -r
.left
, -r
.top
);
8480 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
8481 DrawThemeParentBackground(infoPtr
->hwndSelf
, dc
, &r
);
8482 DrawThemeBackground (theme
, dc
, 0, 0, &r
, 0);
8483 ReleaseDC(infoPtr
->hwndSelf
, dc
);
8485 /* Call default proc to get the scrollbars etc. painted */
8486 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)cliprgn
, 0);
8493 * Determines the type of structure to use.
8496 * [I] infoPtr : valid pointer to the listview structureof the sender
8497 * [I] hwndFrom : listview window handle
8498 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8503 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
8505 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
8507 if (nCommand
!= NF_REQUERY
) return 0;
8509 infoPtr
->notifyFormat
= SendMessageW(hwndFrom
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
8516 * Paints/Repaints the listview control.
8519 * [I] infoPtr : valid pointer to the listview structure
8520 * [I] hdc : device context handle
8525 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
8527 TRACE("(hdc=%p)\n", hdc
);
8529 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
8531 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8533 infoPtr
->bNoItemMetrics
= FALSE
;
8534 LISTVIEW_UpdateItemSize(infoPtr
);
8535 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
8536 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8537 LISTVIEW_UpdateScroll(infoPtr
);
8540 LISTVIEW_Refresh(infoPtr
, hdc
);
8545 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
8547 if (ps
.fErase
) LISTVIEW_FillBkgnd(infoPtr
, hdc
, &ps
.rcPaint
);
8548 LISTVIEW_Refresh(infoPtr
, hdc
);
8549 EndPaint(infoPtr
->hwndSelf
, &ps
);
8558 * Paints/Repaints the listview control.
8561 * [I] infoPtr : valid pointer to the listview structure
8562 * [I] hdc : device context handle
8563 * [I] options : drawing options
8568 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
8570 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc
, options
);
8572 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
8575 if (options
& PRF_ERASEBKGND
)
8576 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
8578 if (options
& PRF_CLIENT
)
8579 LISTVIEW_Paint(infoPtr
, hdc
);
8587 * Processes double click messages (right mouse button).
8590 * [I] infoPtr : valid pointer to the listview structure
8591 * [I] wKey : key flag
8592 * [I] x,y : mouse coordinate
8597 static LRESULT
LISTVIEW_RButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8599 LVHITTESTINFO lvHitTestInfo
;
8601 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
8603 /* send NM_RELEASEDCAPTURE notification */
8604 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8606 /* send NM_RDBLCLK notification */
8607 lvHitTestInfo
.pt
.x
= x
;
8608 lvHitTestInfo
.pt
.y
= y
;
8609 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8610 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
8617 * Processes mouse down messages (right mouse button).
8620 * [I] infoPtr : valid pointer to the listview structure
8621 * [I] wKey : key flag
8622 * [I] x,y : mouse coordinate
8627 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8629 LVHITTESTINFO lvHitTestInfo
;
8632 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
8634 /* send NM_RELEASEDCAPTURE notification */
8635 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8637 /* make sure the listview control window has the focus */
8638 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
8640 /* set right button down flag */
8641 infoPtr
->bRButtonDown
= TRUE
;
8643 /* determine the index of the selected item */
8644 lvHitTestInfo
.pt
.x
= x
;
8645 lvHitTestInfo
.pt
.y
= y
;
8646 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8648 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8650 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8651 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
8652 !LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8653 LISTVIEW_SetSelection(infoPtr
, nItem
);
8657 LISTVIEW_DeselectAll(infoPtr
);
8665 * Processes mouse up messages (right mouse button).
8668 * [I] infoPtr : valid pointer to the listview structure
8669 * [I] wKey : key flag
8670 * [I] x,y : mouse coordinate
8675 static LRESULT
LISTVIEW_RButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8677 LVHITTESTINFO lvHitTestInfo
;
8680 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
8682 if (!infoPtr
->bRButtonDown
) return 0;
8684 /* set button flag */
8685 infoPtr
->bRButtonDown
= FALSE
;
8687 /* Send NM_RClICK notification */
8688 lvHitTestInfo
.pt
.x
= x
;
8689 lvHitTestInfo
.pt
.y
= y
;
8690 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8691 if (!notify_click(infoPtr
, NM_RCLICK
, &lvHitTestInfo
)) return 0;
8693 /* Change to screen coordinate for WM_CONTEXTMENU */
8694 pt
= lvHitTestInfo
.pt
;
8695 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
8697 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8698 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
8699 (WPARAM
)infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
8710 * [I] infoPtr : valid pointer to the listview structure
8711 * [I] hwnd : window handle of window containing the cursor
8712 * [I] nHittest : hit-test code
8713 * [I] wMouseMsg : ideintifier of the mouse message
8716 * TRUE if cursor is set
8719 static BOOL
LISTVIEW_SetCursor(LISTVIEW_INFO
*infoPtr
, HWND hwnd
, UINT nHittest
, UINT wMouseMsg
)
8721 LVHITTESTINFO lvHitTestInfo
;
8723 if(!(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)) return FALSE
;
8725 if(!infoPtr
->hHotCursor
) return FALSE
;
8727 GetCursorPos(&lvHitTestInfo
.pt
);
8728 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) return FALSE
;
8730 SetCursor(infoPtr
->hHotCursor
);
8740 * [I] infoPtr : valid pointer to the listview structure
8741 * [I] hwndLoseFocus : handle of previously focused window
8746 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
8748 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
8750 /* if we have the focus already, there's nothing to do */
8751 if (infoPtr
->bFocus
) return 0;
8753 /* send NM_SETFOCUS notification */
8754 if (!notify(infoPtr
, NM_SETFOCUS
)) return 0;
8756 /* set window focus flag */
8757 infoPtr
->bFocus
= TRUE
;
8759 /* put the focus rect back on */
8760 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
8762 /* redraw all visible selected items */
8763 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8773 * [I] infoPtr : valid pointer to the listview structure
8774 * [I] fRedraw : font handle
8775 * [I] fRedraw : redraw flag
8780 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
8782 HFONT oldFont
= infoPtr
->hFont
;
8784 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
8786 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
8787 if (infoPtr
->hFont
== oldFont
) return 0;
8789 LISTVIEW_SaveTextMetrics(infoPtr
);
8791 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
)
8792 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
8794 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
8801 * Message handling for WM_SETREDRAW.
8802 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8805 * [I] infoPtr : valid pointer to the listview structure
8806 * [I] bRedraw: state of redraw flag
8809 * DefWinProc return value
8811 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
8813 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr
->bRedraw
, bRedraw
);
8815 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
8816 if ((infoPtr
->bRedraw
&& bRedraw
) || (!infoPtr
->bRedraw
&& !bRedraw
)) return 0;
8818 infoPtr
->bRedraw
= bRedraw
;
8820 if(!bRedraw
) return 0;
8822 if (is_autoarrange(infoPtr
))
8823 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8824 LISTVIEW_UpdateScroll(infoPtr
);
8826 /* despite what the WM_SETREDRAW docs says, apps expect us
8827 * to invalidate the listview here... stupid! */
8828 LISTVIEW_InvalidateList(infoPtr
);
8835 * Resizes the listview control. This function processes WM_SIZE
8836 * messages. At this time, the width and height are not used.
8839 * [I] infoPtr : valid pointer to the listview structure
8840 * [I] Width : new width
8841 * [I] Height : new height
8846 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
8848 RECT rcOld
= infoPtr
->rcList
;
8850 TRACE("(width=%d, height=%d)\n", Width
, Height
);
8852 LISTVIEW_UpdateSize(infoPtr
);
8853 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
8855 /* do not bother with display related stuff if we're not redrawing */
8856 if (!is_redrawing(infoPtr
)) return 0;
8858 if (is_autoarrange(infoPtr
))
8859 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8861 LISTVIEW_UpdateScroll(infoPtr
);
8863 /* refresh all only for lists whose height changed significantly */
8864 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
&&
8865 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
8866 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
8867 LISTVIEW_InvalidateList(infoPtr
);
8874 * Sets the size information.
8877 * [I] infoPtr : valid pointer to the listview structure
8882 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
8884 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8886 TRACE("uView=%d, rcList(old)=%s\n", uView
, wine_dbgstr_rect(&infoPtr
->rcList
));
8888 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
8890 if (uView
== LVS_LIST
)
8892 /* Apparently the "LIST" style is supposed to have the same
8893 * number of items in a column even if there is no scroll bar.
8894 * Since if a scroll bar already exists then the bottom is already
8895 * reduced, only reduce if the scroll bar does not currently exist.
8896 * The "2" is there to mimic the native control. I think it may be
8897 * related to either padding or edges. (GLA 7/2002)
8899 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_HSCROLL
))
8900 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
8901 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
8903 else if (uView
== LVS_REPORT
&& !(infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
))
8908 hl
.prc
= &infoPtr
->rcList
;
8910 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
8912 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
8914 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
8917 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr
->rcList
));
8922 * Processes WM_STYLECHANGED messages.
8925 * [I] infoPtr : valid pointer to the listview structure
8926 * [I] wStyleType : window style type (normal or extended)
8927 * [I] lpss : window style information
8932 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
8933 const STYLESTRUCT
*lpss
)
8935 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
8936 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
8938 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8939 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
8941 if (wStyleType
!= GWL_STYLE
) return 0;
8943 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8944 /* what if LVS_OWNERDATA changed? */
8945 /* or LVS_SINGLESEL */
8946 /* or LVS_SORT{AS,DES}CENDING */
8948 infoPtr
->dwStyle
= lpss
->styleNew
;
8950 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
8951 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
8952 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
8954 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
8955 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
8956 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
8958 if (uNewView
!= uOldView
)
8960 SIZE oldIconSize
= infoPtr
->iconSize
;
8963 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8964 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
8966 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
8967 SetRectEmpty(&infoPtr
->rcFocus
);
8969 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
8970 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
8972 if (uNewView
== LVS_ICON
)
8974 if ((infoPtr
->iconSize
.cx
!= oldIconSize
.cx
) || (infoPtr
->iconSize
.cy
!= oldIconSize
.cy
))
8976 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8977 oldIconSize
.cx
, oldIconSize
.cy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
8978 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
8981 else if (uNewView
== LVS_REPORT
)
8986 hl
.prc
= &infoPtr
->rcList
;
8988 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
8989 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
8992 LISTVIEW_UpdateItemSize(infoPtr
);
8995 if (uNewView
== LVS_REPORT
)
8996 ShowWindow(infoPtr
->hwndHeader
, (lpss
->styleNew
& LVS_NOCOLUMNHEADER
) ? SW_HIDE
: SW_SHOWNORMAL
);
8998 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
8999 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
9000 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9002 /* update the size of the client area */
9003 LISTVIEW_UpdateSize(infoPtr
);
9005 /* add scrollbars if needed */
9006 LISTVIEW_UpdateScroll(infoPtr
);
9008 /* invalidate client area + erase background */
9009 LISTVIEW_InvalidateList(infoPtr
);
9016 * Window procedure of the listview control.
9019 static LRESULT WINAPI
9020 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9022 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9024 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg
, wParam
, lParam
);
9026 if (!infoPtr
&& (uMsg
!= WM_CREATE
))
9027 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9031 case LVM_APPROXIMATEVIEWRECT
:
9032 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
9033 LOWORD(lParam
), HIWORD(lParam
));
9035 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
9037 /* case LVM_CANCELEDITLABEL: */
9039 case LVM_CREATEDRAGIMAGE
:
9040 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9042 case LVM_DELETEALLITEMS
:
9043 return LISTVIEW_DeleteAllItems(infoPtr
);
9045 case LVM_DELETECOLUMN
:
9046 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
9048 case LVM_DELETEITEM
:
9049 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
9051 case LVM_EDITLABELW
:
9052 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, TRUE
);
9054 case LVM_EDITLABELA
:
9055 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, FALSE
);
9057 /* case LVM_ENABLEGROUPVIEW: */
9059 case LVM_ENSUREVISIBLE
:
9060 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
9063 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
9066 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
9068 case LVM_GETBKCOLOR
:
9069 return infoPtr
->clrBk
;
9071 /* case LVM_GETBKIMAGE: */
9073 case LVM_GETCALLBACKMASK
:
9074 return infoPtr
->uCallbackMask
;
9076 case LVM_GETCOLUMNA
:
9077 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9079 case LVM_GETCOLUMNW
:
9080 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9082 case LVM_GETCOLUMNORDERARRAY
:
9083 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9085 case LVM_GETCOLUMNWIDTH
:
9086 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
9088 case LVM_GETCOUNTPERPAGE
:
9089 return LISTVIEW_GetCountPerPage(infoPtr
);
9091 case LVM_GETEDITCONTROL
:
9092 return (LRESULT
)infoPtr
->hwndEdit
;
9094 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
9095 return infoPtr
->dwLvExStyle
;
9097 /* case LVM_GETGROUPINFO: */
9099 /* case LVM_GETGROUPMETRICS: */
9102 return (LRESULT
)infoPtr
->hwndHeader
;
9104 case LVM_GETHOTCURSOR
:
9105 return (LRESULT
)infoPtr
->hHotCursor
;
9107 case LVM_GETHOTITEM
:
9108 return infoPtr
->nHotItem
;
9110 case LVM_GETHOVERTIME
:
9111 return infoPtr
->dwHoverTime
;
9113 case LVM_GETIMAGELIST
:
9114 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
9116 /* case LVM_GETINSERTMARK: */
9118 /* case LVM_GETINSERTMARKCOLOR: */
9120 /* case LVM_GETINSERTMARKRECT: */
9122 case LVM_GETISEARCHSTRINGA
:
9123 case LVM_GETISEARCHSTRINGW
:
9124 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9128 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9131 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9133 case LVM_GETITEMCOUNT
:
9134 return infoPtr
->nItemCount
;
9136 case LVM_GETITEMPOSITION
:
9137 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9139 case LVM_GETITEMRECT
:
9140 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
9142 case LVM_GETITEMSPACING
:
9143 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
9145 case LVM_GETITEMSTATE
:
9146 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
9148 case LVM_GETITEMTEXTA
:
9149 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9151 case LVM_GETITEMTEXTW
:
9152 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9154 case LVM_GETNEXTITEM
:
9155 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
9157 case LVM_GETNUMBEROFWORKAREAS
:
9158 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9162 if (!lParam
) return FALSE
;
9163 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
9166 /* case LVM_GETOUTLINECOLOR: */
9168 /* case LVM_GETSELECTEDCOLUMN: */
9170 case LVM_GETSELECTEDCOUNT
:
9171 return LISTVIEW_GetSelectedCount(infoPtr
);
9173 case LVM_GETSELECTIONMARK
:
9174 return infoPtr
->nSelectionMark
;
9176 case LVM_GETSTRINGWIDTHA
:
9177 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, FALSE
);
9179 case LVM_GETSTRINGWIDTHW
:
9180 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, TRUE
);
9182 case LVM_GETSUBITEMRECT
:
9183 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
9185 case LVM_GETTEXTBKCOLOR
:
9186 return infoPtr
->clrTextBk
;
9188 case LVM_GETTEXTCOLOR
:
9189 return infoPtr
->clrText
;
9191 /* case LVM_GETTILEINFO: */
9193 /* case LVM_GETTILEVIEWINFO: */
9195 case LVM_GETTOOLTIPS
:
9196 if( !infoPtr
->hwndToolTip
)
9197 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
9198 return (LRESULT
)infoPtr
->hwndToolTip
;
9200 case LVM_GETTOPINDEX
:
9201 return LISTVIEW_GetTopIndex(infoPtr
);
9203 /*case LVM_GETUNICODEFORMAT:
9204 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9207 /* case LVM_GETVIEW: */
9209 case LVM_GETVIEWRECT
:
9210 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
9212 case LVM_GETWORKAREAS
:
9213 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9216 /* case LVM_HASGROUP: */
9219 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, FALSE
);
9221 case LVM_INSERTCOLUMNA
:
9222 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9224 case LVM_INSERTCOLUMNW
:
9225 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9227 /* case LVM_INSERTGROUP: */
9229 /* case LVM_INSERTGROUPSORTED: */
9231 case LVM_INSERTITEMA
:
9232 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9234 case LVM_INSERTITEMW
:
9235 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9237 /* case LVM_INSERTMARKHITTEST: */
9239 /* case LVM_ISGROUPVIEWENABLED: */
9241 /* case LVM_MAPIDTOINDEX: */
9243 /* case LVM_MAPINDEXTOID: */
9245 /* case LVM_MOVEGROUP: */
9247 /* case LVM_MOVEITEMTOGROUP: */
9249 case LVM_REDRAWITEMS
:
9250 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9252 /* case LVM_REMOVEALLGROUPS: */
9254 /* case LVM_REMOVEGROUP: */
9257 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9259 case LVM_SETBKCOLOR
:
9260 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
9262 /* case LVM_SETBKIMAGE: */
9264 case LVM_SETCALLBACKMASK
:
9265 infoPtr
->uCallbackMask
= (UINT
)wParam
;
9268 case LVM_SETCOLUMNA
:
9269 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9271 case LVM_SETCOLUMNW
:
9272 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9274 case LVM_SETCOLUMNORDERARRAY
:
9275 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9277 case LVM_SETCOLUMNWIDTH
:
9278 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
9280 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
9281 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
9283 /* case LVM_SETGROUPINFO: */
9285 /* case LVM_SETGROUPMETRICS: */
9287 case LVM_SETHOTCURSOR
:
9288 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
9290 case LVM_SETHOTITEM
:
9291 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
9293 case LVM_SETHOVERTIME
:
9294 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)wParam
);
9296 case LVM_SETICONSPACING
:
9297 return LISTVIEW_SetIconSpacing(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
9299 case LVM_SETIMAGELIST
:
9300 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
9302 /* case LVM_SETINFOTIP: */
9304 /* case LVM_SETINSERTMARK: */
9306 /* case LVM_SETINSERTMARKCOLOR: */
9309 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9312 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9314 case LVM_SETITEMCOUNT
:
9315 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
9317 case LVM_SETITEMPOSITION
:
9320 pt
.x
= (short)LOWORD(lParam
);
9321 pt
.y
= (short)HIWORD(lParam
);
9322 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, pt
);
9325 case LVM_SETITEMPOSITION32
:
9326 if (lParam
== 0) return FALSE
;
9327 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, *((POINT
*)lParam
));
9329 case LVM_SETITEMSTATE
:
9330 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
9332 case LVM_SETITEMTEXTA
:
9333 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9335 case LVM_SETITEMTEXTW
:
9336 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9338 /* case LVM_SETOUTLINECOLOR: */
9340 /* case LVM_SETSELECTEDCOLUMN: */
9342 case LVM_SETSELECTIONMARK
:
9343 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
9345 case LVM_SETTEXTBKCOLOR
:
9346 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
9348 case LVM_SETTEXTCOLOR
:
9349 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
9351 /* case LVM_SETTILEINFO: */
9353 /* case LVM_SETTILEVIEWINFO: */
9355 /* case LVM_SETTILEWIDTH: */
9357 case LVM_SETTOOLTIPS
:
9358 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
9360 /* case LVM_SETUNICODEFORMAT: */
9362 /* case LVM_SETVIEW: */
9364 /* case LVM_SETWORKAREAS: */
9366 /* case LVM_SORTGROUPS: */
9369 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
);
9371 /* LVM_SORTITEMSEX: */
9373 case LVM_SUBITEMHITTEST
:
9374 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
9377 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
9380 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
9383 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
9386 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
9389 return LISTVIEW_Destroy(infoPtr
);
9392 return LISTVIEW_Enable(infoPtr
, (BOOL
)wParam
);
9395 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
9398 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
9401 return (LRESULT
)infoPtr
->hFont
;
9404 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
9407 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
9410 return LISTVIEW_KillFocus(infoPtr
);
9412 case WM_LBUTTONDBLCLK
:
9413 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9415 case WM_LBUTTONDOWN
:
9416 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9419 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9422 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9425 return LISTVIEW_MouseHover(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9428 return LISTVIEW_NCDestroy(infoPtr
);
9431 if (LISTVIEW_NCPaint(infoPtr
, (HRGN
)wParam
))
9436 if (lParam
&& ((LPNMHDR
)lParam
)->hwndFrom
== infoPtr
->hwndHeader
)
9437 return LISTVIEW_HeaderNotification(infoPtr
, (LPNMHEADERW
)lParam
);
9440 case WM_NOTIFYFORMAT
:
9441 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
9443 case WM_PRINTCLIENT
:
9444 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
9447 return LISTVIEW_Paint(infoPtr
, (HDC
)wParam
);
9449 case WM_RBUTTONDBLCLK
:
9450 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9452 case WM_RBUTTONDOWN
:
9453 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9456 return LISTVIEW_RButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9459 if(LISTVIEW_SetCursor(infoPtr
, (HWND
)wParam
, LOWORD(lParam
), HIWORD(lParam
)))
9464 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
9467 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
9470 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
9473 return LISTVIEW_Size(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
9475 case WM_STYLECHANGED
:
9476 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
9478 case WM_SYSCOLORCHANGE
:
9479 COMCTL32_RefreshSysColors();
9482 /* case WM_TIMER: */
9483 case WM_THEMECHANGED
:
9484 return LISTVIEW_ThemeChanged(infoPtr
);
9487 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
9490 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
9491 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9492 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
9494 case WM_WINDOWPOSCHANGED
:
9495 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
9497 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9498 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
9499 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
9501 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
9503 MEASUREITEMSTRUCT mis
;
9504 mis
.CtlType
= ODT_LISTVIEW
;
9505 mis
.CtlID
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
9509 mis
.itemHeight
= infoPtr
->nItemHeight
;
9510 SendMessageW(infoPtr
->hwndNotify
, WM_MEASUREITEM
, mis
.CtlID
, (LPARAM
)&mis
);
9511 if (infoPtr
->nItemHeight
!= max(mis
.itemHeight
, 1))
9512 infoPtr
->nMeasureItemHeight
= infoPtr
->nItemHeight
= max(mis
.itemHeight
, 1);
9515 LISTVIEW_UpdateSize(infoPtr
);
9516 LISTVIEW_UpdateScroll(infoPtr
);
9518 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9520 /* case WM_WININICHANGE: */
9523 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
))
9524 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg
, wParam
, lParam
);
9527 /* call default window procedure */
9528 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9535 * Registers the window class.
9543 void LISTVIEW_Register(void)
9547 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
9548 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
9549 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
9550 wndClass
.cbClsExtra
= 0;
9551 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
9552 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
9553 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
9554 wndClass
.lpszClassName
= WC_LISTVIEWW
;
9555 RegisterClassW(&wndClass
);
9560 * Unregisters the window class.
9568 void LISTVIEW_Unregister(void)
9570 UnregisterClassW(WC_LISTVIEWW
, NULL
);
9575 * Handle any WM_COMMAND messages
9578 * [I] infoPtr : valid pointer to the listview structure
9579 * [I] wParam : the first message parameter
9580 * [I] lParam : the second message parameter
9585 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
9587 switch (HIWORD(wParam
))
9592 * Adjust the edit window size
9595 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
9596 HFONT hFont
, hOldFont
= 0;
9601 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
9602 len
= GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
9603 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
9605 /* Select font to get the right dimension of the string */
9606 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
9609 hOldFont
= SelectObject(hdc
, hFont
);
9612 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
9614 TEXTMETRICW textMetric
;
9616 /* Add Extra spacing for the next character */
9617 GetTextMetricsW(hdc
, &textMetric
);
9618 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
9626 rect
.bottom
- rect
.top
,
9627 SWP_DRAWFRAME
|SWP_NOMOVE
);
9630 SelectObject(hdc
, hOldFont
);
9632 ReleaseDC(infoPtr
->hwndEdit
, hdc
);
9638 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);
9647 * Subclassed edit control windproc function
9650 * [I] hwnd : the edit window handle
9651 * [I] uMsg : the message that is to be processed
9652 * [I] wParam : first message parameter
9653 * [I] lParam : second message parameter
9654 * [I] isW : TRUE if input is Unicode
9659 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
9661 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
9662 BOOL cancel
= FALSE
;
9664 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9665 hwnd
, uMsg
, wParam
, lParam
, isW
);
9670 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
9677 WNDPROC editProc
= infoPtr
->EditWndProc
;
9678 infoPtr
->EditWndProc
= 0;
9679 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
9680 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
9684 if (VK_ESCAPE
== (INT
)wParam
)
9689 else if (VK_RETURN
== (INT
)wParam
)
9693 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
9697 if (infoPtr
->hwndEdit
)
9699 LPWSTR buffer
= NULL
;
9701 infoPtr
->hwndEdit
= 0;
9704 DWORD len
= isW
? GetWindowTextLengthW(hwnd
) : GetWindowTextLengthA(hwnd
);
9708 if ( (buffer
= Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))) )
9710 if (isW
) GetWindowTextW(hwnd
, buffer
, len
+1);
9711 else GetWindowTextA(hwnd
, (CHAR
*)buffer
, len
+1);
9715 LISTVIEW_EndEditLabelT(infoPtr
, buffer
, isW
);
9717 if (buffer
) Free(buffer
);
9721 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
9727 * Subclassed edit control Unicode windproc function
9730 * [I] hwnd : the edit window handle
9731 * [I] uMsg : the message that is to be processed
9732 * [I] wParam : first message parameter
9733 * [I] lParam : second message parameter
9737 LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9739 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
9744 * Subclassed edit control ANSI windproc function
9747 * [I] hwnd : the edit window handle
9748 * [I] uMsg : the message that is to be processed
9749 * [I] wParam : first message parameter
9750 * [I] lParam : second message parameter
9754 LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9756 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
9761 * Creates a subclassed edit cotrol
9764 * [I] infoPtr : valid pointer to the listview structure
9765 * [I] text : initial text for the edit
9766 * [I] style : the window style
9767 * [I] isW : TRUE if input is Unicode
9771 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, DWORD style
,
9772 INT x
, INT y
, INT width
, INT height
, BOOL isW
)
9774 WCHAR editName
[5] = { 'E', 'd', 'i', 't', '\0' };
9779 TEXTMETRICW textMetric
;
9780 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
9782 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text
, isW
), isW
);
9784 style
|= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
;
9785 hdc
= GetDC(infoPtr
->hwndSelf
);
9787 /* Select the font to get appropriate metric dimensions */
9788 if(infoPtr
->hFont
!= 0)
9789 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
9791 /*Get String Length in pixels */
9792 GetTextExtentPoint32W(hdc
, text
, lstrlenW(text
), &sz
);
9794 /*Add Extra spacing for the next character */
9795 GetTextMetricsW(hdc
, &textMetric
);
9796 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
9798 if(infoPtr
->hFont
!= 0)
9799 SelectObject(hdc
, hOldFont
);
9801 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
9803 hedit
= CreateWindowW(editName
, text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
9805 hedit
= CreateWindowA("Edit", (LPCSTR
)text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
9807 if (!hedit
) return 0;
9809 infoPtr
->EditWndProc
= (WNDPROC
)
9810 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
9811 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
9813 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);