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"
186 #include "commctrl.h"
187 #include "comctl32.h"
190 #include "wine/debug.h"
191 #include "wine/unicode.h"
193 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
195 /* make sure you set this to 0 for production use! */
196 #define DEBUG_RANGES 1
198 typedef struct tagCOLUMN_INFO
200 RECT rcHeader
; /* tracks the header's rectangle */
201 int fmt
; /* same as LVCOLUMN.fmt */
204 typedef struct tagITEMHDR
208 } ITEMHDR
, *LPITEMHDR
;
210 typedef struct tagSUBITEM_INFO
216 typedef struct tagITEM_INFO
224 typedef struct tagRANGE
230 typedef struct tagRANGES
235 typedef struct tagITERATOR
244 typedef struct tagLISTVIEW_INFO
251 COLORREF clrTextBkDefault
;
252 HIMAGELIST himlNormal
;
253 HIMAGELIST himlSmall
;
254 HIMAGELIST himlState
;
257 POINT ptClickPos
; /* point where the user clicked */
258 BOOL bNoItemMetrics
; /* flags if item metrics are not yet computed */
261 RANGES selectionRanges
;
266 RECT rcList
; /* This rectangle is really the window
267 * client rectangle possibly reduced by the
268 * horizontal scroll bar and/or header - see
269 * LISTVIEW_UpdateSize. This rectangle offset
270 * by the LISTVIEW_GetOrigin value is in
271 * client coordinates */
280 INT ntmHeight
; /* Some cached metrics of the font used */
281 INT ntmMaxCharWidth
; /* by the listview to draw items */
283 BOOL bRedraw
; /* Turns on/off repaints & invalidations */
284 BOOL bAutoarrange
; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
286 BOOL bDoChangeNotify
; /* send change notification messages? */
289 DWORD dwStyle
; /* the cached window GWL_STYLE */
290 DWORD dwLvExStyle
; /* extended listview style */
291 INT nItemCount
; /* the number of items in the list */
292 HDPA hdpaItems
; /* array ITEM_INFO pointers */
293 HDPA hdpaPosX
; /* maintains the (X, Y) coordinates of the */
294 HDPA hdpaPosY
; /* items in LVS_ICON, and LVS_SMALLICON modes */
295 HDPA hdpaColumns
; /* array of COLUMN_INFO pointers */
296 POINT currIconPos
; /* this is the position next icon will be placed */
297 PFNLVCOMPARE pfnCompare
;
305 DWORD cditemmode
; /* Keep the custom draw flags for an item/row */
307 DWORD lastKeyPressTimestamp
;
309 INT nSearchParamLength
;
310 WCHAR szSearchParam
[ MAX_PATH
];
312 INT nMeasureItemHeight
;
318 /* How many we debug buffer to allocate */
319 #define DEBUG_BUFFERS 20
320 /* The size of a single debug bbuffer */
321 #define DEBUG_BUFFER_SIZE 256
323 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
324 #define SB_INTERNAL -1
326 /* maximum size of a label */
327 #define DISP_TEXT_SIZE 512
329 /* padding for items in list and small icon display modes */
330 #define WIDTH_PADDING 12
332 /* padding for items in list, report and small icon display modes */
333 #define HEIGHT_PADDING 1
335 /* offset of items in report display mode */
336 #define REPORT_MARGINX 2
338 /* padding for icon in large icon display mode
339 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
340 * that HITTEST will see.
341 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
342 * ICON_TOP_PADDING - sum of the two above.
343 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
344 * LABEL_HOR_PADDING - between text and sides of box
345 * LABEL_VERT_PADDING - between bottom of text and end of box
347 * ICON_LR_PADDING - additional width above icon size.
348 * ICON_LR_HALF - half of the above value
350 #define ICON_TOP_PADDING_NOTHITABLE 2
351 #define ICON_TOP_PADDING_HITABLE 2
352 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
353 #define ICON_BOTTOM_PADDING 4
354 #define LABEL_HOR_PADDING 5
355 #define LABEL_VERT_PADDING 7
356 #define ICON_LR_PADDING 16
357 #define ICON_LR_HALF (ICON_LR_PADDING/2)
359 /* default label width for items in list and small icon display modes */
360 #define DEFAULT_LABEL_WIDTH 40
362 /* default column width for items in list display mode */
363 #define DEFAULT_COLUMN_WIDTH 128
365 /* Size of "line" scroll for V & H scrolls */
366 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
368 /* Padding betwen image and label */
369 #define IMAGE_PADDING 2
371 /* Padding behind the label */
372 #define TRAILING_LABEL_PADDING 12
373 #define TRAILING_HEADER_PADDING 11
375 /* Border for the icon caption */
376 #define CAPTION_BORDER 2
378 /* Standard DrawText flags */
379 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
380 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
381 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
383 /* Image index from state */
384 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
386 /* The time in milliseconds to reset the search in the list */
387 #define KEY_DELAY 450
389 /* Dump the LISTVIEW_INFO structure to the debug channel */
390 #define LISTVIEW_DUMP(iP) do { \
391 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
392 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
393 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
394 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
395 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
396 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
397 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
398 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
399 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
400 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
403 static const WCHAR themeClass
[] = {'L','i','s','t','V','i','e','w',0};
406 * forward declarations
408 static BOOL
LISTVIEW_GetItemT(LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
409 static void LISTVIEW_GetItemBox(LISTVIEW_INFO
*, INT
, LPRECT
);
410 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO
*, INT
, LPPOINT
);
411 static BOOL
LISTVIEW_GetItemPosition(LISTVIEW_INFO
*, INT
, LPPOINT
);
412 static BOOL
LISTVIEW_GetItemRect(LISTVIEW_INFO
*, INT
, LPRECT
);
413 static INT
LISTVIEW_GetLabelWidth(LISTVIEW_INFO
*, INT
);
414 static void LISTVIEW_GetOrigin(LISTVIEW_INFO
*, LPPOINT
);
415 static BOOL
LISTVIEW_GetViewRect(LISTVIEW_INFO
*, LPRECT
);
416 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*, INT
);
417 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*, const LVITEMW
*, BOOL
);
418 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO
*);
419 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*, INT
);
420 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
421 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*, INT
, BOOL
);
422 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*, WPARAM
, LPARAM
);
423 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*, PFNLVCOMPARE
, LPARAM
);
424 static INT
LISTVIEW_GetStringWidthT(LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
425 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
);
426 static UINT
LISTVIEW_GetItemState(LISTVIEW_INFO
*, INT
, UINT
);
427 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, const LVITEMW
*);
428 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
429 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
430 static INT
LISTVIEW_GetTopIndex(LISTVIEW_INFO
*);
431 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
432 static HWND
CreateEditLabelT(LISTVIEW_INFO
*, LPCWSTR
, DWORD
, INT
, INT
, INT
, INT
, BOOL
);
433 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*, INT
, HIMAGELIST
);
434 static INT
LISTVIEW_HitTest(LISTVIEW_INFO
*, LPLVHITTESTINFO
, BOOL
, BOOL
);
436 /******** Text handling functions *************************************/
438 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
439 * text string. The string may be ANSI or Unicode, in which case
440 * the boolean isW tells us the type of the string.
442 * The name of the function tell what type of strings it expects:
443 * W: Unicode, T: ANSI/Unicode - function of isW
446 static inline BOOL
is_textW(LPCWSTR text
)
448 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
451 static inline BOOL
is_textT(LPCWSTR text
, BOOL isW
)
453 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
454 return is_textW(text
);
457 static inline int textlenT(LPCWSTR text
, BOOL isW
)
459 return !is_textT(text
, isW
) ? 0 :
460 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
463 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
466 if (isSrcW
) lstrcpynW(dest
, src
, max
);
467 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
469 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
470 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
473 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
475 LPWSTR wstr
= (LPWSTR
)text
;
477 if (!isW
&& is_textT(text
, isW
))
479 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
480 wstr
= Alloc(len
* sizeof(WCHAR
));
481 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
483 TRACE(" wstr=%s\n", text
== LPSTR_TEXTCALLBACKW
? "(callback)" : debugstr_w(wstr
));
487 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
489 if (!isW
&& is_textT(wstr
, isW
)) Free (wstr
);
493 * dest is a pointer to a Unicode string
494 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
496 static BOOL
textsetptrT(LPWSTR
*dest
, LPWSTR src
, BOOL isW
)
500 if (src
== LPSTR_TEXTCALLBACKW
)
502 if (is_textW(*dest
)) Free(*dest
);
503 *dest
= LPSTR_TEXTCALLBACKW
;
507 LPWSTR pszText
= textdupTtoW(src
, isW
);
508 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
509 bResult
= Str_SetPtrW(dest
, pszText
);
510 textfreeT(pszText
, isW
);
516 * compares a Unicode to a Unicode/ANSI text string
518 static inline int textcmpWT(LPCWSTR aw
, LPCWSTR bt
, BOOL isW
)
520 if (!aw
) return bt
? -1 : 0;
521 if (!bt
) return aw
? 1 : 0;
522 if (aw
== LPSTR_TEXTCALLBACKW
)
523 return bt
== LPSTR_TEXTCALLBACKW
? 0 : -1;
524 if (bt
!= LPSTR_TEXTCALLBACKW
)
526 LPWSTR bw
= textdupTtoW(bt
, isW
);
527 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
535 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
539 n
= min(min(n
, strlenW(s1
)), strlenW(s2
));
540 res
= CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
);
541 return res
? res
- sizeof(WCHAR
) : res
;
544 /******** Debugging functions *****************************************/
546 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
548 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
549 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
552 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
554 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
555 n
= min(textlenT(text
, isW
), n
);
556 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
559 static char* debug_getbuf(void)
561 static int index
= 0;
562 static char buffers
[DEBUG_BUFFERS
][DEBUG_BUFFER_SIZE
];
563 return buffers
[index
++ % DEBUG_BUFFERS
];
566 static inline const char* debugrange(const RANGE
*lprng
)
570 char* buf
= debug_getbuf();
571 snprintf(buf
, DEBUG_BUFFER_SIZE
, "[%d, %d)", lprng
->lower
, lprng
->upper
);
573 } else return "(null)";
576 static inline const char* debugpoint(const POINT
*lppt
)
580 char* buf
= debug_getbuf();
581 snprintf(buf
, DEBUG_BUFFER_SIZE
, "(%ld, %ld)", lppt
->x
, lppt
->y
);
583 } else return "(null)";
586 static inline const char* debugrect(const RECT
*rect
)
590 char* buf
= debug_getbuf();
591 snprintf(buf
, DEBUG_BUFFER_SIZE
, "[(%ld, %ld);(%ld, %ld)]",
592 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
594 } else return "(null)";
597 static const char* debugscrollinfo(const SCROLLINFO
*pScrollInfo
)
599 char* buf
= debug_getbuf(), *text
= buf
;
600 int len
, size
= DEBUG_BUFFER_SIZE
;
602 if (pScrollInfo
== NULL
) return "(null)";
603 len
= snprintf(buf
, size
, "{cbSize=%d, ", pScrollInfo
->cbSize
);
604 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
605 if (pScrollInfo
->fMask
& SIF_RANGE
)
606 len
= snprintf(buf
, size
, "nMin=%d, nMax=%d, ", pScrollInfo
->nMin
, pScrollInfo
->nMax
);
608 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
609 if (pScrollInfo
->fMask
& SIF_PAGE
)
610 len
= snprintf(buf
, size
, "nPage=%u, ", pScrollInfo
->nPage
);
612 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
613 if (pScrollInfo
->fMask
& SIF_POS
)
614 len
= snprintf(buf
, size
, "nPos=%d, ", pScrollInfo
->nPos
);
616 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
617 if (pScrollInfo
->fMask
& SIF_TRACKPOS
)
618 len
= snprintf(buf
, size
, "nTrackPos=%d, ", pScrollInfo
->nTrackPos
);
620 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
623 buf
= text
+ strlen(text
);
625 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
629 static const char* debugnmlistview(const NMLISTVIEW
*plvnm
)
633 char* buf
= debug_getbuf();
634 snprintf(buf
, DEBUG_BUFFER_SIZE
, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
635 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
636 plvnm
->iItem
, plvnm
->iSubItem
, plvnm
->uNewState
, plvnm
->uOldState
,
637 plvnm
->uChanged
, debugpoint(&plvnm
->ptAction
), plvnm
->lParam
);
639 } else return "(null)";
642 static const char* debuglvitem_t(const LVITEMW
*lpLVItem
, BOOL isW
)
644 char* buf
= debug_getbuf(), *text
= buf
;
645 int len
, size
= DEBUG_BUFFER_SIZE
;
647 if (lpLVItem
== NULL
) return "(null)";
648 len
= snprintf(buf
, size
, "{iItem=%d, iSubItem=%d, ", lpLVItem
->iItem
, lpLVItem
->iSubItem
);
649 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
650 if (lpLVItem
->mask
& LVIF_STATE
)
651 len
= snprintf(buf
, size
, "state=%x, stateMask=%x, ", lpLVItem
->state
, lpLVItem
->stateMask
);
653 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
654 if (lpLVItem
->mask
& LVIF_TEXT
)
655 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem
->pszText
, isW
, 80), lpLVItem
->cchTextMax
);
657 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
658 if (lpLVItem
->mask
& LVIF_IMAGE
)
659 len
= snprintf(buf
, size
, "iImage=%d, ", lpLVItem
->iImage
);
661 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
662 if (lpLVItem
->mask
& LVIF_PARAM
)
663 len
= snprintf(buf
, size
, "lParam=%lx, ", lpLVItem
->lParam
);
665 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
666 if (lpLVItem
->mask
& LVIF_INDENT
)
667 len
= snprintf(buf
, size
, "iIndent=%d, ", lpLVItem
->iIndent
);
669 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
672 buf
= text
+ strlen(text
);
674 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
678 static const char* debuglvcolumn_t(const LVCOLUMNW
*lpColumn
, BOOL isW
)
680 char* buf
= debug_getbuf(), *text
= buf
;
681 int len
, size
= DEBUG_BUFFER_SIZE
;
683 if (lpColumn
== NULL
) return "(null)";
684 len
= snprintf(buf
, size
, "{");
685 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
686 if (lpColumn
->mask
& LVCF_SUBITEM
)
687 len
= snprintf(buf
, size
, "iSubItem=%d, ", lpColumn
->iSubItem
);
689 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
690 if (lpColumn
->mask
& LVCF_FMT
)
691 len
= snprintf(buf
, size
, "fmt=%x, ", lpColumn
->fmt
);
693 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
694 if (lpColumn
->mask
& LVCF_WIDTH
)
695 len
= snprintf(buf
, size
, "cx=%d, ", lpColumn
->cx
);
697 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
698 if (lpColumn
->mask
& LVCF_TEXT
)
699 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn
->pszText
, isW
, 80), lpColumn
->cchTextMax
);
701 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
702 if (lpColumn
->mask
& LVCF_IMAGE
)
703 len
= snprintf(buf
, size
, "iImage=%d, ", lpColumn
->iImage
);
705 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
706 if (lpColumn
->mask
& LVCF_ORDER
)
707 len
= snprintf(buf
, size
, "iOrder=%d, ", lpColumn
->iOrder
);
709 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
712 buf
= text
+ strlen(text
);
714 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
718 static const char* debuglvhittestinfo(const LVHITTESTINFO
*lpht
)
722 char* buf
= debug_getbuf();
723 snprintf(buf
, DEBUG_BUFFER_SIZE
, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
724 debugpoint(&lpht
->pt
), lpht
->flags
, lpht
->iItem
, lpht
->iSubItem
);
726 } else return "(null)";
729 /* Return the corresponding text for a given scroll value */
730 static inline LPCSTR
debugscrollcode(int nScrollCode
)
734 case SB_LINELEFT
: return "SB_LINELEFT";
735 case SB_LINERIGHT
: return "SB_LINERIGHT";
736 case SB_PAGELEFT
: return "SB_PAGELEFT";
737 case SB_PAGERIGHT
: return "SB_PAGERIGHT";
738 case SB_THUMBPOSITION
: return "SB_THUMBPOSITION";
739 case SB_THUMBTRACK
: return "SB_THUMBTRACK";
740 case SB_ENDSCROLL
: return "SB_ENDSCROLL";
741 case SB_INTERNAL
: return "SB_INTERNAL";
742 default: return "unknown";
747 /******** Notification functions i************************************/
749 static LRESULT
notify_forward_header(LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
751 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
752 (WPARAM
)lpnmh
->hdr
.idFrom
, (LPARAM
)lpnmh
);
755 static LRESULT
notify_hdr(LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
759 TRACE("(code=%d)\n", code
);
761 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
762 pnmh
->idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
764 result
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
765 (WPARAM
)pnmh
->idFrom
, (LPARAM
)pnmh
);
767 TRACE(" <= %ld\n", result
);
772 static inline BOOL
notify(LISTVIEW_INFO
*infoPtr
, INT code
)
775 HWND hwnd
= infoPtr
->hwndSelf
;
776 notify_hdr(infoPtr
, code
, &nmh
);
777 return IsWindow(hwnd
);
780 static inline void notify_itemactivate(LISTVIEW_INFO
*infoPtr
, LVHITTESTINFO
*htInfo
)
791 item
.mask
= LVIF_PARAM
|LVIF_STATE
;
792 item
.iItem
= htInfo
->iItem
;
794 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) {
795 nmia
.lParam
= item
.lParam
;
796 nmia
.uOldState
= item
.state
;
797 nmia
.uNewState
= item
.state
| LVIS_ACTIVATING
;
798 nmia
.uChanged
= LVIF_STATE
;
801 nmia
.iItem
= htInfo
->iItem
;
802 nmia
.iSubItem
= htInfo
->iSubItem
;
803 nmia
.ptAction
= htInfo
->pt
;
805 if (GetKeyState(VK_SHIFT
) & 0x8000) nmia
.uKeyFlags
|= LVKF_SHIFT
;
806 if (GetKeyState(VK_CONTROL
) & 0x8000) nmia
.uKeyFlags
|= LVKF_CONTROL
;
807 if (GetKeyState(VK_MENU
) & 0x8000) nmia
.uKeyFlags
|= LVKF_ALT
;
809 notify_hdr(infoPtr
, LVN_ITEMACTIVATE
, (LPNMHDR
)&nmia
);
812 static inline LRESULT
notify_listview(LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
814 TRACE("(code=%d, plvnm=%s)\n", code
, debugnmlistview(plvnm
));
815 return notify_hdr(infoPtr
, code
, (LPNMHDR
)plvnm
);
818 static BOOL
notify_click(LISTVIEW_INFO
*infoPtr
, INT code
, LVHITTESTINFO
*lvht
)
822 HWND hwnd
= infoPtr
->hwndSelf
;
824 TRACE("code=%d, lvht=%s\n", code
, debuglvhittestinfo(lvht
));
825 ZeroMemory(&nmlv
, sizeof(nmlv
));
826 nmlv
.iItem
= lvht
->iItem
;
827 nmlv
.iSubItem
= lvht
->iSubItem
;
828 nmlv
.ptAction
= lvht
->pt
;
829 item
.mask
= LVIF_PARAM
;
830 item
.iItem
= lvht
->iItem
;
832 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
833 notify_listview(infoPtr
, code
, &nmlv
);
834 return IsWindow(hwnd
);
837 static BOOL
notify_deleteitem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
841 HWND hwnd
= infoPtr
->hwndSelf
;
843 ZeroMemory(&nmlv
, sizeof (NMLISTVIEW
));
845 item
.mask
= LVIF_PARAM
;
848 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
849 notify_listview(infoPtr
, LVN_DELETEITEM
, &nmlv
);
850 return IsWindow(hwnd
);
853 static int get_ansi_notification(INT unicodeNotificationCode
)
855 switch (unicodeNotificationCode
)
857 case LVN_BEGINLABELEDITW
: return LVN_BEGINLABELEDITA
;
858 case LVN_ENDLABELEDITW
: return LVN_ENDLABELEDITA
;
859 case LVN_GETDISPINFOW
: return LVN_GETDISPINFOA
;
860 case LVN_SETDISPINFOW
: return LVN_SETDISPINFOA
;
861 case LVN_ODFINDITEMW
: return LVN_ODFINDITEMA
;
862 case LVN_GETINFOTIPW
: return LVN_GETINFOTIPA
;
864 ERR("unknown notification %x\n", unicodeNotificationCode
);
870 Send notification. depends on dispinfoW having same
871 structure as dispinfoA.
872 infoPtr : listview struct
873 notificationCode : *Unicode* notification code
874 pdi : dispinfo structure (can be unicode or ansi)
875 isW : TRUE if dispinfo is Unicode
877 static BOOL
notify_dispinfoT(LISTVIEW_INFO
*infoPtr
, INT notificationCode
, LPNMLVDISPINFOW pdi
, BOOL isW
)
879 BOOL bResult
= FALSE
;
880 BOOL convertToAnsi
= FALSE
, convertToUnicode
= FALSE
;
881 INT cchTempBufMax
= 0, savCchTextMax
= 0, realNotifCode
;
882 LPWSTR pszTempBuf
= NULL
, savPszText
= NULL
;
884 if ((pdi
->item
.mask
& LVIF_TEXT
) && is_textT(pdi
->item
.pszText
, isW
))
886 convertToAnsi
= (isW
&& infoPtr
->notifyFormat
== NFR_ANSI
);
887 convertToUnicode
= (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
);
890 if (convertToAnsi
|| convertToUnicode
)
892 if (notificationCode
!= LVN_GETDISPINFOW
)
894 cchTempBufMax
= convertToUnicode
?
895 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
896 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
900 cchTempBufMax
= pdi
->item
.cchTextMax
;
901 *pdi
->item
.pszText
= 0; /* make sure we don't process garbage */
904 pszTempBuf
= Alloc( (convertToUnicode
? sizeof(WCHAR
) : sizeof(CHAR
)) * cchTempBufMax
);
905 if (!pszTempBuf
) return FALSE
;
907 if (convertToUnicode
)
908 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
909 pszTempBuf
, cchTempBufMax
);
911 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) pszTempBuf
,
912 cchTempBufMax
, NULL
, NULL
);
914 savCchTextMax
= pdi
->item
.cchTextMax
;
915 savPszText
= pdi
->item
.pszText
;
916 pdi
->item
.pszText
= pszTempBuf
;
917 pdi
->item
.cchTextMax
= cchTempBufMax
;
920 if (infoPtr
->notifyFormat
== NFR_ANSI
)
921 realNotifCode
= get_ansi_notification(notificationCode
);
923 realNotifCode
= notificationCode
;
924 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi
->item
, infoPtr
->notifyFormat
!= NFR_ANSI
));
925 bResult
= notify_hdr(infoPtr
, realNotifCode
, &pdi
->hdr
);
927 if (convertToUnicode
|| convertToAnsi
)
929 if (convertToUnicode
) /* note : pointer can be changed by app ! */
930 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) savPszText
,
931 savCchTextMax
, NULL
, NULL
);
933 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
934 savPszText
, savCchTextMax
);
935 pdi
->item
.pszText
= savPszText
; /* restores our buffer */
936 pdi
->item
.cchTextMax
= savCchTextMax
;
942 static void customdraw_fill(NMLVCUSTOMDRAW
*lpnmlvcd
, LISTVIEW_INFO
*infoPtr
, HDC hdc
,
943 const RECT
*rcBounds
, const LVITEMW
*lplvItem
)
945 ZeroMemory(lpnmlvcd
, sizeof(NMLVCUSTOMDRAW
));
946 lpnmlvcd
->nmcd
.hdc
= hdc
;
947 lpnmlvcd
->nmcd
.rc
= *rcBounds
;
948 lpnmlvcd
->clrTextBk
= infoPtr
->clrTextBk
;
949 lpnmlvcd
->clrText
= infoPtr
->clrText
;
950 if (!lplvItem
) return;
951 lpnmlvcd
->nmcd
.dwItemSpec
= lplvItem
->iItem
+ 1;
952 lpnmlvcd
->iSubItem
= lplvItem
->iSubItem
;
953 if (lplvItem
->state
& LVIS_SELECTED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_SELECTED
;
954 if (lplvItem
->state
& LVIS_FOCUSED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_FOCUS
;
955 if (lplvItem
->iItem
== infoPtr
->nHotItem
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_HOT
;
956 lpnmlvcd
->nmcd
.lItemlParam
= lplvItem
->lParam
;
959 static inline DWORD
notify_customdraw (LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, NMLVCUSTOMDRAW
*lpnmlvcd
)
961 BOOL isForItem
= (lpnmlvcd
->nmcd
.dwItemSpec
!= 0);
964 lpnmlvcd
->nmcd
.dwDrawStage
= dwDrawStage
;
965 if (isForItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_ITEM
;
966 if (lpnmlvcd
->iSubItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_SUBITEM
;
967 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
--;
968 result
= notify_hdr(infoPtr
, NM_CUSTOMDRAW
, &lpnmlvcd
->nmcd
.hdr
);
969 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
++;
973 static void prepaint_setup (LISTVIEW_INFO
*infoPtr
, HDC hdc
, NMLVCUSTOMDRAW
*lpnmlvcd
)
975 /* apprently, for selected items, we have to override the returned values */
976 if (lpnmlvcd
->nmcd
.uItemState
& CDIS_SELECTED
)
980 lpnmlvcd
->clrTextBk
= comctl32_color
.clrHighlight
;
981 lpnmlvcd
->clrText
= comctl32_color
.clrHighlightText
;
983 else if (infoPtr
->dwStyle
& LVS_SHOWSELALWAYS
)
985 lpnmlvcd
->clrTextBk
= comctl32_color
.clr3dFace
;
986 lpnmlvcd
->clrText
= comctl32_color
.clrBtnText
;
990 /* Set the text attributes */
991 if (lpnmlvcd
->clrTextBk
!= CLR_NONE
)
993 SetBkMode(hdc
, OPAQUE
);
994 if (lpnmlvcd
->clrTextBk
== CLR_DEFAULT
)
995 SetBkColor(hdc
, infoPtr
->clrTextBkDefault
);
997 SetBkColor(hdc
,lpnmlvcd
->clrTextBk
);
1000 SetBkMode(hdc
, TRANSPARENT
);
1001 SetTextColor(hdc
, lpnmlvcd
->clrText
);
1004 static inline DWORD
notify_postpaint (LISTVIEW_INFO
*infoPtr
, NMLVCUSTOMDRAW
*lpnmlvcd
)
1006 return notify_customdraw(infoPtr
, CDDS_POSTPAINT
, lpnmlvcd
);
1009 /******** Item iterator functions **********************************/
1011 static RANGES
ranges_create(int count
);
1012 static void ranges_destroy(RANGES ranges
);
1013 static BOOL
ranges_add(RANGES ranges
, RANGE range
);
1014 static BOOL
ranges_del(RANGES ranges
, RANGE range
);
1015 static void ranges_dump(RANGES ranges
);
1017 static inline BOOL
ranges_additem(RANGES ranges
, INT nItem
)
1019 RANGE range
= { nItem
, nItem
+ 1 };
1021 return ranges_add(ranges
, range
);
1024 static inline BOOL
ranges_delitem(RANGES ranges
, INT nItem
)
1026 RANGE range
= { nItem
, nItem
+ 1 };
1028 return ranges_del(ranges
, range
);
1032 * ITERATOR DOCUMENTATION
1034 * The iterator functions allow for easy, and convenient iteration
1035 * over items of iterest in the list. Typically, you create a
1036 * iterator, use it, and destroy it, as such:
1039 * iterator_xxxitems(&i, ...);
1040 * while (iterator_{prev,next}(&i)
1042 * //code which uses i.nItem
1044 * iterator_destroy(&i);
1046 * where xxx is either: framed, or visible.
1047 * Note that it is important that the code destroys the iterator
1048 * after it's done with it, as the creation of the iterator may
1049 * allocate memory, which thus needs to be freed.
1051 * You can iterate both forwards, and backwards through the list,
1052 * by using iterator_next or iterator_prev respectively.
1054 * Lower numbered items are draw on top of higher number items in
1055 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1056 * items may overlap). So, to test items, you should use
1058 * which lists the items top to bottom (in Z-order).
1059 * For drawing items, you should use
1061 * which lists the items bottom to top (in Z-order).
1062 * If you keep iterating over the items after the end-of-items
1063 * marker (-1) is returned, the iterator will start from the
1064 * beginning. Typically, you don't need to test for -1,
1065 * because iterator_{next,prev} will return TRUE if more items
1066 * are to be iterated over, or FALSE otherwise.
1068 * Note: the iterator is defined to be bidirectional. That is,
1069 * any number of prev followed by any number of next, or
1070 * five versa, should leave the iterator at the same item:
1071 * prev * n, next * n = next * n, prev * n
1073 * The iterator has a notion of an out-of-order, special item,
1074 * which sits at the start of the list. This is used in
1075 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1076 * which needs to be first, as it may overlap other items.
1078 * The code is a bit messy because we have:
1079 * - a special item to deal with
1080 * - simple range, or composite range
1082 * If you find bugs, or want to add features, please make sure you
1083 * always check/modify *both* iterator_prev, and iterator_next.
1087 * This function iterates through the items in increasing order,
1088 * but prefixed by the special item, then -1. That is:
1089 * special, 1, 2, 3, ..., n, -1.
1090 * Each item is listed only once.
1092 static inline BOOL
iterator_next(ITERATOR
* i
)
1096 i
->nItem
= i
->nSpecial
;
1097 if (i
->nItem
!= -1) return TRUE
;
1099 if (i
->nItem
== i
->nSpecial
)
1101 if (i
->ranges
) i
->index
= 0;
1107 if (i
->nItem
== i
->nSpecial
) i
->nItem
++;
1108 if (i
->nItem
< i
->range
.upper
) return TRUE
;
1113 if (i
->index
< DPA_GetPtrCount(i
->ranges
->hdpa
))
1114 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, i
->index
++);
1117 else if (i
->nItem
>= i
->range
.upper
) goto end
;
1119 i
->nItem
= i
->range
.lower
;
1120 if (i
->nItem
>= 0) goto testitem
;
1127 * This function iterates through the items in decreasing order,
1128 * followed by the special item, then -1. That is:
1129 * n, n-1, ..., 3, 2, 1, special, -1.
1130 * Each item is listed only once.
1132 static inline BOOL
iterator_prev(ITERATOR
* i
)
1139 if (i
->ranges
) i
->index
= DPA_GetPtrCount(i
->ranges
->hdpa
);
1142 if (i
->nItem
== i
->nSpecial
)
1150 if (i
->nItem
== i
->nSpecial
) i
->nItem
--;
1151 if (i
->nItem
>= i
->range
.lower
) return TRUE
;
1157 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, --i
->index
);
1160 else if (!start
&& i
->nItem
< i
->range
.lower
) goto end
;
1162 i
->nItem
= i
->range
.upper
;
1163 if (i
->nItem
> 0) goto testitem
;
1165 return (i
->nItem
= i
->nSpecial
) != -1;
1168 static RANGE
iterator_range(ITERATOR
* i
)
1172 if (!i
->ranges
) return i
->range
;
1174 if (DPA_GetPtrCount(i
->ranges
->hdpa
) > 0)
1176 range
.lower
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, 0)).lower
;
1177 range
.upper
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, DPA_GetPtrCount(i
->ranges
->hdpa
) - 1)).upper
;
1179 else range
.lower
= range
.upper
= 0;
1185 * Releases resources associated with this ierator.
1187 static inline void iterator_destroy(ITERATOR
* i
)
1189 ranges_destroy(i
->ranges
);
1193 * Create an empty iterator.
1195 static inline BOOL
iterator_empty(ITERATOR
* i
)
1197 ZeroMemory(i
, sizeof(*i
));
1198 i
->nItem
= i
->nSpecial
= i
->range
.lower
= i
->range
.upper
= -1;
1203 * Create an iterator over a range.
1205 static inline BOOL
iterator_rangeitems(ITERATOR
* i
, RANGE range
)
1213 * Create an iterator over a bunch of ranges.
1214 * Please note that the iterator will take ownership of the ranges,
1215 * and will free them upon destruction.
1217 static inline BOOL
iterator_rangesitems(ITERATOR
* i
, RANGES ranges
)
1225 * Creates an iterator over the items which intersect lprc.
1227 static BOOL
iterator_frameditems(ITERATOR
* i
, LISTVIEW_INFO
* infoPtr
, const RECT
*lprc
)
1229 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1230 RECT frame
= *lprc
, rcItem
, rcTemp
;
1233 /* in case we fail, we want to return an empty iterator */
1234 if (!iterator_empty(i
)) return FALSE
;
1236 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1238 TRACE("(lprc=%s)\n", debugrect(lprc
));
1239 OffsetRect(&frame
, -Origin
.x
, -Origin
.y
);
1241 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
1245 if (uView
== LVS_ICON
&& infoPtr
->nFocusedItem
!= -1)
1247 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcItem
);
1248 if (IntersectRect(&rcTemp
, &rcItem
, lprc
))
1249 i
->nSpecial
= infoPtr
->nFocusedItem
;
1251 if (!(iterator_rangesitems(i
, ranges_create(50)))) return FALSE
;
1252 /* to do better here, we need to have PosX, and PosY sorted */
1253 TRACE("building icon ranges:\n");
1254 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
1256 rcItem
.left
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1257 rcItem
.top
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1258 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1259 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1260 if (IntersectRect(&rcTemp
, &rcItem
, &frame
))
1261 ranges_additem(i
->ranges
, nItem
);
1265 else if (uView
== LVS_REPORT
)
1269 if (frame
.left
>= infoPtr
->nItemWidth
) return TRUE
;
1270 if (frame
.top
>= infoPtr
->nItemHeight
* infoPtr
->nItemCount
) return TRUE
;
1272 range
.lower
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1273 range
.upper
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, infoPtr
->nItemCount
- 1) + 1;
1274 if (range
.upper
<= range
.lower
) return TRUE
;
1275 if (!iterator_rangeitems(i
, range
)) return FALSE
;
1276 TRACE(" report=%s\n", debugrange(&i
->range
));
1280 INT nPerCol
= max((infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
, 1);
1281 INT nFirstRow
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1282 INT nLastRow
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, nPerCol
- 1);
1283 INT nFirstCol
= max(frame
.left
/ infoPtr
->nItemWidth
, 0);
1284 INT nLastCol
= min((frame
.right
- 1) / infoPtr
->nItemWidth
, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1285 INT lower
= nFirstCol
* nPerCol
+ nFirstRow
;
1289 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1290 nPerCol
, nFirstRow
, nLastRow
, nFirstCol
, nLastCol
, lower
);
1292 if (nLastCol
< nFirstCol
|| nLastRow
< nFirstRow
) return TRUE
;
1294 if (!(iterator_rangesitems(i
, ranges_create(nLastCol
- nFirstCol
+ 1)))) return FALSE
;
1295 TRACE("building list ranges:\n");
1296 for (nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++)
1298 item_range
.lower
= nCol
* nPerCol
+ nFirstRow
;
1299 if(item_range
.lower
>= infoPtr
->nItemCount
) break;
1300 item_range
.upper
= min(nCol
* nPerCol
+ nLastRow
+ 1, infoPtr
->nItemCount
);
1301 TRACE(" list=%s\n", debugrange(&item_range
));
1302 ranges_add(i
->ranges
, item_range
);
1310 * Creates an iterator over the items which intersect the visible region of hdc.
1312 static BOOL
iterator_visibleitems(ITERATOR
*i
, LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1314 POINT Origin
, Position
;
1315 RECT rcItem
, rcClip
;
1318 rgntype
= GetClipBox(hdc
, &rcClip
);
1319 if (rgntype
== NULLREGION
) return iterator_empty(i
);
1320 if (!iterator_frameditems(i
, infoPtr
, &rcClip
)) return FALSE
;
1321 if (rgntype
== SIMPLEREGION
) return TRUE
;
1323 /* first deal with the special item */
1324 if (i
->nSpecial
!= -1)
1326 LISTVIEW_GetItemBox(infoPtr
, i
->nSpecial
, &rcItem
);
1327 if (!RectVisible(hdc
, &rcItem
)) i
->nSpecial
= -1;
1330 /* if we can't deal with the region, we'll just go with the simple range */
1331 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1332 TRACE("building visible range:\n");
1333 if (!i
->ranges
&& i
->range
.lower
< i
->range
.upper
)
1335 if (!(i
->ranges
= ranges_create(50))) return TRUE
;
1336 if (!ranges_add(i
->ranges
, i
->range
))
1338 ranges_destroy(i
->ranges
);
1344 /* now delete the invisible items from the list */
1345 while(iterator_next(i
))
1347 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
1348 rcItem
.left
= Position
.x
+ Origin
.x
;
1349 rcItem
.top
= Position
.y
+ Origin
.y
;
1350 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1351 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1352 if (!RectVisible(hdc
, &rcItem
))
1353 ranges_delitem(i
->ranges
, i
->nItem
);
1355 /* the iterator should restart on the next iterator_next */
1361 /******** Misc helper functions ************************************/
1363 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
1364 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
1366 if (isW
) return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
1367 else return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
1370 static inline BOOL
is_autoarrange(LISTVIEW_INFO
*infoPtr
)
1372 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1374 return ((infoPtr
->dwStyle
& LVS_AUTOARRANGE
) || infoPtr
->bAutoarrange
) &&
1375 (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
);
1378 /******** Internal API functions ************************************/
1380 static inline COLUMN_INFO
* LISTVIEW_GetColumnInfo(LISTVIEW_INFO
*infoPtr
, INT nSubItem
)
1382 static COLUMN_INFO mainItem
;
1384 if (nSubItem
== 0 && DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0) return &mainItem
;
1385 assert (nSubItem
>= 0 && nSubItem
< DPA_GetPtrCount(infoPtr
->hdpaColumns
));
1386 return (COLUMN_INFO
*)DPA_GetPtr(infoPtr
->hdpaColumns
, nSubItem
);
1389 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO
*infoPtr
, INT nSubItem
, RECT
*lprc
)
1391 *lprc
= LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->rcHeader
;
1394 static inline BOOL
LISTVIEW_GetItemW(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
1396 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
1399 /* Listview invalidation functions: use _only_ these functions to invalidate */
1401 static inline BOOL
is_redrawing(LISTVIEW_INFO
*infoPtr
)
1403 return infoPtr
->bRedraw
;
1406 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO
*infoPtr
, const RECT
* rect
)
1408 if(!is_redrawing(infoPtr
)) return;
1409 TRACE(" invalidating rect=%s\n", debugrect(rect
));
1410 InvalidateRect(infoPtr
->hwndSelf
, rect
, TRUE
);
1413 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1417 if(!is_redrawing(infoPtr
)) return;
1418 LISTVIEW_GetItemBox(infoPtr
, nItem
, &rcBox
);
1419 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1422 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
)
1424 POINT Origin
, Position
;
1427 if(!is_redrawing(infoPtr
)) return;
1428 assert ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
);
1429 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1430 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
1431 LISTVIEW_GetHeaderRect(infoPtr
, nSubItem
, &rcBox
);
1433 rcBox
.bottom
= infoPtr
->nItemHeight
;
1434 OffsetRect(&rcBox
, Origin
.x
+ Position
.x
, Origin
.y
+ Position
.y
);
1435 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1438 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO
*infoPtr
)
1440 LISTVIEW_InvalidateRect(infoPtr
, NULL
);
1443 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
1447 if(!is_redrawing(infoPtr
)) return;
1448 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
1449 rcCol
.top
= infoPtr
->rcList
.top
;
1450 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
1451 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
1456 * Retrieves the number of items that can fit vertically in the client area.
1459 * [I] infoPtr : valid pointer to the listview structure
1462 * Number of items per row.
1464 static inline INT
LISTVIEW_GetCountPerRow(LISTVIEW_INFO
*infoPtr
)
1466 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1468 return max(nListWidth
/infoPtr
->nItemWidth
, 1);
1473 * Retrieves the number of items that can fit horizontally in the client
1477 * [I] infoPtr : valid pointer to the listview structure
1480 * Number of items per column.
1482 static inline INT
LISTVIEW_GetCountPerColumn(LISTVIEW_INFO
*infoPtr
)
1484 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1486 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
1490 /*************************************************************************
1491 * LISTVIEW_ProcessLetterKeys
1493 * Processes keyboard messages generated by pressing the letter keys
1495 * What this does is perform a case insensitive search from the
1496 * current position with the following quirks:
1497 * - If two chars or more are pressed in quick succession we search
1498 * for the corresponding string (e.g. 'abc').
1499 * - If there is a delay we wipe away the current search string and
1500 * restart with just that char.
1501 * - If the user keeps pressing the same character, whether slowly or
1502 * fast, so that the search string is entirely composed of this
1503 * character ('aaaaa' for instance), then we search for first item
1504 * that starting with that character.
1505 * - If the user types the above character in quick succession, then
1506 * we must also search for the corresponding string ('aaaaa'), and
1507 * go to that string if there is a match.
1510 * [I] hwnd : handle to the window
1511 * [I] charCode : the character code, the actual character
1512 * [I] keyData : key data
1520 * - The current implementation has a list of characters it will
1521 * accept and it ignores averything else. In particular it will
1522 * ignore accentuated characters which seems to match what
1523 * Windows does. But I'm not sure it makes sense to follow
1525 * - We don't sound a beep when the search fails.
1529 * TREEVIEW_ProcessLetterKeys
1531 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
1536 WCHAR buffer
[MAX_PATH
];
1537 DWORD lastKeyPressTimestamp
= infoPtr
->lastKeyPressTimestamp
;
1539 /* simple parameter checking */
1540 if (!charCode
|| !keyData
) return 0;
1542 /* only allow the valid WM_CHARs through */
1543 if (!isalnum(charCode
) &&
1544 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
1545 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
1546 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
1547 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
1548 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
1549 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
1550 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
1551 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
1552 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
1555 /* if there's one item or less, there is no where to go */
1556 if (infoPtr
->nItemCount
<= 1) return 0;
1558 /* update the search parameters */
1559 infoPtr
->lastKeyPressTimestamp
= GetTickCount();
1560 if (infoPtr
->lastKeyPressTimestamp
- lastKeyPressTimestamp
< KEY_DELAY
) {
1561 if (infoPtr
->nSearchParamLength
< MAX_PATH
)
1562 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++]=charCode
;
1563 if (infoPtr
->charCode
!= charCode
)
1564 infoPtr
->charCode
= charCode
= 0;
1566 infoPtr
->charCode
=charCode
;
1567 infoPtr
->szSearchParam
[0]=charCode
;
1568 infoPtr
->nSearchParamLength
=1;
1569 /* Redundant with the 1 char string */
1573 /* and search from the current position */
1575 if (infoPtr
->nFocusedItem
>= 0) {
1576 endidx
=infoPtr
->nFocusedItem
;
1578 /* if looking for single character match,
1579 * then we must always move forward
1581 if (infoPtr
->nSearchParamLength
== 1)
1584 endidx
=infoPtr
->nItemCount
;
1588 if (idx
== infoPtr
->nItemCount
) {
1589 if (endidx
== infoPtr
->nItemCount
|| endidx
== 0)
1595 item
.mask
= LVIF_TEXT
;
1598 item
.pszText
= buffer
;
1599 item
.cchTextMax
= MAX_PATH
;
1600 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
1602 /* check for a match */
1603 if (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,infoPtr
->nSearchParamLength
) == 0) {
1606 } else if ( (charCode
!= 0) && (nItem
== -1) && (nItem
!= infoPtr
->nFocusedItem
) &&
1607 (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,1) == 0) ) {
1608 /* This would work but we must keep looking for a longer match */
1612 } while (idx
!= endidx
);
1615 LISTVIEW_KeySelection(infoPtr
, nItem
);
1620 /*************************************************************************
1621 * LISTVIEW_UpdateHeaderSize [Internal]
1623 * Function to resize the header control
1626 * [I] hwnd : handle to a window
1627 * [I] nNewScrollPos : scroll pos to set
1632 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
1637 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
1639 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
1640 point
[0].x
= winRect
.left
;
1641 point
[0].y
= winRect
.top
;
1642 point
[1].x
= winRect
.right
;
1643 point
[1].y
= winRect
.bottom
;
1645 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
1646 point
[0].x
= -nNewScrollPos
;
1647 point
[1].x
+= nNewScrollPos
;
1649 SetWindowPos(infoPtr
->hwndHeader
,0,
1650 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
1651 SWP_NOZORDER
| SWP_NOACTIVATE
);
1656 * Update the scrollbars. This functions should be called whenever
1657 * the content, size or view changes.
1660 * [I] infoPtr : valid pointer to the listview structure
1665 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO
*infoPtr
)
1667 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1668 SCROLLINFO horzInfo
, vertInfo
;
1670 if ((infoPtr
->dwStyle
& LVS_NOSCROLL
) || !is_redrawing(infoPtr
)) return;
1672 ZeroMemory(&horzInfo
, sizeof(SCROLLINFO
));
1673 horzInfo
.cbSize
= sizeof(SCROLLINFO
);
1674 horzInfo
.nPage
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1676 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1677 if (uView
== LVS_LIST
)
1679 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
1680 horzInfo
.nMax
= (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
;
1682 /* scroll by at least one column per page */
1683 if(horzInfo
.nPage
< infoPtr
->nItemWidth
)
1684 horzInfo
.nPage
= infoPtr
->nItemWidth
;
1686 horzInfo
.nPage
/= infoPtr
->nItemWidth
;
1688 else if (uView
== LVS_REPORT
)
1690 horzInfo
.nMax
= infoPtr
->nItemWidth
;
1692 else /* LVS_ICON, or LVS_SMALLICON */
1696 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) horzInfo
.nMax
= rcView
.right
- rcView
.left
;
1699 horzInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1700 horzInfo
.nMax
= max(horzInfo
.nMax
- 1, 0);
1701 SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
, TRUE
);
1702 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo
));
1704 /* Setting the horizontal scroll can change the listview size
1705 * (and potentially everything else) so we need to recompute
1706 * everything again for the vertical scroll
1709 ZeroMemory(&vertInfo
, sizeof(SCROLLINFO
));
1710 vertInfo
.cbSize
= sizeof(SCROLLINFO
);
1711 vertInfo
.nPage
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1713 if (uView
== LVS_REPORT
)
1715 vertInfo
.nMax
= infoPtr
->nItemCount
;
1717 /* scroll by at least one page */
1718 if(vertInfo
.nPage
< infoPtr
->nItemHeight
)
1719 vertInfo
.nPage
= infoPtr
->nItemHeight
;
1721 vertInfo
.nPage
/= infoPtr
->nItemHeight
;
1723 else if (uView
!= LVS_LIST
) /* LVS_ICON, or LVS_SMALLICON */
1727 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) vertInfo
.nMax
= rcView
.bottom
- rcView
.top
;
1730 vertInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1731 vertInfo
.nMax
= max(vertInfo
.nMax
- 1, 0);
1732 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &vertInfo
, TRUE
);
1733 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo
));
1735 /* Update the Header Control */
1736 if (uView
== LVS_REPORT
)
1738 horzInfo
.fMask
= SIF_POS
;
1739 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
);
1740 LISTVIEW_UpdateHeaderSize(infoPtr
, horzInfo
.nPos
);
1747 * Shows/hides the focus rectangle.
1750 * [I] infoPtr : valid pointer to the listview structure
1751 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1756 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO
*infoPtr
, BOOL fShow
)
1758 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1761 TRACE("fShow=%d, nItem=%d\n", fShow
, infoPtr
->nFocusedItem
);
1763 if (infoPtr
->nFocusedItem
< 0) return;
1765 /* we need some gymnastics in ICON mode to handle large items */
1766 if ( (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
1770 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcBox
);
1771 if ((rcBox
.bottom
- rcBox
.top
) > infoPtr
->nItemHeight
)
1773 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1778 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) return;
1780 /* for some reason, owner draw should work only in report mode */
1781 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
1786 item
.iItem
= infoPtr
->nFocusedItem
;
1788 item
.mask
= LVIF_PARAM
;
1789 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto done
;
1791 ZeroMemory(&dis
, sizeof(dis
));
1792 dis
.CtlType
= ODT_LISTVIEW
;
1793 dis
.CtlID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1794 dis
.itemID
= item
.iItem
;
1795 dis
.itemAction
= ODA_FOCUS
;
1796 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
1797 dis
.hwndItem
= infoPtr
->hwndSelf
;
1799 LISTVIEW_GetItemBox(infoPtr
, dis
.itemID
, &dis
.rcItem
);
1800 dis
.itemData
= item
.lParam
;
1802 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
1806 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
1809 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1813 * Invalidates all visible selected items.
1815 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO
*infoPtr
)
1819 iterator_frameditems(&i
, infoPtr
, &infoPtr
->rcList
);
1820 while(iterator_next(&i
))
1822 if (LISTVIEW_GetItemState(infoPtr
, i
.nItem
, LVIS_SELECTED
))
1823 LISTVIEW_InvalidateItem(infoPtr
, i
.nItem
);
1825 iterator_destroy(&i
);
1830 * DESCRIPTION: [INTERNAL]
1831 * Computes an item's (left,top) corner, relative to rcView.
1832 * That is, the position has NOT been made relative to the Origin.
1833 * This is deliberate, to avoid computing the Origin over, and
1834 * over again, when this function is call in a loop. Instead,
1835 * one ca factor the computation of the Origin before the loop,
1836 * and offset the value retured by this function, on every iteration.
1839 * [I] infoPtr : valid pointer to the listview structure
1840 * [I] nItem : item number
1841 * [O] lpptOrig : item top, left corner
1846 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
1848 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1850 assert(nItem
>= 0 && nItem
< infoPtr
->nItemCount
);
1852 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1854 lpptPosition
->x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1855 lpptPosition
->y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1857 else if (uView
== LVS_LIST
)
1859 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
1860 lpptPosition
->x
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
1861 lpptPosition
->y
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
1863 else /* LVS_REPORT */
1865 lpptPosition
->x
= 0;
1866 lpptPosition
->y
= nItem
* infoPtr
->nItemHeight
;
1871 * DESCRIPTION: [INTERNAL]
1872 * Compute the rectangles of an item. This is to localize all
1873 * the computations in one place. If you are not interested in some
1874 * of these values, simply pass in a NULL -- the fucntion is smart
1875 * enough to compute only what's necessary. The function computes
1876 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1877 * one, the BOX rectangle. This rectangle is very cheap to compute,
1878 * and is guaranteed to contain all the other rectangles. Computing
1879 * the ICON rect is also cheap, but all the others are potentaily
1880 * expensive. This gives an easy and effective optimization when
1881 * searching (like point inclusion, or rectangle intersection):
1882 * first test against the BOX, and if TRUE, test agains the desired
1884 * If the function does not have all the necessary information
1885 * to computed the requested rectangles, will crash with a
1886 * failed assertion. This is done so we catch all programming
1887 * errors, given that the function is called only from our code.
1889 * We have the following 'special' meanings for a few fields:
1890 * * If LVIS_FOCUSED is set, we assume the item has the focus
1891 * This is important in ICON mode, where it might get a larger
1892 * then usual rectange
1894 * Please note that subitem support works only in REPORT mode.
1897 * [I] infoPtr : valid pointer to the listview structure
1898 * [I] lpLVItem : item to compute the measures for
1899 * [O] lprcBox : ptr to Box rectangle
1900 * The internal LVIR_BOX rectangle
1901 * [0] lprcState : ptr to State icon rectangle
1902 * The internal LVIR_STATE rectangle
1903 * [O] lprcIcon : ptr to Icon rectangle
1904 * Same as LVM_GETITEMRECT with LVIR_ICON
1905 * [O] lprcLabel : ptr to Label rectangle
1906 * Same as LVM_GETITEMRECT with LVIR_LABEL
1911 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
,
1912 LPRECT lprcBox
, LPRECT lprcState
,
1913 LPRECT lprcIcon
, LPRECT lprcLabel
)
1915 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1916 BOOL doState
= FALSE
, doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
1917 RECT Box
, State
, Icon
, Label
;
1918 COLUMN_INFO
*lpColumnInfo
= NULL
;
1920 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem
, TRUE
));
1922 /* Be smart and try to figure out the minimum we have to do */
1923 if (lpLVItem
->iSubItem
) assert(uView
== LVS_REPORT
);
1924 if (uView
== LVS_ICON
&& (lprcBox
|| lprcLabel
))
1926 assert((lpLVItem
->mask
& LVIF_STATE
) && (lpLVItem
->stateMask
& LVIS_FOCUSED
));
1927 if (lpLVItem
->state
& LVIS_FOCUSED
) oversizedBox
= doLabel
= TRUE
;
1929 if (lprcLabel
) doLabel
= TRUE
;
1930 if (doLabel
|| lprcIcon
) doIcon
= TRUE
;
1931 if (doIcon
|| lprcState
) doState
= TRUE
;
1933 /************************************************************/
1934 /* compute the box rectangle (it should be cheap to do) */
1935 /************************************************************/
1936 if (lpLVItem
->iSubItem
|| uView
== LVS_REPORT
)
1937 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpLVItem
->iSubItem
);
1939 if (lpLVItem
->iSubItem
)
1941 Box
= lpColumnInfo
->rcHeader
;
1946 Box
.right
= infoPtr
->nItemWidth
;
1949 Box
.bottom
= infoPtr
->nItemHeight
;
1951 /************************************************************/
1952 /* compute STATEICON bounding box */
1953 /************************************************************/
1956 if (uView
== LVS_ICON
)
1958 State
.left
= Box
.left
- infoPtr
->iconStateSize
.cx
- 2;
1959 if (infoPtr
->himlNormal
)
1960 State
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
1961 State
.top
= Box
.top
+ infoPtr
->iconSize
.cy
- infoPtr
->iconStateSize
.cy
+ 4;
1965 /* we need the ident in report mode, if we don't have it, we fail */
1966 State
.left
= Box
.left
;
1967 if (uView
== LVS_REPORT
)
1969 if (lpLVItem
->iSubItem
== 0)
1971 State
.left
+= REPORT_MARGINX
;
1972 assert(lpLVItem
->mask
& LVIF_INDENT
);
1973 State
.left
+= infoPtr
->iconSize
.cx
* lpLVItem
->iIndent
;
1976 State
.top
= Box
.top
;
1978 State
.right
= State
.left
;
1979 State
.bottom
= State
.top
;
1980 if (infoPtr
->himlState
&& lpLVItem
->iSubItem
== 0)
1982 State
.right
+= infoPtr
->iconStateSize
.cx
;
1983 State
.bottom
+= infoPtr
->iconStateSize
.cy
;
1985 if (lprcState
) *lprcState
= State
;
1986 TRACE(" - state=%s\n", debugrect(&State
));
1988 else State
.right
= 0;
1990 /************************************************************/
1991 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1992 /************************************************************/
1995 if (uView
== LVS_ICON
)
1997 Icon
.left
= Box
.left
;
1998 if (infoPtr
->himlNormal
)
1999 Icon
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
2000 Icon
.top
= Box
.top
+ ICON_TOP_PADDING
;
2001 Icon
.right
= Icon
.left
;
2002 Icon
.bottom
= Icon
.top
;
2003 if (infoPtr
->himlNormal
)
2005 Icon
.right
+= infoPtr
->iconSize
.cx
;
2006 Icon
.bottom
+= infoPtr
->iconSize
.cy
;
2009 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2011 Icon
.left
= State
.right
;
2013 Icon
.right
= Icon
.left
;
2014 if (infoPtr
->himlSmall
&&
2015 (!lpColumnInfo
|| lpLVItem
->iSubItem
== 0 || (lpColumnInfo
->fmt
& LVCFMT_IMAGE
) ||
2016 ((infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
) && lpLVItem
->iImage
!= I_IMAGECALLBACK
)))
2017 Icon
.right
+= infoPtr
->iconSize
.cx
;
2018 Icon
.bottom
= Icon
.top
+ infoPtr
->nItemHeight
;
2020 if(lprcIcon
) *lprcIcon
= Icon
;
2021 TRACE(" - icon=%s\n", debugrect(&Icon
));
2023 else Icon
.right
= 0;
2025 /************************************************************/
2026 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2027 /************************************************************/
2030 SIZE labelSize
= { 0, 0 };
2032 /* calculate how far to the right can the label strech */
2033 Label
.right
= Box
.right
;
2034 if (uView
== LVS_REPORT
)
2036 if (lpLVItem
->iSubItem
== 0) Label
= lpColumnInfo
->rcHeader
;
2039 if (lpLVItem
->iSubItem
|| ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && uView
== LVS_REPORT
))
2041 labelSize
.cx
= infoPtr
->nItemWidth
;
2042 labelSize
.cy
= infoPtr
->nItemHeight
;
2046 /* we need the text in non owner draw mode */
2047 assert(lpLVItem
->mask
& LVIF_TEXT
);
2048 if (is_textT(lpLVItem
->pszText
, TRUE
))
2050 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2051 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2052 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2056 /* compute rough rectangle where the label will go */
2057 SetRectEmpty(&rcText
);
2058 rcText
.right
= infoPtr
->nItemWidth
- TRAILING_LABEL_PADDING
;
2059 rcText
.bottom
= infoPtr
->nItemHeight
;
2060 if (uView
== LVS_ICON
)
2061 rcText
.bottom
-= ICON_TOP_PADDING
+ infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2063 /* now figure out the flags */
2064 if (uView
== LVS_ICON
)
2065 uFormat
= oversizedBox
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
;
2067 uFormat
= LV_SL_DT_FLAGS
;
2069 DrawTextW (hdc
, lpLVItem
->pszText
, -1, &rcText
, uFormat
| DT_CALCRECT
);
2071 labelSize
.cx
= min(rcText
.right
- rcText
.left
+ TRAILING_LABEL_PADDING
, infoPtr
->nItemWidth
);
2072 labelSize
.cy
= rcText
.bottom
- rcText
.top
;
2074 SelectObject(hdc
, hOldFont
);
2075 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2079 if (uView
== LVS_ICON
)
2081 Label
.left
= Box
.left
+ (infoPtr
->nItemWidth
- labelSize
.cx
) / 2;
2082 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
2083 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2084 Label
.right
= Label
.left
+ labelSize
.cx
;
2085 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2086 if (!oversizedBox
&& labelSize
.cy
> infoPtr
->ntmHeight
&&
2089 labelSize
.cy
= min(Box
.bottom
- Label
.top
, labelSize
.cy
);
2090 labelSize
.cy
/= infoPtr
->ntmHeight
;
2091 labelSize
.cy
= max(labelSize
.cy
, 1);
2092 labelSize
.cy
*= infoPtr
->ntmHeight
;
2094 Label
.bottom
= Label
.top
+ labelSize
.cy
+ HEIGHT_PADDING
;
2096 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2098 Label
.left
= Icon
.right
;
2099 Label
.top
= Box
.top
;
2100 Label
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2101 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2104 if (lprcLabel
) *lprcLabel
= Label
;
2105 TRACE(" - label=%s\n", debugrect(&Label
));
2108 /* Fix the Box if necessary */
2111 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
2112 else *lprcBox
= Box
;
2114 TRACE(" - box=%s\n", debugrect(&Box
));
2118 * DESCRIPTION: [INTERNAL]
2121 * [I] infoPtr : valid pointer to the listview structure
2122 * [I] nItem : item number
2123 * [O] lprcBox : ptr to Box rectangle
2128 static void LISTVIEW_GetItemBox(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprcBox
)
2130 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2131 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2132 POINT Position
, Origin
;
2135 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
2136 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
2138 /* Be smart and try to figure out the minimum we have to do */
2140 if (uView
== LVS_ICON
&& infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
2141 lvItem
.mask
|= LVIF_TEXT
;
2142 lvItem
.iItem
= nItem
;
2143 lvItem
.iSubItem
= 0;
2144 lvItem
.pszText
= szDispText
;
2145 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2146 if (lvItem
.mask
) LISTVIEW_GetItemW(infoPtr
, &lvItem
);
2147 if (uView
== LVS_ICON
)
2149 lvItem
.mask
|= LVIF_STATE
;
2150 lvItem
.stateMask
= LVIS_FOCUSED
;
2151 lvItem
.state
= (lvItem
.mask
& LVIF_TEXT
? LVIS_FOCUSED
: 0);
2153 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprcBox
, 0, 0, 0);
2155 OffsetRect(lprcBox
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
2161 * Returns the current icon position, and advances it along the top.
2162 * The returned position is not offset by Origin.
2165 * [I] infoPtr : valid pointer to the listview structure
2166 * [O] lpPos : will get the current icon position
2171 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2173 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2175 *lpPos
= infoPtr
->currIconPos
;
2177 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2178 if (infoPtr
->currIconPos
.x
+ infoPtr
->nItemWidth
<= nListWidth
) return;
2180 infoPtr
->currIconPos
.x
= 0;
2181 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2187 * Returns the current icon position, and advances it down the left edge.
2188 * The returned position is not offset by Origin.
2191 * [I] infoPtr : valid pointer to the listview structure
2192 * [O] lpPos : will get the current icon position
2197 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2199 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2201 *lpPos
= infoPtr
->currIconPos
;
2203 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2204 if (infoPtr
->currIconPos
.y
+ infoPtr
->nItemHeight
<= nListHeight
) return;
2206 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2207 infoPtr
->currIconPos
.y
= 0;
2213 * Moves an icon to the specified position.
2214 * It takes care of invalidating the item, etc.
2217 * [I] infoPtr : valid pointer to the listview structure
2218 * [I] nItem : the item to move
2219 * [I] lpPos : the new icon position
2220 * [I] isNew : flags the item as being new
2226 static BOOL
LISTVIEW_MoveIconTo(LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*lppt
, BOOL isNew
)
2232 old
.x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2233 old
.y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2235 if (lppt
->x
== old
.x
&& lppt
->y
== old
.y
) return TRUE
;
2236 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2239 /* Allocating a POINTER for every item is too resource intensive,
2240 * so we'll keep the (x,y) in different arrays */
2241 if (!DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)(LONG_PTR
)lppt
->x
)) return FALSE
;
2242 if (!DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)(LONG_PTR
)lppt
->y
)) return FALSE
;
2244 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2251 * Arranges listview items in icon display mode.
2254 * [I] infoPtr : valid pointer to the listview structure
2255 * [I] nAlignCode : alignment code
2261 static BOOL
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
2263 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2264 void (*next_pos
)(LISTVIEW_INFO
*, LPPOINT
);
2268 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
) return FALSE
;
2270 TRACE("nAlignCode=%d\n", nAlignCode
);
2272 if (nAlignCode
== LVA_DEFAULT
)
2274 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
) nAlignCode
= LVA_ALIGNLEFT
;
2275 else nAlignCode
= LVA_ALIGNTOP
;
2280 case LVA_ALIGNLEFT
: next_pos
= LISTVIEW_NextIconPosLeft
; break;
2281 case LVA_ALIGNTOP
: next_pos
= LISTVIEW_NextIconPosTop
; break;
2282 case LVA_SNAPTOGRID
: next_pos
= LISTVIEW_NextIconPosTop
; break; /* FIXME */
2283 default: return FALSE
;
2286 infoPtr
->bAutoarrange
= TRUE
;
2287 infoPtr
->currIconPos
.x
= infoPtr
->currIconPos
.y
= 0;
2288 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2290 next_pos(infoPtr
, &pos
);
2291 LISTVIEW_MoveIconTo(infoPtr
, i
, &pos
, FALSE
);
2299 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2302 * [I] infoPtr : valid pointer to the listview structure
2303 * [O] lprcView : bounding rectangle
2309 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2313 SetRectEmpty(lprcView
);
2315 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
2319 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2321 x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, i
);
2322 y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, i
);
2323 lprcView
->right
= max(lprcView
->right
, x
);
2324 lprcView
->bottom
= max(lprcView
->bottom
, y
);
2326 if (infoPtr
->nItemCount
> 0)
2328 lprcView
->right
+= infoPtr
->nItemWidth
;
2329 lprcView
->bottom
+= infoPtr
->nItemHeight
;
2334 y
= LISTVIEW_GetCountPerColumn(infoPtr
);
2335 x
= infoPtr
->nItemCount
/ y
;
2336 if (infoPtr
->nItemCount
% y
) x
++;
2337 lprcView
->right
= x
* infoPtr
->nItemWidth
;
2338 lprcView
->bottom
= y
* infoPtr
->nItemHeight
;
2342 lprcView
->right
= infoPtr
->nItemWidth
;
2343 lprcView
->bottom
= infoPtr
->nItemCount
* infoPtr
->nItemHeight
;
2350 * Retrieves the bounding rectangle of all the items.
2353 * [I] infoPtr : valid pointer to the listview structure
2354 * [O] lprcView : bounding rectangle
2360 static BOOL
LISTVIEW_GetViewRect(LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2364 TRACE("(lprcView=%p)\n", lprcView
);
2366 if (!lprcView
) return FALSE
;
2368 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
2369 LISTVIEW_GetAreaRect(infoPtr
, lprcView
);
2370 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
2372 TRACE("lprcView=%s\n", debugrect(lprcView
));
2379 * Retrieves the subitem pointer associated with the subitem index.
2382 * [I] hdpaSubItems : DPA handle for a specific item
2383 * [I] nSubItem : index of subitem
2386 * SUCCESS : subitem pointer
2389 static SUBITEM_INFO
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
, INT nSubItem
)
2391 SUBITEM_INFO
*lpSubItem
;
2394 /* we should binary search here if need be */
2395 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
2397 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
2398 if (lpSubItem
->iSubItem
== nSubItem
)
2408 * Caclulates the desired item width.
2411 * [I] infoPtr : valid pointer to the listview structure
2414 * The desired item width.
2416 static INT
LISTVIEW_CalculateItemWidth(LISTVIEW_INFO
*infoPtr
)
2418 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2421 TRACE("uView=%d\n", uView
);
2423 if (uView
== LVS_ICON
)
2424 nItemWidth
= infoPtr
->iconSpacing
.cx
;
2425 else if (uView
== LVS_REPORT
)
2429 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2431 LISTVIEW_GetHeaderRect(infoPtr
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, &rcHeader
);
2432 nItemWidth
= rcHeader
.right
;
2435 else /* LVS_SMALLICON, or LVS_LIST */
2439 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2440 nItemWidth
= max(LISTVIEW_GetLabelWidth(infoPtr
, i
), nItemWidth
);
2442 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
2443 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
2445 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
+ WIDTH_PADDING
);
2448 return max(nItemWidth
, 1);
2453 * Caclulates the desired item height.
2456 * [I] infoPtr : valid pointer to the listview structure
2459 * The desired item height.
2461 static INT
LISTVIEW_CalculateItemHeight(LISTVIEW_INFO
*infoPtr
)
2463 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2466 TRACE("uView=%d\n", uView
);
2468 if (uView
== LVS_ICON
)
2469 nItemHeight
= infoPtr
->iconSpacing
.cy
;
2472 nItemHeight
= infoPtr
->ntmHeight
;
2473 if (infoPtr
->himlState
)
2474 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
2475 if (infoPtr
->himlSmall
)
2476 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
2477 if (infoPtr
->himlState
|| infoPtr
->himlSmall
)
2478 nItemHeight
+= HEIGHT_PADDING
;
2479 if (infoPtr
->nMeasureItemHeight
> 0)
2480 nItemHeight
= infoPtr
->nMeasureItemHeight
;
2483 return max(nItemHeight
, 1);
2488 * Updates the width, and height of an item.
2491 * [I] infoPtr : valid pointer to the listview structure
2496 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO
*infoPtr
)
2498 infoPtr
->nItemWidth
= LISTVIEW_CalculateItemWidth(infoPtr
);
2499 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
2505 * Retrieves and saves important text metrics info for the current
2509 * [I] infoPtr : valid pointer to the listview structure
2512 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
2514 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2515 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2516 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2520 if (GetTextMetricsW(hdc
, &tm
))
2522 infoPtr
->ntmHeight
= tm
.tmHeight
;
2523 infoPtr
->ntmMaxCharWidth
= tm
.tmMaxCharWidth
;
2526 if (GetTextExtentPoint32A(hdc
, "...", 3, &sz
))
2527 infoPtr
->nEllipsisWidth
= sz
.cx
;
2529 SelectObject(hdc
, hOldFont
);
2530 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2532 TRACE("tmHeight=%d\n", infoPtr
->ntmHeight
);
2537 * A compare function for ranges
2540 * [I] range1 : pointer to range 1;
2541 * [I] range2 : pointer to range 2;
2545 * > 0 : if range 1 > range 2
2546 * < 0 : if range 2 > range 1
2547 * = 0 : if range intersects range 2
2549 static INT CALLBACK
ranges_cmp(LPVOID range1
, LPVOID range2
, LPARAM flags
)
2553 if (((RANGE
*)range1
)->upper
<= ((RANGE
*)range2
)->lower
)
2555 else if (((RANGE
*)range2
)->upper
<= ((RANGE
*)range1
)->lower
)
2560 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE
*)range1
), debugrange((RANGE
*)range2
), cmp
);
2566 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2568 #define ranges_check(ranges, desc) do { } while(0)
2571 static void ranges_assert(RANGES ranges
, LPCSTR desc
, const char *func
, int line
)
2576 TRACE("*** Checking %s:%d:%s ***\n", func
, line
, desc
);
2578 assert (DPA_GetPtrCount(ranges
->hdpa
) >= 0);
2579 ranges_dump(ranges
);
2580 prev
= (RANGE
*)DPA_GetPtr(ranges
->hdpa
, 0);
2581 if (DPA_GetPtrCount(ranges
->hdpa
) > 0)
2582 assert (prev
->lower
>= 0 && prev
->lower
< prev
->upper
);
2583 for (i
= 1; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2585 curr
= (RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
);
2586 assert (prev
->upper
<= curr
->lower
);
2587 assert (curr
->lower
< curr
->upper
);
2590 TRACE("--- Done checking---\n");
2593 static RANGES
ranges_create(int count
)
2595 RANGES ranges
= (RANGES
)Alloc(sizeof(struct tagRANGES
));
2596 if (!ranges
) return NULL
;
2597 ranges
->hdpa
= DPA_Create(count
);
2598 if (ranges
->hdpa
) return ranges
;
2603 static void ranges_clear(RANGES ranges
)
2607 for(i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2608 Free(DPA_GetPtr(ranges
->hdpa
, i
));
2609 DPA_DeleteAllPtrs(ranges
->hdpa
);
2613 static void ranges_destroy(RANGES ranges
)
2615 if (!ranges
) return;
2616 ranges_clear(ranges
);
2617 DPA_Destroy(ranges
->hdpa
);
2621 static RANGES
ranges_clone(RANGES ranges
)
2626 if (!(clone
= ranges_create(DPA_GetPtrCount(ranges
->hdpa
)))) goto fail
;
2628 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2630 RANGE
*newrng
= (RANGE
*)Alloc(sizeof(RANGE
));
2631 if (!newrng
) goto fail
;
2632 *newrng
= *((RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
));
2633 DPA_SetPtr(clone
->hdpa
, i
, newrng
);
2638 TRACE ("clone failed\n");
2639 ranges_destroy(clone
);
2643 static RANGES
ranges_diff(RANGES ranges
, RANGES sub
)
2647 for (i
= 0; i
< DPA_GetPtrCount(sub
->hdpa
); i
++)
2648 ranges_del(ranges
, *((RANGE
*)DPA_GetPtr(sub
->hdpa
, i
)));
2653 static void ranges_dump(RANGES ranges
)
2657 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2658 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges
->hdpa
, i
)));
2661 static inline BOOL
ranges_contain(RANGES ranges
, INT nItem
)
2663 RANGE srchrng
= { nItem
, nItem
+ 1 };
2665 TRACE("(nItem=%d)\n", nItem
);
2666 ranges_check(ranges
, "before contain");
2667 return DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
) != -1;
2670 static INT
ranges_itemcount(RANGES ranges
)
2674 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2676 RANGE
*sel
= DPA_GetPtr(ranges
->hdpa
, i
);
2677 count
+= sel
->upper
- sel
->lower
;
2683 static BOOL
ranges_shift(RANGES ranges
, INT nItem
, INT delta
, INT nUpper
)
2685 RANGE srchrng
= { nItem
, nItem
+ 1 }, *chkrng
;
2688 index
= DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2689 if (index
== -1) return TRUE
;
2691 for (; index
< DPA_GetPtrCount(ranges
->hdpa
); index
++)
2693 chkrng
= DPA_GetPtr(ranges
->hdpa
, index
);
2694 if (chkrng
->lower
>= nItem
)
2695 chkrng
->lower
= max(min(chkrng
->lower
+ delta
, nUpper
- 1), 0);
2696 if (chkrng
->upper
> nItem
)
2697 chkrng
->upper
= max(min(chkrng
->upper
+ delta
, nUpper
), 0);
2702 static BOOL
ranges_add(RANGES ranges
, RANGE range
)
2707 TRACE("(%s)\n", debugrange(&range
));
2708 ranges_check(ranges
, "before add");
2710 /* try find overlapping regions first */
2711 srchrgn
.lower
= range
.lower
- 1;
2712 srchrgn
.upper
= range
.upper
+ 1;
2713 index
= DPA_Search(ranges
->hdpa
, &srchrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
);
2719 TRACE("Adding new range\n");
2721 /* create the brand new range to insert */
2722 newrgn
= (RANGE
*)Alloc(sizeof(RANGE
));
2723 if(!newrgn
) goto fail
;
2726 /* figure out where to insert it */
2727 index
= DPA_Search(ranges
->hdpa
, newrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2728 TRACE("index=%d\n", index
);
2729 if (index
== -1) index
= 0;
2731 /* and get it over with */
2732 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2740 RANGE
*chkrgn
, *mrgrgn
;
2741 INT fromindex
, mergeindex
;
2743 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2744 TRACE("Merge with %s @%d\n", debugrange(chkrgn
), index
);
2746 chkrgn
->lower
= min(range
.lower
, chkrgn
->lower
);
2747 chkrgn
->upper
= max(range
.upper
, chkrgn
->upper
);
2749 TRACE("New range %s @%d\n", debugrange(chkrgn
), index
);
2751 /* merge now common anges */
2753 srchrgn
.lower
= chkrgn
->lower
- 1;
2754 srchrgn
.upper
= chkrgn
->upper
+ 1;
2758 mergeindex
= DPA_Search(ranges
->hdpa
, &srchrgn
, fromindex
, ranges_cmp
, 0, 0);
2759 if (mergeindex
== -1) break;
2760 if (mergeindex
== index
)
2762 fromindex
= index
+ 1;
2766 TRACE("Merge with index %i\n", mergeindex
);
2768 mrgrgn
= DPA_GetPtr(ranges
->hdpa
, mergeindex
);
2769 chkrgn
->lower
= min(chkrgn
->lower
, mrgrgn
->lower
);
2770 chkrgn
->upper
= max(chkrgn
->upper
, mrgrgn
->upper
);
2772 DPA_DeletePtr(ranges
->hdpa
, mergeindex
);
2773 if (mergeindex
< index
) index
--;
2777 ranges_check(ranges
, "after add");
2781 ranges_check(ranges
, "failed add");
2785 static BOOL
ranges_del(RANGES ranges
, RANGE range
)
2790 TRACE("(%s)\n", debugrange(&range
));
2791 ranges_check(ranges
, "before del");
2793 /* we don't use DPAS_SORTED here, since we need *
2794 * to find the first overlapping range */
2795 index
= DPA_Search(ranges
->hdpa
, &range
, 0, ranges_cmp
, 0, 0);
2798 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2800 TRACE("Matches range %s @%d\n", debugrange(chkrgn
), index
);
2802 /* case 1: Same range */
2803 if ( (chkrgn
->upper
== range
.upper
) &&
2804 (chkrgn
->lower
== range
.lower
) )
2806 DPA_DeletePtr(ranges
->hdpa
, index
);
2809 /* case 2: engulf */
2810 else if ( (chkrgn
->upper
<= range
.upper
) &&
2811 (chkrgn
->lower
>= range
.lower
) )
2813 DPA_DeletePtr(ranges
->hdpa
, index
);
2815 /* case 3: overlap upper */
2816 else if ( (chkrgn
->upper
<= range
.upper
) &&
2817 (chkrgn
->lower
< range
.lower
) )
2819 chkrgn
->upper
= range
.lower
;
2821 /* case 4: overlap lower */
2822 else if ( (chkrgn
->upper
> range
.upper
) &&
2823 (chkrgn
->lower
>= range
.lower
) )
2825 chkrgn
->lower
= range
.upper
;
2828 /* case 5: fully internal */
2831 RANGE tmprgn
= *chkrgn
, *newrgn
;
2833 if (!(newrgn
= (RANGE
*)Alloc(sizeof(RANGE
)))) goto fail
;
2834 newrgn
->lower
= chkrgn
->lower
;
2835 newrgn
->upper
= range
.lower
;
2836 chkrgn
->lower
= range
.upper
;
2837 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2846 index
= DPA_Search(ranges
->hdpa
, &range
, index
, ranges_cmp
, 0, 0);
2849 ranges_check(ranges
, "after del");
2853 ranges_check(ranges
, "failed del");
2859 * Removes all selection ranges
2862 * [I] infoPtr : valid pointer to the listview structure
2863 * [I] toSkip : item range to skip removing the selection
2869 static BOOL
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO
*infoPtr
, RANGES toSkip
)
2878 lvItem
.stateMask
= LVIS_SELECTED
;
2880 /* need to clone the DPA because callbacks can change it */
2881 if (!(clone
= ranges_clone(infoPtr
->selectionRanges
))) return FALSE
;
2882 iterator_rangesitems(&i
, ranges_diff(clone
, toSkip
));
2883 while(iterator_next(&i
))
2884 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &lvItem
);
2885 /* note that the iterator destructor will free the cloned range */
2886 iterator_destroy(&i
);
2891 static inline BOOL
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2895 if (!(toSkip
= ranges_create(1))) return FALSE
;
2896 if (nItem
!= -1) ranges_additem(toSkip
, nItem
);
2897 LISTVIEW_DeselectAllSkipItems(infoPtr
, toSkip
);
2898 ranges_destroy(toSkip
);
2902 static inline BOOL
LISTVIEW_DeselectAll(LISTVIEW_INFO
*infoPtr
)
2904 return LISTVIEW_DeselectAllSkipItem(infoPtr
, -1);
2909 * Retrieves the number of items that are marked as selected.
2912 * [I] infoPtr : valid pointer to the listview structure
2915 * Number of items selected.
2917 static INT
LISTVIEW_GetSelectedCount(LISTVIEW_INFO
*infoPtr
)
2919 INT nSelectedCount
= 0;
2921 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
2924 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2926 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
2931 nSelectedCount
= ranges_itemcount(infoPtr
->selectionRanges
);
2933 TRACE("nSelectedCount=%d\n", nSelectedCount
);
2934 return nSelectedCount
;
2939 * Manages the item focus.
2942 * [I] infoPtr : valid pointer to the listview structure
2943 * [I] nItem : item index
2946 * TRUE : focused item changed
2947 * FALSE : focused item has NOT changed
2949 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2951 INT oldFocus
= infoPtr
->nFocusedItem
;
2954 if (nItem
== infoPtr
->nFocusedItem
) return FALSE
;
2956 lvItem
.state
= nItem
== -1 ? 0 : LVIS_FOCUSED
;
2957 lvItem
.stateMask
= LVIS_FOCUSED
;
2958 LISTVIEW_SetItemState(infoPtr
, nItem
== -1 ? infoPtr
->nFocusedItem
: nItem
, &lvItem
);
2960 return oldFocus
!= infoPtr
->nFocusedItem
;
2963 /* Helper function for LISTVIEW_ShiftIndices *only* */
2964 static INT
shift_item(LISTVIEW_INFO
*infoPtr
, INT nShiftItem
, INT nItem
, INT direction
)
2966 if (nShiftItem
< nItem
) return nShiftItem
;
2968 if (nShiftItem
> nItem
) return nShiftItem
+ direction
;
2970 if (direction
> 0) return nShiftItem
+ direction
;
2972 return min(nShiftItem
, infoPtr
->nItemCount
- 1);
2977 * Updates the various indices after an item has been inserted or deleted.
2980 * [I] infoPtr : valid pointer to the listview structure
2981 * [I] nItem : item index
2982 * [I] direction : Direction of shift, +1 or -1.
2987 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
2992 /* temporarily disable change notification while shifting items */
2993 bOldChange
= infoPtr
->bDoChangeNotify
;
2994 infoPtr
->bDoChangeNotify
= FALSE
;
2996 TRACE("Shifting %iu, %i steps\n", nItem
, direction
);
2998 ranges_shift(infoPtr
->selectionRanges
, nItem
, direction
, infoPtr
->nItemCount
);
3000 assert(abs(direction
) == 1);
3002 infoPtr
->nSelectionMark
= shift_item(infoPtr
, infoPtr
->nSelectionMark
, nItem
, direction
);
3004 nNewFocus
= shift_item(infoPtr
, infoPtr
->nFocusedItem
, nItem
, direction
);
3005 if (nNewFocus
!= infoPtr
->nFocusedItem
)
3006 LISTVIEW_SetItemFocus(infoPtr
, nNewFocus
);
3008 /* But we are not supposed to modify nHotItem! */
3010 infoPtr
->bDoChangeNotify
= bOldChange
;
3016 * Adds a block of selections.
3019 * [I] infoPtr : valid pointer to the listview structure
3020 * [I] nItem : item index
3023 * Whether the window is still valid.
3025 static BOOL
LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3027 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
3028 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
3029 HWND hwndSelf
= infoPtr
->hwndSelf
;
3030 NMLVODSTATECHANGE nmlv
;
3035 /* Temporarily disable change notification
3036 * If the control is LVS_OWNERDATA, we need to send
3037 * only one LVN_ODSTATECHANGED notification.
3038 * See MSDN documentation for LVN_ITEMCHANGED.
3040 bOldChange
= infoPtr
->bDoChangeNotify
;
3041 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3043 if (nFirst
== -1) nFirst
= nItem
;
3045 item
.state
= LVIS_SELECTED
;
3046 item
.stateMask
= LVIS_SELECTED
;
3048 for (i
= nFirst
; i
<= nLast
; i
++)
3049 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
3051 ZeroMemory(&nmlv
, sizeof(nmlv
));
3052 nmlv
.iFrom
= nFirst
;
3055 nmlv
.uOldState
= item
.state
;
3057 notify_hdr(infoPtr
, LVN_ODSTATECHANGED
, (LPNMHDR
)&nmlv
);
3058 if (!IsWindow(hwndSelf
))
3060 infoPtr
->bDoChangeNotify
= bOldChange
;
3067 * Sets a single group selection.
3070 * [I] infoPtr : valid pointer to the listview structure
3071 * [I] nItem : item index
3076 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3078 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3083 if (!(selection
= ranges_create(100))) return;
3085 item
.state
= LVIS_SELECTED
;
3086 item
.stateMask
= LVIS_SELECTED
;
3088 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
3090 if (infoPtr
->nSelectionMark
== -1)
3092 infoPtr
->nSelectionMark
= nItem
;
3093 ranges_additem(selection
, nItem
);
3099 sel
.lower
= min(infoPtr
->nSelectionMark
, nItem
);
3100 sel
.upper
= max(infoPtr
->nSelectionMark
, nItem
) + 1;
3101 ranges_add(selection
, sel
);
3106 RECT rcItem
, rcSel
, rcSelMark
;
3109 rcItem
.left
= LVIR_BOUNDS
;
3110 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return;
3111 rcSelMark
.left
= LVIR_BOUNDS
;
3112 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) return;
3113 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
3114 iterator_frameditems(&i
, infoPtr
, &rcSel
);
3115 while(iterator_next(&i
))
3117 LISTVIEW_GetItemPosition(infoPtr
, i
.nItem
, &ptItem
);
3118 if (PtInRect(&rcSel
, ptItem
)) ranges_additem(selection
, i
.nItem
);
3120 iterator_destroy(&i
);
3123 LISTVIEW_DeselectAllSkipItems(infoPtr
, selection
);
3124 iterator_rangesitems(&i
, selection
);
3125 while(iterator_next(&i
))
3126 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &item
);
3127 /* this will also destroy the selection */
3128 iterator_destroy(&i
);
3130 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3135 * Sets a single selection.
3138 * [I] infoPtr : valid pointer to the listview structure
3139 * [I] nItem : item index
3144 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3148 TRACE("nItem=%d\n", nItem
);
3150 LISTVIEW_DeselectAllSkipItem(infoPtr
, nItem
);
3152 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3153 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3154 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3156 infoPtr
->nSelectionMark
= nItem
;
3161 * Set selection(s) with keyboard.
3164 * [I] infoPtr : valid pointer to the listview structure
3165 * [I] nItem : item index
3168 * SUCCESS : TRUE (needs to be repainted)
3169 * FAILURE : FALSE (nothing has changed)
3171 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3173 /* FIXME: pass in the state */
3174 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
3175 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
3176 BOOL bResult
= FALSE
;
3178 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3180 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
3183 LISTVIEW_SetSelection(infoPtr
, nItem
);
3190 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3194 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3199 LISTVIEW_SetSelection(infoPtr
, nItem
);
3202 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3205 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3209 static BOOL
LISTVIEW_GetItemAtPt(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, POINT pt
)
3211 LVHITTESTINFO lvHitTestInfo
;
3213 ZeroMemory(&lvHitTestInfo
, sizeof(lvHitTestInfo
));
3214 lvHitTestInfo
.pt
.x
= pt
.x
;
3215 lvHitTestInfo
.pt
.y
= pt
.y
;
3217 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
3219 lpLVItem
->mask
= LVIF_PARAM
;
3220 lpLVItem
->iItem
= lvHitTestInfo
.iItem
;
3221 lpLVItem
->iSubItem
= 0;
3223 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
3228 * Called when the mouse is being actively tracked and has hovered for a specified
3232 * [I] infoPtr : valid pointer to the listview structure
3233 * [I] fwKeys : key indicator
3234 * [I] x,y : mouse position
3237 * 0 if the message was processed, non-zero if there was an error
3240 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3241 * over the item for a certain period of time.
3244 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, WORD fwKyes
, INT x
, INT y
)
3246 if (infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)
3254 if (LISTVIEW_GetItemAtPt(infoPtr
, &item
, pt
))
3255 LISTVIEW_SetSelection(infoPtr
, item
.iItem
);
3263 * Called whenever WM_MOUSEMOVE is received.
3266 * [I] infoPtr : valid pointer to the listview structure
3267 * [I] fwKeys : key indicator
3268 * [I] x,y : mouse position
3271 * 0 if the message is processed, non-zero if there was an error
3273 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3275 TRACKMOUSEEVENT trackinfo
;
3277 if (!(fwKeys
& MK_LBUTTON
))
3278 infoPtr
->bLButtonDown
= FALSE
;
3280 if (infoPtr
->bLButtonDown
&& DragDetect(infoPtr
->hwndSelf
, infoPtr
->ptClickPos
))
3282 LVHITTESTINFO lvHitTestInfo
;
3285 lvHitTestInfo
.pt
= infoPtr
->ptClickPos
;
3286 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
3288 ZeroMemory(&nmlv
, sizeof(nmlv
));
3289 nmlv
.iItem
= lvHitTestInfo
.iItem
;
3290 nmlv
.ptAction
= infoPtr
->ptClickPos
;
3292 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
3297 infoPtr
->bLButtonDown
= FALSE
;
3299 /* see if we are supposed to be tracking mouse hovering */
3300 if(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) {
3301 /* fill in the trackinfo struct */
3302 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3303 trackinfo
.dwFlags
= TME_QUERY
;
3304 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
3305 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
3307 /* see if we are already tracking this hwnd */
3308 _TrackMouseEvent(&trackinfo
);
3310 if(!(trackinfo
.dwFlags
& TME_HOVER
)) {
3311 trackinfo
.dwFlags
= TME_HOVER
;
3313 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3314 _TrackMouseEvent(&trackinfo
);
3323 * Tests wheather the item is assignable to a list with style lStyle
3325 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
3327 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
3328 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
3329 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
3337 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3340 * [I] infoPtr : valid pointer to the listview structure
3341 * [I] lpLVItem : valid pointer to new item atttributes
3342 * [I] isNew : the item being set is being inserted
3343 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3344 * [O] bChanged : will be set to TRUE if the item really changed
3350 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
3352 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3360 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
3362 if (lpLVItem
->mask
== 0) return TRUE
;
3364 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
3366 /* a virtual listview we stores only selection and focus */
3367 if (lpLVItem
->mask
& ~LVIF_STATE
)
3373 HDPA hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3374 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
3378 /* we need to get the lParam and state of the item */
3379 item
.iItem
= lpLVItem
->iItem
;
3380 item
.iSubItem
= lpLVItem
->iSubItem
;
3381 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
3382 item
.stateMask
= ~0;
3385 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
3387 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
3388 /* determine what fields will change */
3389 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
))
3390 uChanged
|= LVIF_STATE
;
3392 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
3393 uChanged
|= LVIF_IMAGE
;
3395 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
3396 uChanged
|= LVIF_PARAM
;
3398 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
3399 uChanged
|= LVIF_INDENT
;
3401 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
3402 uChanged
|= LVIF_TEXT
;
3404 TRACE("uChanged=0x%x\n", uChanged
);
3405 if (!uChanged
) return TRUE
;
3408 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
3409 nmlv
.iItem
= lpLVItem
->iItem
;
3410 nmlv
.uNewState
= (item
.state
& ~lpLVItem
->stateMask
) | (lpLVItem
->state
& lpLVItem
->stateMask
);
3411 nmlv
.uOldState
= item
.state
;
3412 nmlv
.uChanged
= uChanged
;
3413 nmlv
.lParam
= item
.lParam
;
3415 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3416 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3418 if(lpItem
&& !isNew
&& infoPtr
->bDoChangeNotify
)
3420 HWND hwndSelf
= infoPtr
->hwndSelf
;
3422 if (notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
3424 if (!IsWindow(hwndSelf
))
3428 /* copy information */
3429 if (lpLVItem
->mask
& LVIF_TEXT
)
3430 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3432 if (lpLVItem
->mask
& LVIF_IMAGE
)
3433 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
3435 if (lpLVItem
->mask
& LVIF_PARAM
)
3436 lpItem
->lParam
= lpLVItem
->lParam
;
3438 if (lpLVItem
->mask
& LVIF_INDENT
)
3439 lpItem
->iIndent
= lpLVItem
->iIndent
;
3441 if (uChanged
& LVIF_STATE
)
3443 if (lpItem
&& (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& ~(LVIS_FOCUSED
| LVIS_SELECTED
)))
3445 lpItem
->state
&= ~lpLVItem
->stateMask
;
3446 lpItem
->state
|= (lpLVItem
->state
& lpLVItem
->stateMask
);
3448 if (lpLVItem
->state
& lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3450 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
3451 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3453 else if (lpLVItem
->stateMask
& LVIS_SELECTED
)
3454 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3456 /* if we are asked to change focus, and we manage it, do it */
3457 if (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
3459 if (lpLVItem
->state
& LVIS_FOCUSED
)
3461 LISTVIEW_SetItemFocus(infoPtr
, -1);
3462 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
3463 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, uView
== LVS_LIST
);
3465 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
3466 infoPtr
->nFocusedItem
= -1;
3470 /* if we're inserting the item, we're done */
3471 if (isNew
) return TRUE
;
3473 /* send LVN_ITEMCHANGED notification */
3474 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
3475 if (infoPtr
->bDoChangeNotify
) notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
3482 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3485 * [I] infoPtr : valid pointer to the listview structure
3486 * [I] lpLVItem : valid pointer to new subitem atttributes
3487 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3488 * [O] bChanged : will be set to TRUE if the item really changed
3494 static BOOL
set_sub_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
3497 SUBITEM_INFO
*lpSubItem
;
3499 /* we do not support subitems for virtual listviews */
3500 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
3502 /* set subitem only if column is present */
3503 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
3505 /* First do some sanity checks */
3506 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
)) return FALSE
;
3507 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
))) return TRUE
;
3509 /* get the subitem structure, and create it if not there */
3510 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3511 assert (hdpaSubItems
);
3513 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
3516 SUBITEM_INFO
*tmpSubItem
;
3519 lpSubItem
= (SUBITEM_INFO
*)Alloc(sizeof(SUBITEM_INFO
));
3520 if (!lpSubItem
) return FALSE
;
3521 /* we could binary search here, if need be...*/
3522 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
3524 tmpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
3525 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
3527 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
3532 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
3533 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
3537 if (lpLVItem
->mask
& LVIF_IMAGE
)
3538 if (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
)
3540 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
3544 if (lpLVItem
->mask
& LVIF_TEXT
)
3545 if (lpSubItem
->hdr
.pszText
!= lpLVItem
->pszText
)
3547 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3556 * Sets item attributes.
3559 * [I] infoPtr : valid pointer to the listview structure
3560 * [I] lpLVItem : new item atttributes
3561 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3567 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
3569 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3570 HWND hwndSelf
= infoPtr
->hwndSelf
;
3571 LPWSTR pszText
= NULL
;
3572 BOOL bResult
, bChanged
= FALSE
;
3574 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
3576 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
3579 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3580 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_textW(lpLVItem
->pszText
))
3582 pszText
= lpLVItem
->pszText
;
3583 ((LVITEMW
*)lpLVItem
)->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
3586 /* actually set the fields */
3587 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
3589 if (lpLVItem
->iSubItem
)
3590 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
3592 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
3593 if (!IsWindow(hwndSelf
))
3596 /* redraw item, if necessary */
3597 if (bChanged
&& !infoPtr
->bIsDrawing
)
3599 /* this little optimization eliminates some nasty flicker */
3600 if ( uView
== LVS_REPORT
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
3601 (!(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) || lpLVItem
->iSubItem
) )
3602 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
3604 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
3609 textfreeT(lpLVItem
->pszText
, isW
);
3610 ((LVITEMW
*)lpLVItem
)->pszText
= pszText
;
3618 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3621 * [I] infoPtr : valid pointer to the listview structure
3626 static INT
LISTVIEW_GetTopIndex(LISTVIEW_INFO
*infoPtr
)
3628 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3630 SCROLLINFO scrollInfo
;
3632 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3633 scrollInfo
.fMask
= SIF_POS
;
3635 if (uView
== LVS_LIST
)
3637 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
3638 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
3640 else if (uView
== LVS_REPORT
)
3642 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3643 nItem
= scrollInfo
.nPos
;
3647 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3648 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
3651 TRACE("nItem=%d\n", nItem
);
3659 * Erases the background of the given rectangle
3662 * [I] infoPtr : valid pointer to the listview structure
3663 * [I] hdc : device context handle
3664 * [I] lprcBox : clipping rectangle
3670 static inline BOOL
LISTVIEW_FillBkgnd(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
3672 if (!infoPtr
->hBkBrush
) return FALSE
;
3674 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, debugrect(lprcBox
), infoPtr
->hBkBrush
);
3676 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
3684 * [I] infoPtr : valid pointer to the listview structure
3685 * [I] hdc : device context handle
3686 * [I] nItem : item index
3687 * [I] nSubItem : subitem index
3688 * [I] pos : item position in client coordinates
3689 * [I] cdmode : custom draw mode
3695 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, INT nSubItem
, POINT pos
, DWORD cdmode
)
3697 UINT uFormat
, uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3698 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
3699 static const WCHAR szCallback
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3700 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
3701 RECT
* lprcFocus
, rcSelect
, rcBox
, rcState
, rcIcon
, rcLabel
;
3702 NMLVCUSTOMDRAW nmlvcd
;
3706 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc
, nItem
, nSubItem
, debugpoint(&pos
));
3708 /* get information needed for drawing the item */
3709 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
;
3710 if (nSubItem
== 0) lvItem
.mask
|= LVIF_STATE
| LVIF_PARAM
;
3711 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
3712 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
;
3713 lvItem
.iItem
= nItem
;
3714 lvItem
.iSubItem
= nSubItem
;
3717 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3718 lvItem
.pszText
= szDispText
;
3719 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
3720 if (nSubItem
> 0 && (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3721 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3722 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= (LPWSTR
)szCallback
;
3723 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3725 /* now check if we need to update the focus rectangle */
3726 lprcFocus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
3728 if (!lprcFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
3729 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcState
, &rcIcon
, &rcLabel
);
3730 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
3731 OffsetRect(&rcState
, pos
.x
, pos
.y
);
3732 OffsetRect(&rcIcon
, pos
.x
, pos
.y
);
3733 OffsetRect(&rcLabel
, pos
.x
, pos
.y
);
3734 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3735 debugrect(&rcBox
), debugrect(&rcState
), debugrect(&rcIcon
), debugrect(&rcLabel
));
3737 /* fill in the custom draw structure */
3738 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
3740 if (nSubItem
> 0) cdmode
= infoPtr
->cditemmode
;
3741 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3742 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3743 if (nSubItem
== 0) infoPtr
->cditemmode
= cdsubitemmode
;
3744 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3745 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3746 if (nSubItem
== 0 && cdsubitemmode
== CDRF_NOTIFYITEMDRAW
)
3748 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
3749 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3751 if (nSubItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
3752 prepaint_setup(infoPtr
, hdc
, &nmlvcd
);
3754 /* in full row select, subitems, will just use main item's colors */
3755 if (nSubItem
&& uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3756 nmlvcd
.clrTextBk
= CLR_NONE
;
3759 if (infoPtr
->himlState
&& !IsRectEmpty(&rcState
))
3761 UINT uStateImage
= STATEIMAGEINDEX(lvItem
.state
);
3764 TRACE("uStateImage=%d\n", uStateImage
);
3765 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
, rcState
.left
, rcState
.top
, ILD_NORMAL
);
3770 himl
= (uView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
3771 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
))
3773 TRACE("iImage=%d\n", lvItem
.iImage
);
3774 ImageList_Draw(himl
, lvItem
.iImage
, hdc
, rcIcon
.left
, rcIcon
.top
,
3775 (lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
) ? ILD_SELECTED
: ILD_NORMAL
);
3778 /* Don't bother painting item being edited */
3779 if (infoPtr
->hwndEdit
&& nItem
== infoPtr
->nEditLabelItem
&& nSubItem
== 0) goto postpaint
;
3781 /* draw the selection background, if we're drawing the main item */
3785 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3786 rcSelect
.right
= rcBox
.right
;
3788 if (nmlvcd
.clrTextBk
!= CLR_NONE
)
3789 ExtTextOutW(hdc
, rcSelect
.left
, rcSelect
.top
, ETO_OPAQUE
, &rcSelect
, 0, 0, 0);
3790 if(lprcFocus
) *lprcFocus
= rcSelect
;
3793 /* figure out the text drawing flags */
3794 uFormat
= (uView
== LVS_ICON
? (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
3795 if (uView
== LVS_ICON
)
3796 uFormat
= (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
3799 switch (LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
3801 case LVCFMT_RIGHT
: uFormat
|= DT_RIGHT
; break;
3802 case LVCFMT_CENTER
: uFormat
|= DT_CENTER
; break;
3803 default: uFormat
|= DT_LEFT
;
3806 if (!(uFormat
& (DT_RIGHT
| DT_CENTER
)))
3808 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
3809 else rcLabel
.left
+= LABEL_HOR_PADDING
;
3811 else if (uFormat
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
3812 DrawTextW(hdc
, lvItem
.pszText
, -1, &rcLabel
, uFormat
);
3815 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
3816 notify_postpaint(infoPtr
, &nmlvcd
);
3822 * Draws listview items when in owner draw mode.
3825 * [I] infoPtr : valid pointer to the listview structure
3826 * [I] hdc : device context handle
3831 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3833 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
3834 DWORD cditemmode
= CDRF_DODEFAULT
;
3835 NMLVCUSTOMDRAW nmlvcd
;
3836 POINT Origin
, Position
;
3842 ZeroMemory(&dis
, sizeof(dis
));
3844 /* Get scroll info once before loop */
3845 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3847 /* iterate through the invalidated rows */
3848 while(iterator_next(i
))
3850 item
.iItem
= i
->nItem
;
3852 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
3853 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
3854 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
3856 dis
.CtlType
= ODT_LISTVIEW
;
3858 dis
.itemID
= item
.iItem
;
3859 dis
.itemAction
= ODA_DRAWENTIRE
;
3861 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
3862 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
3863 dis
.hwndItem
= infoPtr
->hwndSelf
;
3865 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
3866 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
3867 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
3868 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
3869 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
3870 dis
.itemData
= item
.lParam
;
3872 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), debugrect(&dis
.rcItem
));
3875 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3876 * structure for the rest. of the paint cycle
3878 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
3879 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3880 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3882 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
3884 prepaint_setup (infoPtr
, hdc
, &nmlvcd
);
3885 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
3888 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3889 notify_postpaint(infoPtr
, &nmlvcd
);
3895 * Draws listview items when in report display mode.
3898 * [I] infoPtr : valid pointer to the listview structure
3899 * [I] hdc : device context handle
3900 * [I] cdmode : custom draw mode
3905 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3908 RECT rcClip
, rcItem
;
3909 POINT Origin
, Position
;
3915 /* figure out what to draw */
3916 rgntype
= GetClipBox(hdc
, &rcClip
);
3917 if (rgntype
== NULLREGION
) return;
3919 /* Get scroll info once before loop */
3920 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3922 /* narrow down the columns we need to paint */
3923 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
3925 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
3926 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
3928 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
3930 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
3931 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
3933 iterator_rangeitems(&j
, colRange
);
3935 /* in full row select, we _have_ to draw the main item */
3936 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
3939 /* iterate through the invalidated rows */
3940 while(iterator_next(i
))
3942 /* iterate through the invalidated columns */
3943 while(iterator_next(&j
))
3945 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
3946 Position
.x
+= Origin
.x
;
3947 Position
.y
+= Origin
.y
;
3949 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
3951 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
3953 rcItem
.bottom
= infoPtr
->nItemHeight
;
3954 OffsetRect(&rcItem
, Position
.x
, Position
.y
);
3955 if (!RectVisible(hdc
, &rcItem
)) continue;
3958 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, j
.nItem
, Position
, cdmode
);
3961 iterator_destroy(&j
);
3966 * Draws listview items when in list display mode.
3969 * [I] infoPtr : valid pointer to the listview structure
3970 * [I] hdc : device context handle
3971 * [I] cdmode : custom draw mode
3976 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3978 POINT Origin
, Position
;
3980 /* Get scroll info once before loop */
3981 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3983 while(iterator_prev(i
))
3985 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
3986 Position
.x
+= Origin
.x
;
3987 Position
.y
+= Origin
.y
;
3989 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, 0, Position
, cdmode
);
3996 * Draws listview items.
3999 * [I] infoPtr : valid pointer to the listview structure
4000 * [I] hdc : device context handle
4005 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
4007 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4008 COLORREF oldTextColor
, oldClrTextBk
, oldClrText
;
4009 NMLVCUSTOMDRAW nmlvcd
;
4016 LISTVIEW_DUMP(infoPtr
);
4018 infoPtr
->bIsDrawing
= TRUE
;
4020 /* save dc values we're gonna trash while drawing */
4021 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
4022 oldBkMode
= GetBkMode(hdc
);
4023 infoPtr
->clrTextBkDefault
= GetBkColor(hdc
);
4024 oldTextColor
= GetTextColor(hdc
);
4026 oldClrTextBk
= infoPtr
->clrTextBk
;
4027 oldClrText
= infoPtr
->clrText
;
4029 infoPtr
->cditemmode
= CDRF_DODEFAULT
;
4031 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
4032 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
4033 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4034 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
4035 prepaint_setup(infoPtr
, hdc
, &nmlvcd
);
4037 /* Use these colors to draw the items */
4038 infoPtr
->clrTextBk
= nmlvcd
.clrTextBk
;
4039 infoPtr
->clrText
= nmlvcd
.clrText
;
4041 /* nothing to draw */
4042 if(infoPtr
->nItemCount
== 0) goto enddraw
;
4044 /* figure out what we need to draw */
4045 iterator_visibleitems(&i
, infoPtr
, hdc
);
4047 /* send cache hint notification */
4048 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4050 RANGE range
= iterator_range(&i
);
4053 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
4054 nmlv
.iFrom
= range
.lower
;
4055 nmlv
.iTo
= range
.upper
- 1;
4056 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
4059 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
4060 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
4063 if (uView
== LVS_REPORT
)
4064 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
4065 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4066 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
4068 /* if we have a focus rect, draw it */
4069 if (infoPtr
->bFocus
)
4070 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
4072 iterator_destroy(&i
);
4075 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
4076 notify_postpaint(infoPtr
, &nmlvcd
);
4078 infoPtr
->clrTextBk
= oldClrTextBk
;
4079 infoPtr
->clrText
= oldClrText
;
4081 SelectObject(hdc
, hOldFont
);
4082 SetBkMode(hdc
, oldBkMode
);
4083 SetBkColor(hdc
, infoPtr
->clrTextBkDefault
);
4084 SetTextColor(hdc
, oldTextColor
);
4085 infoPtr
->bIsDrawing
= FALSE
;
4091 * Calculates the approximate width and height of a given number of items.
4094 * [I] infoPtr : valid pointer to the listview structure
4095 * [I] nItemCount : number of items
4096 * [I] wWidth : width
4097 * [I] wHeight : height
4100 * Returns a DWORD. The width in the low word and the height in high word.
4102 static DWORD
LISTVIEW_ApproximateViewRect(LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
4103 WORD wWidth
, WORD wHeight
)
4105 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4106 INT nItemCountPerColumn
= 1;
4107 INT nColumnCount
= 0;
4108 DWORD dwViewRect
= 0;
4110 if (nItemCount
== -1)
4111 nItemCount
= infoPtr
->nItemCount
;
4113 if (uView
== LVS_LIST
)
4115 if (wHeight
== 0xFFFF)
4117 /* use current height */
4118 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4121 if (wHeight
< infoPtr
->nItemHeight
)
4122 wHeight
= infoPtr
->nItemHeight
;
4126 if (infoPtr
->nItemHeight
> 0)
4128 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
4129 if (nItemCountPerColumn
== 0)
4130 nItemCountPerColumn
= 1;
4132 if (nItemCount
% nItemCountPerColumn
!= 0)
4133 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
4135 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
4139 /* Microsoft padding magic */
4140 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
4141 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
4143 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4145 else if (uView
== LVS_REPORT
)
4149 if (infoPtr
->nItemCount
> 0)
4151 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
4152 wWidth
= rcBox
.right
- rcBox
.left
;
4153 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
4157 /* use current height and width */
4158 if (wHeight
== 0xffff)
4159 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4160 if (wWidth
== 0xffff)
4161 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
4164 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4166 else if (uView
== LVS_SMALLICON
)
4167 FIXME("uView == LVS_SMALLICON: not implemented\n");
4168 else if (uView
== LVS_ICON
)
4169 FIXME("uView == LVS_ICON: not implemented\n");
4177 * Create a drag image list for the specified item.
4180 * [I] infoPtr : valid pointer to the listview structure
4181 * [I] iItem : index of item
4182 * [O] lppt : Upperr-left corner of the image
4185 * Returns a handle to the image list if successful, NULL otherwise.
4187 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
4193 HBITMAP hbmp
, hOldbmp
;
4194 HIMAGELIST dragList
= 0;
4195 TRACE("iItem=%d Count=%d\n", iItem
, infoPtr
->nItemCount
);
4197 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
)
4200 rcItem
.left
= LVIR_BOUNDS
;
4201 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
4204 lppt
->x
= rcItem
.left
;
4205 lppt
->y
= rcItem
.top
;
4207 size
.cx
= rcItem
.right
- rcItem
.left
;
4208 size
.cy
= rcItem
.bottom
- rcItem
.top
;
4210 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
4211 hdc
= CreateCompatibleDC(hdcOrig
);
4212 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
4213 hOldbmp
= SelectObject(hdc
, hbmp
);
4215 rcItem
.left
= rcItem
.top
= 0;
4216 rcItem
.right
= size
.cx
;
4217 rcItem
.bottom
= size
.cy
;
4218 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
4221 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, 0, pos
, infoPtr
->cditemmode
))
4223 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
4224 SelectObject(hdc
, hOldbmp
);
4225 ImageList_Add(dragList
, hbmp
, 0);
4228 SelectObject(hdc
, hOldbmp
);
4232 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
4234 TRACE("ret=%p\n", dragList
);
4242 * Removes all listview items and subitems.
4245 * [I] infoPtr : valid pointer to the listview structure
4251 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
)
4254 HDPA hdpaSubItems
= NULL
;
4261 /* we do it directly, to avoid notifications */
4262 ranges_clear(infoPtr
->selectionRanges
);
4263 infoPtr
->nSelectionMark
= -1;
4264 infoPtr
->nFocusedItem
= -1;
4265 SetRectEmpty(&infoPtr
->rcFocus
);
4266 /* But we are supposed to leave nHotItem as is! */
4269 /* send LVN_DELETEALLITEMS notification */
4270 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4272 bSuppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
4274 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
4276 /* send LVN_DELETEITEM notification, if not suppressed */
4277 if (!bSuppress
) notify_deleteitem(infoPtr
, i
);
4278 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4280 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
4281 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
4283 hdrItem
= (ITEMHDR
*)DPA_GetPtr(hdpaSubItems
, j
);
4284 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4287 DPA_Destroy(hdpaSubItems
);
4288 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
4290 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
4291 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
4292 infoPtr
->nItemCount
--;
4295 LISTVIEW_UpdateScroll(infoPtr
);
4297 LISTVIEW_InvalidateList(infoPtr
);
4304 * Scrolls, and updates the columns, when a column is changing width.
4307 * [I] infoPtr : valid pointer to the listview structure
4308 * [I] nColumn : column to scroll
4309 * [I] dx : amount of scroll, in pixels
4314 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
4316 COLUMN_INFO
*lpColumnInfo
;
4321 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
4322 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
4323 rcCol
= lpColumnInfo
->rcHeader
;
4324 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
4325 rcCol
.left
= rcCol
.right
;
4327 /* ajust the other columns */
4328 for (nCol
= nColumn
; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
4330 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
4331 lpColumnInfo
->rcHeader
.left
+= dx
;
4332 lpColumnInfo
->rcHeader
.right
+= dx
;
4335 /* do not update screen if not in report mode */
4336 if (!is_redrawing(infoPtr
) || (infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return;
4338 /* if we have a focus, must first erase the focus rect */
4339 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
4341 /* Need to reset the item width when inserting a new column */
4342 infoPtr
->nItemWidth
+= dx
;
4344 LISTVIEW_UpdateScroll(infoPtr
);
4345 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
4347 /* scroll to cover the deleted column, and invalidate for redraw */
4348 rcOld
= infoPtr
->rcList
;
4349 rcOld
.left
= ptOrigin
.x
+ rcCol
.left
+ dx
;
4350 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4352 /* we can restore focus now */
4353 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
4358 * Removes a column from the listview control.
4361 * [I] infoPtr : valid pointer to the listview structure
4362 * [I] nColumn : column index
4368 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4372 TRACE("nColumn=%d\n", nColumn
);
4374 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0
4375 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4377 /* While the MSDN specifically says that column zero should not be deleted,
4378 what actually happens is that the column itself is deleted but no items or subitems
4382 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
4384 if (!Header_DeleteItem(infoPtr
->hwndHeader
, nColumn
))
4387 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
4388 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
4390 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
4392 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
4394 INT nItem
, nSubItem
, i
;
4396 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
4398 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
4401 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4403 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
4404 if (lpSubItem
->iSubItem
== nColumn
)
4407 lpDelItem
= lpSubItem
;
4409 else if (lpSubItem
->iSubItem
> nColumn
)
4411 lpSubItem
->iSubItem
--;
4415 /* if we found our subitem, zapp it */
4419 if (is_textW(lpDelItem
->hdr
.pszText
))
4420 Free(lpDelItem
->hdr
.pszText
);
4425 /* free dpa memory */
4426 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
4431 /* update the other column info */
4432 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
4433 LISTVIEW_InvalidateList(infoPtr
);
4435 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
4442 * Invalidates the listview after an item's insertion or deletion.
4445 * [I] infoPtr : valid pointer to the listview structure
4446 * [I] nItem : item index
4447 * [I] dir : -1 if deleting, 1 if inserting
4452 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
4454 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4455 INT nPerCol
, nItemCol
, nItemRow
;
4459 /* if we don't refresh, what's the point of scrolling? */
4460 if (!is_redrawing(infoPtr
)) return;
4462 assert (abs(dir
) == 1);
4464 /* arrange icons if autoarrange is on */
4465 if (is_autoarrange(infoPtr
))
4467 BOOL arrange
= TRUE
;
4468 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
4469 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
4470 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4473 /* scrollbars need updating */
4474 LISTVIEW_UpdateScroll(infoPtr
);
4476 /* figure out the item's position */
4477 if (uView
== LVS_REPORT
)
4478 nPerCol
= infoPtr
->nItemCount
+ 1;
4479 else if (uView
== LVS_LIST
)
4480 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
4481 else /* LVS_ICON, or LVS_SMALLICON */
4484 nItemCol
= nItem
/ nPerCol
;
4485 nItemRow
= nItem
% nPerCol
;
4486 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4488 /* move the items below up a slot */
4489 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
4490 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
4491 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
4492 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4493 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4494 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
4495 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4497 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll
), debugrect(&infoPtr
->rcList
));
4498 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4499 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4502 /* report has only that column, so we're done */
4503 if (uView
== LVS_REPORT
) return;
4505 /* now for LISTs, we have to deal with the columns to the right */
4506 rcScroll
.left
= (nItemCol
+ 1) * infoPtr
->nItemWidth
;
4508 rcScroll
.right
= (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
4509 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4510 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4511 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4512 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4513 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4518 * Removes an item from the listview control.
4521 * [I] infoPtr : valid pointer to the listview structure
4522 * [I] nItem : item index
4528 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
4530 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4533 TRACE("(nItem=%d)\n", nItem
);
4535 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
4537 /* remove selection, and focus */
4539 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4540 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
4542 /* send LVN_DELETEITEM notification. */
4543 if (!notify_deleteitem(infoPtr
, nItem
)) return FALSE
;
4545 /* we need to do this here, because we'll be deleting stuff */
4546 if (uView
== LVS_SMALLICON
|| uView
== LVS_ICON
)
4547 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4549 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4555 hdpaSubItems
= (HDPA
)DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
4556 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4558 hdrItem
= (ITEMHDR
*)DPA_GetPtr(hdpaSubItems
, i
);
4559 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4562 DPA_Destroy(hdpaSubItems
);
4565 if (uView
== LVS_SMALLICON
|| uView
== LVS_ICON
)
4567 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
4568 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
4571 infoPtr
->nItemCount
--;
4572 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
4574 /* now is the invalidation fun */
4575 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
4582 * Callback implementation for editlabel control
4585 * [I] infoPtr : valid pointer to the listview structure
4586 * [I] pszText : modified text
4587 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4593 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, LPWSTR pszText
, BOOL isW
)
4595 HWND hwndSelf
= infoPtr
->hwndSelf
;
4596 NMLVDISPINFOW dispInfo
;
4598 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
4600 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4601 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4602 dispInfo
.item
.iItem
= infoPtr
->nEditLabelItem
;
4603 dispInfo
.item
.iSubItem
= 0;
4604 dispInfo
.item
.stateMask
= ~0;
4605 if (!LISTVIEW_GetItemW(infoPtr
, &dispInfo
.item
)) return FALSE
;
4606 /* add the text from the edit in */
4607 dispInfo
.item
.mask
|= LVIF_TEXT
;
4608 dispInfo
.item
.pszText
= pszText
;
4609 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4611 /* Do we need to update the Item Text */
4612 if (!notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
)) return FALSE
;
4613 if (!IsWindow(hwndSelf
))
4615 if (!pszText
) return TRUE
;
4617 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4619 HDPA hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nEditLabelItem
);
4620 ITEM_INFO
* lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
4621 if (lpItem
&& lpItem
->hdr
.pszText
== LPSTR_TEXTCALLBACKW
)
4623 LISTVIEW_InvalidateItem(infoPtr
, infoPtr
->nEditLabelItem
);
4628 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4629 dispInfo
.item
.mask
= LVIF_TEXT
;
4630 dispInfo
.item
.iItem
= infoPtr
->nEditLabelItem
;
4631 dispInfo
.item
.iSubItem
= 0;
4632 dispInfo
.item
.pszText
= pszText
;
4633 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4634 return LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
4639 * Begin in place editing of specified list view item
4642 * [I] infoPtr : valid pointer to the listview structure
4643 * [I] nItem : item index
4644 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4650 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
4652 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
4653 NMLVDISPINFOW dispInfo
;
4655 HWND hwndSelf
= infoPtr
->hwndSelf
;
4657 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
4659 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
4660 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
4662 infoPtr
->nEditLabelItem
= nItem
;
4664 /* Is the EditBox still there, if so remove it */
4665 if(infoPtr
->hwndEdit
!= 0)
4667 SetFocus(infoPtr
->hwndSelf
);
4668 infoPtr
->hwndEdit
= 0;
4671 LISTVIEW_SetSelection(infoPtr
, nItem
);
4672 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
4673 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4675 rect
.left
= LVIR_LABEL
;
4676 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
4678 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4679 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4680 dispInfo
.item
.iItem
= nItem
;
4681 dispInfo
.item
.iSubItem
= 0;
4682 dispInfo
.item
.stateMask
= ~0;
4683 dispInfo
.item
.pszText
= szDispText
;
4684 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
4685 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
4687 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, WS_VISIBLE
,
4688 rect
.left
-2, rect
.top
-1, 0, rect
.bottom
- rect
.top
+2, isW
);
4689 if (!infoPtr
->hwndEdit
) return 0;
4691 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
4693 if (!IsWindow(hwndSelf
))
4695 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
4696 infoPtr
->hwndEdit
= 0;
4700 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
4701 SetFocus(infoPtr
->hwndEdit
);
4702 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
4703 return infoPtr
->hwndEdit
;
4709 * Ensures the specified item is visible, scrolling into view if necessary.
4712 * [I] infoPtr : valid pointer to the listview structure
4713 * [I] nItem : item index
4714 * [I] bPartial : partially or entirely visible
4720 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
4722 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4723 INT nScrollPosHeight
= 0;
4724 INT nScrollPosWidth
= 0;
4725 INT nHorzAdjust
= 0;
4726 INT nVertAdjust
= 0;
4729 RECT rcItem
, rcTemp
;
4731 rcItem
.left
= LVIR_BOUNDS
;
4732 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
4734 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
4736 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
4738 /* scroll left/right, but in LVS_REPORT mode */
4739 if (uView
== LVS_LIST
)
4740 nScrollPosWidth
= infoPtr
->nItemWidth
;
4741 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
4742 nScrollPosWidth
= 1;
4744 if (rcItem
.left
< infoPtr
->rcList
.left
)
4747 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
4752 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
4756 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
4758 /* scroll up/down, but not in LVS_LIST mode */
4759 if (uView
== LVS_REPORT
)
4760 nScrollPosHeight
= infoPtr
->nItemHeight
;
4761 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
4762 nScrollPosHeight
= 1;
4764 if (rcItem
.top
< infoPtr
->rcList
.top
)
4767 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
4772 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
4776 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
4778 if (nScrollPosWidth
)
4780 INT diff
= nHorzDiff
/ nScrollPosWidth
;
4781 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
4782 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
4785 if (nScrollPosHeight
)
4787 INT diff
= nVertDiff
/ nScrollPosHeight
;
4788 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
4789 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
4797 * Searches for an item with specific characteristics.
4800 * [I] hwnd : window handle
4801 * [I] nStart : base item index
4802 * [I] lpFindInfo : item information to look for
4805 * SUCCESS : index of item
4808 static INT
LISTVIEW_FindItemW(LISTVIEW_INFO
*infoPtr
, INT nStart
,
4809 const LVFINDINFOW
*lpFindInfo
)
4811 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4812 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
4813 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
4814 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
4815 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
4816 POINT Position
, Destination
;
4819 if (!lpFindInfo
|| nItem
< 0) return -1;
4822 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
))
4824 lvItem
.mask
|= LVIF_TEXT
;
4825 lvItem
.pszText
= szDispText
;
4826 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4829 if (lpFindInfo
->flags
& LVFI_WRAP
)
4832 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
4833 (uView
== LVS_ICON
|| uView
==LVS_SMALLICON
))
4838 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4839 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
4840 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
4841 switch(lpFindInfo
->vkDirection
)
4843 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
4844 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
4845 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
4846 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
4847 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
4848 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
4849 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
4851 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
4852 Destination
.x
= rcArea
.right
;
4853 Destination
.y
= rcArea
.bottom
;
4855 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
4859 else Destination
.x
= Destination
.y
= 0;
4861 /* if LVFI_PARAM is specified, all other flags are ignored */
4862 if (lpFindInfo
->flags
& LVFI_PARAM
)
4864 lvItem
.mask
|= LVIF_PARAM
;
4866 lvItem
.mask
&= ~LVIF_TEXT
;
4870 for (; nItem
< nLast
; nItem
++)
4872 lvItem
.iItem
= nItem
;
4873 lvItem
.iSubItem
= 0;
4874 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
4876 if (lvItem
.mask
& LVIF_PARAM
)
4878 if (lpFindInfo
->lParam
== lvItem
.lParam
)
4884 if (lvItem
.mask
& LVIF_TEXT
)
4886 if (lpFindInfo
->flags
& LVFI_PARTIAL
)
4888 if (strstrW(lvItem
.pszText
, lpFindInfo
->psz
) == NULL
) continue;
4892 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
4896 if (!bNearest
) return nItem
;
4898 /* This is very inefficient. To do a good job here,
4899 * we need a sorted array of (x,y) item positions */
4900 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
4902 /* compute the distance^2 to the destination */
4903 xdist
= Destination
.x
- Position
.x
;
4904 ydist
= Destination
.y
- Position
.y
;
4905 dist
= xdist
* xdist
+ ydist
* ydist
;
4907 /* remember the distance, and item if it's closer */
4911 nNearestItem
= nItem
;
4918 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
4923 return nNearestItem
;
4928 * Searches for an item with specific characteristics.
4931 * [I] hwnd : window handle
4932 * [I] nStart : base item index
4933 * [I] lpFindInfo : item information to look for
4936 * SUCCESS : index of item
4939 static INT
LISTVIEW_FindItemA(LISTVIEW_INFO
*infoPtr
, INT nStart
,
4940 const LVFINDINFOA
*lpFindInfo
)
4942 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
);
4946 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
4947 if (hasText
) fiw
.psz
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
4948 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
4949 if (hasText
) textfreeT((LPWSTR
)fiw
.psz
, FALSE
);
4955 * Retrieves the background image of the listview control.
4958 * [I] infoPtr : valid pointer to the listview structure
4959 * [O] lpBkImage : background image attributes
4965 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4967 /* FIXME (listview, "empty stub!\n"); */
4973 * Retrieves column attributes.
4976 * [I] infoPtr : valid pointer to the listview structure
4977 * [I] nColumn : column index
4978 * [IO] lpColumn : column information
4979 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4980 * otherwise it is in fact a LPLVCOLUMNA
4986 static BOOL
LISTVIEW_GetColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
4988 COLUMN_INFO
*lpColumnInfo
;
4991 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4992 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
4994 /* initialize memory */
4995 ZeroMemory(&hdi
, sizeof(hdi
));
4997 if (lpColumn
->mask
& LVCF_TEXT
)
4999 hdi
.mask
|= HDI_TEXT
;
5000 hdi
.pszText
= lpColumn
->pszText
;
5001 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
5004 if (lpColumn
->mask
& LVCF_IMAGE
)
5005 hdi
.mask
|= HDI_IMAGE
;
5007 if (lpColumn
->mask
& LVCF_ORDER
)
5008 hdi
.mask
|= HDI_ORDER
;
5010 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
5012 if (lpColumn
->mask
& LVCF_FMT
)
5013 lpColumn
->fmt
= lpColumnInfo
->fmt
;
5015 if (lpColumn
->mask
& LVCF_WIDTH
)
5016 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
5018 if (lpColumn
->mask
& LVCF_IMAGE
)
5019 lpColumn
->iImage
= hdi
.iImage
;
5021 if (lpColumn
->mask
& LVCF_ORDER
)
5022 lpColumn
->iOrder
= hdi
.iOrder
;
5028 static BOOL
LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
5035 /* FIXME: little hack */
5036 for (i
= 0; i
< iCount
; i
++)
5044 * Retrieves the column width.
5047 * [I] infoPtr : valid pointer to the listview structure
5048 * [I] int : column index
5051 * SUCCESS : column width
5054 static INT
LISTVIEW_GetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
5056 INT nColumnWidth
= 0;
5059 TRACE("nColumn=%d\n", nColumn
);
5061 /* we have a 'column' in LIST and REPORT mode only */
5062 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
)
5065 nColumnWidth
= infoPtr
->nItemWidth
;
5068 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
5069 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
5070 nColumnWidth
= rcHeader
.right
- rcHeader
.left
;
5074 TRACE("nColumnWidth=%d\n", nColumnWidth
);
5075 return nColumnWidth
;
5080 * In list or report display mode, retrieves the number of items that can fit
5081 * vertically in the visible area. In icon or small icon display mode,
5082 * retrieves the total number of visible items.
5085 * [I] infoPtr : valid pointer to the listview structure
5088 * Number of fully visible items.
5090 static INT
LISTVIEW_GetCountPerPage(LISTVIEW_INFO
*infoPtr
)
5092 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
5096 return infoPtr
->nItemCount
;
5098 return LISTVIEW_GetCountPerColumn(infoPtr
);
5100 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
5108 * Retrieves an image list handle.
5111 * [I] infoPtr : valid pointer to the listview structure
5112 * [I] nImageList : image list identifier
5115 * SUCCESS : image list handle
5118 static HIMAGELIST
LISTVIEW_GetImageList(LISTVIEW_INFO
*infoPtr
, INT nImageList
)
5122 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
5123 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
5124 case LVSIL_STATE
: return infoPtr
->himlState
;
5129 /* LISTVIEW_GetISearchString */
5133 * Retrieves item attributes.
5136 * [I] hwnd : window handle
5137 * [IO] lpLVItem : item info
5138 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5139 * if FALSE, the lpLVItem is a LPLVITEMA.
5142 * This is the internal 'GetItem' interface -- it tries to
5143 * be smart and avoid text copies, if possible, by modifying
5144 * lpLVItem->pszText to point to the text string. Please note
5145 * that this is not always possible (e.g. OWNERDATA), so on
5146 * entry you *must* supply valid values for pszText, and cchTextMax.
5147 * The only difference to the documented interface is that upon
5148 * return, you should use *only* the lpLVItem->pszText, rather than
5149 * the buffer pointer you provided on input. Most code already does
5150 * that, so it's not a problem.
5151 * For the two cases when the text must be copied (that is,
5152 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5158 static BOOL
LISTVIEW_GetItemT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5160 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
5161 NMLVDISPINFOW dispInfo
;
5167 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
5169 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5172 if (lpLVItem
->mask
== 0) return TRUE
;
5174 /* make a local copy */
5175 isubitem
= lpLVItem
->iSubItem
;
5177 /* a quick optimization if all we're asked is the focus state
5178 * these queries are worth optimising since they are common,
5179 * and can be answered in constant time, without the heavy accesses */
5180 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
5181 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
5183 lpLVItem
->state
= 0;
5184 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5185 lpLVItem
->state
|= LVIS_FOCUSED
;
5189 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5191 /* if the app stores all the data, handle it separately */
5192 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5194 dispInfo
.item
.state
= 0;
5196 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5197 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) || infoPtr
->uCallbackMask
)
5199 /* NOTE: copy only fields which we _know_ are initialized, some apps
5200 * depend on the uninitialized fields being 0 */
5201 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
5202 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5203 dispInfo
.item
.iSubItem
= isubitem
;
5204 if (lpLVItem
->mask
& LVIF_TEXT
)
5206 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5207 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5209 if (lpLVItem
->mask
& LVIF_STATE
)
5210 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
5211 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5212 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
5213 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
5215 /* full size structure expected - _WIN32IE >= 0x560 */
5216 *lpLVItem
= dispInfo
.item
;
5218 else if (lpLVItem
->mask
& LVIF_INDENT
)
5220 /* indent member expected - _WIN32IE >= 0x300 */
5221 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
5225 /* minimal structure expected */
5226 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
5228 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
5231 /* make sure lParam is zeroed out */
5232 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
5234 /* we store only a little state, so if we're not asked, we're done */
5235 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
5237 /* if focus is handled by us, report it */
5238 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5240 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5241 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5242 lpLVItem
->state
|= LVIS_FOCUSED
;
5245 /* and do the same for selection, if we handle it */
5246 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5248 lpLVItem
->state
&= ~LVIS_SELECTED
;
5249 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5250 lpLVItem
->state
|= LVIS_SELECTED
;
5256 /* find the item and subitem structures before we proceed */
5257 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
5258 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
5263 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
5264 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
5267 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
5272 pItemHdr
= &lpItem
->hdr
;
5274 /* Do we need to query the state from the app? */
5275 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
5277 dispInfo
.item
.mask
|= LVIF_STATE
;
5278 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
5281 /* Do we need to enquire about the image? */
5282 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
5283 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
5285 dispInfo
.item
.mask
|= LVIF_IMAGE
;
5286 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
5289 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5290 if ((lpLVItem
->mask
& LVIF_TEXT
) && !is_textW(pItemHdr
->pszText
))
5292 dispInfo
.item
.mask
|= LVIF_TEXT
;
5293 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5294 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5295 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
5296 *dispInfo
.item
.pszText
= '\0';
5299 /* If we don't have all the requested info, query the application */
5300 if (dispInfo
.item
.mask
!= 0)
5302 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5303 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
5304 dispInfo
.item
.lParam
= lpItem
->lParam
;
5305 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5306 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
5309 /* we should not store values for subitems */
5310 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
5312 /* Now, handle the iImage field */
5313 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
5315 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
5316 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
5317 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
5319 else if (lpLVItem
->mask
& LVIF_IMAGE
)
5321 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
5322 lpLVItem
->iImage
= pItemHdr
->iImage
;
5324 lpLVItem
->iImage
= 0;
5327 /* The pszText field */
5328 if (dispInfo
.item
.mask
& LVIF_TEXT
)
5330 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
5331 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
5333 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
5335 else if (lpLVItem
->mask
& LVIF_TEXT
)
5337 if (isW
) lpLVItem
->pszText
= pItemHdr
->pszText
;
5338 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
5341 /* if this is a subitem, we're done */
5342 if (isubitem
) return TRUE
;
5344 /* Next is the lParam field */
5345 if (dispInfo
.item
.mask
& LVIF_PARAM
)
5347 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
5348 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
5349 lpItem
->lParam
= dispInfo
.item
.lParam
;
5351 else if (lpLVItem
->mask
& LVIF_PARAM
)
5352 lpLVItem
->lParam
= lpItem
->lParam
;
5354 /* ... the state field (this one is different due to uCallbackmask) */
5355 if (lpLVItem
->mask
& LVIF_STATE
)
5357 lpLVItem
->state
= lpItem
->state
;
5358 if (dispInfo
.item
.mask
& LVIF_STATE
)
5360 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
5361 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
5363 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5365 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5366 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5367 lpLVItem
->state
|= LVIS_FOCUSED
;
5369 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5371 lpLVItem
->state
&= ~LVIS_SELECTED
;
5372 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5373 lpLVItem
->state
|= LVIS_SELECTED
;
5377 /* and last, but not least, the indent field */
5378 if (lpLVItem
->mask
& LVIF_INDENT
)
5379 lpLVItem
->iIndent
= lpItem
->iIndent
;
5386 * Retrieves item attributes.
5389 * [I] hwnd : window handle
5390 * [IO] lpLVItem : item info
5391 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5392 * if FALSE, the lpLVItem is a LPLVITEMA.
5395 * This is the external 'GetItem' interface -- it properly copies
5396 * the text in the provided buffer.
5402 static BOOL
LISTVIEW_GetItemExtT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5407 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5410 pszText
= lpLVItem
->pszText
;
5411 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
5412 if (bResult
&& lpLVItem
->pszText
!= pszText
)
5413 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
5414 lpLVItem
->pszText
= pszText
;
5422 * Retrieves the position (upper-left) of the listview control item.
5423 * Note that for LVS_ICON style, the upper-left is that of the icon
5424 * and not the bounding box.
5427 * [I] infoPtr : valid pointer to the listview structure
5428 * [I] nItem : item index
5429 * [O] lpptPosition : coordinate information
5435 static BOOL
LISTVIEW_GetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
5437 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5440 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
5442 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5444 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5445 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
5447 if (uView
== LVS_ICON
)
5449 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
5450 lpptPosition
->y
+= ICON_TOP_PADDING
;
5452 lpptPosition
->x
+= Origin
.x
;
5453 lpptPosition
->y
+= Origin
.y
;
5455 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition
));
5462 * Retrieves the bounding rectangle for a listview control item.
5465 * [I] infoPtr : valid pointer to the listview structure
5466 * [I] nItem : item index
5467 * [IO] lprc : bounding rectangle coordinates
5468 * lprc->left specifies the portion of the item for which the bounding
5469 * rectangle will be retrieved.
5471 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5472 * including the icon and label.
5475 * * Experiment shows that native control returns:
5476 * * width = min (48, length of text line)
5477 * * .left = position.x - (width - iconsize.cx)/2
5478 * * .right = .left + width
5479 * * height = #lines of text * ntmHeight + icon height + 8
5480 * * .top = position.y - 2
5481 * * .bottom = .top + height
5482 * * separation between items .y = itemSpacing.cy - height
5483 * * .x = itemSpacing.cx - width
5484 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5487 * * Experiment shows that native control returns:
5488 * * width = iconSize.cx + 16
5489 * * .left = position.x - (width - iconsize.cx)/2
5490 * * .right = .left + width
5491 * * height = iconSize.cy + 4
5492 * * .top = position.y - 2
5493 * * .bottom = .top + height
5494 * * separation between items .y = itemSpacing.cy - height
5495 * * .x = itemSpacing.cx - width
5496 * LVIR_LABEL Returns the bounding rectangle of the item text.
5499 * * Experiment shows that native control returns:
5500 * * width = text length
5501 * * .left = position.x - width/2
5502 * * .right = .left + width
5503 * * height = ntmH * linecount + 2
5504 * * .top = position.y + iconSize.cy + 6
5505 * * .bottom = .top + height
5506 * * separation between items .y = itemSpacing.cy - height
5507 * * .x = itemSpacing.cx - width
5508 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5509 * rectangles, but excludes columns in report view.
5516 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5517 * upon whether the window has the focus currently and on whether the item
5518 * is the one with the focus. Ensure that the control's record of which
5519 * item has the focus agrees with the items' records.
5521 static BOOL
LISTVIEW_GetItemRect(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5523 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5524 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5525 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
5526 POINT Position
, Origin
;
5530 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
5532 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5534 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5535 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5537 /* Be smart and try to figure out the minimum we have to do */
5538 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
5539 if (uView
== LVS_REPORT
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
5540 if (uView
== LVS_ICON
&& lprc
->left
!= LVIR_ICON
&&
5541 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
5542 oversizedBox
= TRUE
;
5544 /* get what we need from the item before hand, so we make
5545 * only one request. This can speed up things, if data
5546 * is stored on the app side */
5548 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
5549 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
5550 lvItem
.iItem
= nItem
;
5551 lvItem
.iSubItem
= 0;
5552 lvItem
.pszText
= szDispText
;
5553 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5554 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5555 /* we got the state already up, simulate it here, to avoid a reget */
5556 if (uView
== LVS_ICON
&& (lprc
->left
!= LVIR_ICON
))
5558 lvItem
.mask
|= LVIF_STATE
;
5559 lvItem
.stateMask
= LVIS_FOCUSED
;
5560 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
5563 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
5564 lprc
->left
= LVIR_BOUNDS
;
5568 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
);
5572 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, lprc
);
5576 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
);
5579 case LVIR_SELECTBOUNDS
:
5580 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, &label_rect
);
5581 UnionRect(lprc
, lprc
, &label_rect
);
5585 WARN("Unknown value: %ld\n", lprc
->left
);
5589 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
5591 TRACE(" rect=%s\n", debugrect(lprc
));
5598 * Retrieves the spacing between listview control items.
5601 * [I] infoPtr : valid pointer to the listview structure
5602 * [IO] lprc : rectangle to receive the output
5603 * on input, lprc->top = nSubItem
5604 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5606 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5607 * not only those of the first column.
5608 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5614 static BOOL
LISTVIEW_GetSubItemRect(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5618 INT nColumn
= lprc
->top
;
5620 if (!lprc
) return FALSE
;
5622 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem
, lprc
->top
);
5623 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5625 return LISTVIEW_GetItemRect(infoPtr
, nItem
, lprc
);
5627 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return FALSE
;
5629 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &Position
)) return FALSE
;
5631 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5634 lvItem
.iItem
= nItem
;
5635 lvItem
.iSubItem
= nColumn
;
5637 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5641 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
);
5646 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
);
5650 ERR("Unknown bounds=%ld\n", lprc
->left
);
5654 OffsetRect(lprc
, Position
.x
, Position
.y
);
5661 * Retrieves the width of a label.
5664 * [I] infoPtr : valid pointer to the listview structure
5667 * SUCCESS : string width (in pixels)
5670 static INT
LISTVIEW_GetLabelWidth(LISTVIEW_INFO
*infoPtr
, INT nItem
)
5672 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5675 TRACE("(nItem=%d)\n", nItem
);
5677 lvItem
.mask
= LVIF_TEXT
;
5678 lvItem
.iItem
= nItem
;
5679 lvItem
.iSubItem
= 0;
5680 lvItem
.pszText
= szDispText
;
5681 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5682 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5684 return LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
5689 * Retrieves the spacing between listview control items.
5692 * [I] infoPtr : valid pointer to the listview structure
5693 * [I] bSmall : flag for small or large icon
5696 * Horizontal + vertical spacing
5698 static LONG
LISTVIEW_GetItemSpacing(LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
5704 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
5708 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
5709 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
5711 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
5718 * Retrieves the state of a listview control item.
5721 * [I] infoPtr : valid pointer to the listview structure
5722 * [I] nItem : item index
5723 * [I] uMask : state mask
5726 * State specified by the mask.
5728 static UINT
LISTVIEW_GetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
5732 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
5734 lvItem
.iItem
= nItem
;
5735 lvItem
.iSubItem
= 0;
5736 lvItem
.mask
= LVIF_STATE
;
5737 lvItem
.stateMask
= uMask
;
5738 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5740 return lvItem
.state
& uMask
;
5745 * Retrieves the text of a listview control item or subitem.
5748 * [I] hwnd : window handle
5749 * [I] nItem : item index
5750 * [IO] lpLVItem : item information
5751 * [I] isW : TRUE if lpLVItem is Unicode
5754 * SUCCESS : string length
5757 static INT
LISTVIEW_GetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
5759 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
5761 lpLVItem
->mask
= LVIF_TEXT
;
5762 lpLVItem
->iItem
= nItem
;
5763 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
5765 return textlenT(lpLVItem
->pszText
, isW
);
5770 * Searches for an item based on properties + relationships.
5773 * [I] infoPtr : valid pointer to the listview structure
5774 * [I] nItem : item index
5775 * [I] uFlags : relationship flag
5778 * SUCCESS : item index
5781 static INT
LISTVIEW_GetNextItem(LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
5783 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5785 LVFINDINFOW lvFindInfo
;
5786 INT nCountPerColumn
;
5790 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
5791 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
5793 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
5795 if (uFlags
& LVNI_CUT
)
5798 if (uFlags
& LVNI_DROPHILITED
)
5799 uMask
|= LVIS_DROPHILITED
;
5801 if (uFlags
& LVNI_FOCUSED
)
5802 uMask
|= LVIS_FOCUSED
;
5804 if (uFlags
& LVNI_SELECTED
)
5805 uMask
|= LVIS_SELECTED
;
5807 /* if we're asked for the focused item, that's only one,
5808 * so it's worth optimizing */
5809 if (uFlags
& LVNI_FOCUSED
)
5811 if ((LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) != uMask
) return -1;
5812 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
5815 if (uFlags
& LVNI_ABOVE
)
5817 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
5822 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5828 /* Special case for autoarrange - move 'til the top of a list */
5829 if (is_autoarrange(infoPtr
))
5831 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5832 while (nItem
- nCountPerRow
>= 0)
5834 nItem
-= nCountPerRow
;
5835 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5840 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5841 lvFindInfo
.vkDirection
= VK_UP
;
5842 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5843 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5845 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5850 else if (uFlags
& LVNI_BELOW
)
5852 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
5854 while (nItem
< infoPtr
->nItemCount
)
5857 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5863 /* Special case for autoarrange - move 'til the bottom of a list */
5864 if (is_autoarrange(infoPtr
))
5866 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5867 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
5869 nItem
+= nCountPerRow
;
5870 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5875 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5876 lvFindInfo
.vkDirection
= VK_DOWN
;
5877 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5878 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5880 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5885 else if (uFlags
& LVNI_TOLEFT
)
5887 if (uView
== LVS_LIST
)
5889 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
5890 while (nItem
- nCountPerColumn
>= 0)
5892 nItem
-= nCountPerColumn
;
5893 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5897 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5899 /* Special case for autoarrange - move 'ti the beginning of a row */
5900 if (is_autoarrange(infoPtr
))
5902 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5903 while (nItem
% nCountPerRow
> 0)
5906 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5911 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5912 lvFindInfo
.vkDirection
= VK_LEFT
;
5913 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5914 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5916 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5921 else if (uFlags
& LVNI_TORIGHT
)
5923 if (uView
== LVS_LIST
)
5925 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
5926 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
5928 nItem
+= nCountPerColumn
;
5929 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5933 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5935 /* Special case for autoarrange - move 'til the end of a row */
5936 if (is_autoarrange(infoPtr
))
5938 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5939 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
5942 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5947 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5948 lvFindInfo
.vkDirection
= VK_RIGHT
;
5949 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5950 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5952 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5961 /* search by index */
5962 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
5964 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
5972 /* LISTVIEW_GetNumberOfWorkAreas */
5976 * Retrieves the origin coordinates when in icon or small icon display mode.
5979 * [I] infoPtr : valid pointer to the listview structure
5980 * [O] lpptOrigin : coordinate information
5985 static void LISTVIEW_GetOrigin(LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
5987 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5988 INT nHorzPos
= 0, nVertPos
= 0;
5989 SCROLLINFO scrollInfo
;
5991 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
5992 scrollInfo
.fMask
= SIF_POS
;
5994 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
5995 nHorzPos
= scrollInfo
.nPos
;
5996 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
5997 nVertPos
= scrollInfo
.nPos
;
5999 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
6001 lpptOrigin
->x
= infoPtr
->rcList
.left
;
6002 lpptOrigin
->y
= infoPtr
->rcList
.top
;
6003 if (uView
== LVS_LIST
)
6004 nHorzPos
*= infoPtr
->nItemWidth
;
6005 else if (uView
== LVS_REPORT
)
6006 nVertPos
*= infoPtr
->nItemHeight
;
6008 lpptOrigin
->x
-= nHorzPos
;
6009 lpptOrigin
->y
-= nVertPos
;
6011 TRACE(" origin=%s\n", debugpoint(lpptOrigin
));
6016 * Retrieves the width of a string.
6019 * [I] hwnd : window handle
6020 * [I] lpszText : text string to process
6021 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6024 * SUCCESS : string width (in pixels)
6027 static INT
LISTVIEW_GetStringWidthT(LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
6032 if (is_textT(lpszText
, isW
))
6034 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
6035 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
6036 HFONT hOldFont
= SelectObject(hdc
, hFont
);
6039 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
6041 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
6042 SelectObject(hdc
, hOldFont
);
6043 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6045 return stringSize
.cx
;
6050 * Determines which listview item is located at the specified position.
6053 * [I] infoPtr : valid pointer to the listview structure
6054 * [IO] lpht : hit test information
6055 * [I] subitem : fill out iSubItem.
6056 * [I] select : return the index only if the hit selects the item
6059 * (mm 20001022): We must not allow iSubItem to be touched, for
6060 * an app might pass only a structure with space up to iItem!
6061 * (MS Office 97 does that for instance in the file open dialog)
6064 * SUCCESS : item index
6067 static INT
LISTVIEW_HitTest(LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
6069 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6070 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6071 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
6072 POINT Origin
, Position
, opt
;
6077 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht
->pt
), subitem
, select
);
6081 if (subitem
) lpht
->iSubItem
= 0;
6083 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
6084 lpht
->flags
|= LVHT_TOLEFT
;
6085 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
6086 lpht
->flags
|= LVHT_TORIGHT
;
6088 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
6089 lpht
->flags
|= LVHT_ABOVE
;
6090 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
6091 lpht
->flags
|= LVHT_BELOW
;
6093 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6094 if (lpht
->flags
) return -1;
6096 lpht
->flags
|= LVHT_NOWHERE
;
6098 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6100 /* first deal with the large items */
6101 rcSearch
.left
= lpht
->pt
.x
;
6102 rcSearch
.top
= lpht
->pt
.y
;
6103 rcSearch
.right
= rcSearch
.left
+ 1;
6104 rcSearch
.bottom
= rcSearch
.top
+ 1;
6106 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
6107 iterator_next(&i
); /* go to first item in the sequence */
6109 iterator_destroy(&i
);
6111 TRACE("lpht->iItem=%d\n", iItem
);
6112 if (iItem
== -1) return -1;
6114 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
6115 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
6116 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
6117 if (uView
== LVS_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
6118 lvItem
.iItem
= iItem
;
6119 lvItem
.iSubItem
= 0;
6120 lvItem
.pszText
= szDispText
;
6121 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6122 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
6123 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
6125 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcState
, &rcIcon
, &rcLabel
);
6126 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
6127 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
6128 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
6130 if (uView
== LVS_REPORT
)
6133 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6134 TRACE("rcBounds=%s\n", debugrect(&rcBounds
));
6135 if (!PtInRect(&rcBounds
, opt
)) return -1;
6137 if (PtInRect(&rcIcon
, opt
))
6138 lpht
->flags
|= LVHT_ONITEMICON
;
6139 else if (PtInRect(&rcLabel
, opt
))
6140 lpht
->flags
|= LVHT_ONITEMLABEL
;
6141 else if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && PtInRect(&rcState
, opt
))
6142 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
6143 if (lpht
->flags
& LVHT_ONITEM
)
6144 lpht
->flags
&= ~LVHT_NOWHERE
;
6146 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6147 if (uView
== LVS_REPORT
&& subitem
)
6151 rcBounds
.right
= rcBounds
.left
;
6152 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
6154 rcBounds
.left
= rcBounds
.right
;
6155 rcBounds
.right
+= LISTVIEW_GetColumnWidth(infoPtr
, j
);
6156 if (PtInRect(&rcBounds
, opt
))
6164 if (select
&& !(uView
== LVS_REPORT
&&
6165 ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) ||
6166 (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))))
6168 if (uView
== LVS_REPORT
)
6170 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6171 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6173 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
6175 return lpht
->iItem
= iItem
;
6179 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6180 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6181 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6182 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6183 their own sort proc. when sending LVM_SORTITEMS.
6186 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6188 LVS_SORTXXX must be specified,
6189 LVS_OWNERDRAW is not set,
6190 <item>.pszText is not LPSTR_TEXTCALLBACK.
6192 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6193 are sorted based on item text..."
6195 static INT WINAPI
LISTVIEW_InsertCompare( LPVOID first
, LPVOID second
, LPARAM lParam
)
6197 ITEM_INFO
* lv_first
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)first
, 0 );
6198 ITEM_INFO
* lv_second
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)second
, 0 );
6199 INT cmpv
= textcmpWT(lv_first
->hdr
.pszText
, lv_second
->hdr
.pszText
, TRUE
);
6201 /* if we're sorting descending, negate the return value */
6202 return (((LISTVIEW_INFO
*)lParam
)->dwStyle
& LVS_SORTDESCENDING
) ? -cmpv
: cmpv
;
6207 * Inserts a new item in the listview control.
6210 * [I] infoPtr : valid pointer to the listview structure
6211 * [I] lpLVItem : item information
6212 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6215 * SUCCESS : new item index
6218 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
6220 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6225 BOOL is_sorted
, has_changed
;
6227 HWND hwndSelf
= infoPtr
->hwndSelf
;
6229 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6231 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
6233 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6234 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iSubItem
) return -1;
6236 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
6238 if (!(lpItem
= (ITEM_INFO
*)Alloc(sizeof(ITEM_INFO
)))) return -1;
6240 /* insert item in listview control data structure */
6241 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
6242 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
6244 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
6245 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
6247 nItem
= is_sorted
? infoPtr
->nItemCount
: min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
6248 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
6249 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
6250 if (nItem
== -1) goto fail
;
6251 infoPtr
->nItemCount
++;
6253 /* shift indices first so they don't get tangled */
6254 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
6256 /* set the item attributes */
6257 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6259 /* full size structure expected - _WIN32IE >= 0x560 */
6262 else if (lpLVItem
->mask
& LVIF_INDENT
)
6264 /* indent member expected - _WIN32IE >= 0x300 */
6265 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
6269 /* minimal structure expected */
6270 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
6273 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) item
.state
&= ~LVIS_STATEIMAGEMASK
;
6274 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
6276 /* if we're sorted, sort the list, and update the index */
6279 DPA_Sort( infoPtr
->hdpaItems
, LISTVIEW_InsertCompare
, (LPARAM
)infoPtr
);
6280 nItem
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, hdpaSubItems
);
6281 assert(nItem
!= -1);
6284 /* make room for the position, if we are in the right mode */
6285 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6287 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
6289 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
6291 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
6296 /* send LVN_INSERTITEM notification */
6297 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
6299 nmlv
.lParam
= lpItem
->lParam
;
6300 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
6301 if (!IsWindow(hwndSelf
))
6304 /* align items (set position of each item) */
6305 if ((uView
== LVS_SMALLICON
|| uView
== LVS_ICON
))
6309 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
6310 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
6312 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
6314 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
6317 /* now is the invalidation fun */
6318 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
6322 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
6323 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
6324 infoPtr
->nItemCount
--;
6326 DPA_DeletePtr(hdpaSubItems
, 0);
6327 DPA_Destroy (hdpaSubItems
);
6334 * Redraws a range of items.
6337 * [I] infoPtr : valid pointer to the listview structure
6338 * [I] nFirst : first item
6339 * [I] nLast : last item
6345 static BOOL
LISTVIEW_RedrawItems(LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
6349 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
6350 max(nFirst
, nLast
) >= infoPtr
->nItemCount
)
6353 for (i
= nFirst
; i
<= nLast
; i
++)
6354 LISTVIEW_InvalidateItem(infoPtr
, i
);
6361 * Scroll the content of a listview.
6364 * [I] infoPtr : valid pointer to the listview structure
6365 * [I] dx : horizontal scroll amount in pixels
6366 * [I] dy : vertical scroll amount in pixels
6373 * If the control is in report mode (LVS_REPORT) the control can
6374 * be scrolled only in line increments. "dy" will be rounded to the
6375 * nearest number of pixels that are a whole line. Ex: if line height
6376 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6377 * is passed the the scroll will be 0. (per MSDN 7/2002)
6379 * For: (per experimentaion with native control and CSpy ListView)
6380 * LVS_ICON dy=1 = 1 pixel (vertical only)
6382 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6384 * LVS_LIST dx=1 = 1 column (horizontal only)
6385 * but will only scroll 1 column per message
6386 * no matter what the value.
6387 * dy must be 0 or FALSE returned.
6388 * LVS_REPORT dx=1 = 1 pixel
6392 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
6394 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
) {
6396 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
6397 dy
/= infoPtr
->nItemHeight
;
6400 if (dy
!= 0) return FALSE
;
6407 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
, 0);
6408 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
, 0);
6415 * Sets the background color.
6418 * [I] infoPtr : valid pointer to the listview structure
6419 * [I] clrBk : background color
6425 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrBk
)
6427 TRACE("(clrBk=%lx)\n", clrBk
);
6429 if(infoPtr
->clrBk
!= clrBk
) {
6430 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
6431 infoPtr
->clrBk
= clrBk
;
6432 if (clrBk
== CLR_NONE
)
6433 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
6435 infoPtr
->hBkBrush
= CreateSolidBrush(clrBk
);
6436 LISTVIEW_InvalidateList(infoPtr
);
6442 /* LISTVIEW_SetBkImage */
6444 /*** Helper for {Insert,Set}ColumnT *only* */
6445 static void column_fill_hditem(LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
, const LVCOLUMNW
*lpColumn
, BOOL isW
)
6447 if (lpColumn
->mask
& LVCF_FMT
)
6449 /* format member is valid */
6450 lphdi
->mask
|= HDI_FORMAT
;
6452 /* set text alignment (leftmost column must be left-aligned) */
6453 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
6454 lphdi
->fmt
|= HDF_LEFT
;
6455 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
6456 lphdi
->fmt
|= HDF_RIGHT
;
6457 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
6458 lphdi
->fmt
|= HDF_CENTER
;
6460 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6461 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
6463 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6465 lphdi
->fmt
|= HDF_IMAGE
;
6466 lphdi
->iImage
= I_IMAGECALLBACK
;
6470 if (lpColumn
->mask
& LVCF_WIDTH
)
6472 lphdi
->mask
|= HDI_WIDTH
;
6473 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
6475 /* make it fill the remainder of the controls width */
6479 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
6481 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
6482 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
6485 /* retrieve the layout of the header */
6486 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
6487 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, debugrect(&rcHeader
));
6489 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
6492 lphdi
->cxy
= lpColumn
->cx
;
6495 if (lpColumn
->mask
& LVCF_TEXT
)
6497 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
6498 lphdi
->fmt
|= HDF_STRING
;
6499 lphdi
->pszText
= lpColumn
->pszText
;
6500 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
6503 if (lpColumn
->mask
& LVCF_IMAGE
)
6505 lphdi
->mask
|= HDI_IMAGE
;
6506 lphdi
->iImage
= lpColumn
->iImage
;
6509 if (lpColumn
->mask
& LVCF_ORDER
)
6511 lphdi
->mask
|= HDI_ORDER
;
6512 lphdi
->iOrder
= lpColumn
->iOrder
;
6519 * Inserts a new column.
6522 * [I] infoPtr : valid pointer to the listview structure
6523 * [I] nColumn : column index
6524 * [I] lpColumn : column information
6525 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6528 * SUCCESS : new column index
6531 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6532 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6534 COLUMN_INFO
*lpColumnInfo
;
6538 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6540 if (!lpColumn
|| nColumn
< 0) return -1;
6541 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
6543 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6544 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6546 /* insert item in header control */
6547 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
6548 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
6549 (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6550 if (nNewColumn
== -1) return -1;
6551 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
6553 /* create our own column info */
6554 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
6555 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
6557 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
6558 if (!Header_GetItemRect(infoPtr
->hwndHeader
, nNewColumn
, &lpColumnInfo
->rcHeader
)) goto fail
;
6560 /* now we have to actually adjust the data */
6561 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
6563 SUBITEM_INFO
*lpSubItem
;
6567 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
6569 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
6570 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
6572 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
6573 if (lpSubItem
->iSubItem
>= nNewColumn
)
6574 lpSubItem
->iSubItem
++;
6579 /* make space for the new column */
6580 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
6585 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
6588 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
6596 * Sets the attributes of a header item.
6599 * [I] infoPtr : valid pointer to the listview structure
6600 * [I] nColumn : column index
6601 * [I] lpColumn : column attributes
6602 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6608 static BOOL
LISTVIEW_SetColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6609 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6611 HDITEMW hdi
, hdiget
;
6614 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6616 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6618 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6619 if (lpColumn
->mask
& LVCF_FMT
)
6621 hdi
.mask
|= HDI_FORMAT
;
6622 hdiget
.mask
= HDI_FORMAT
;
6623 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdiget
))
6624 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
6626 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6628 /* set header item attributes */
6629 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6630 if (!bResult
) return FALSE
;
6632 if (lpColumn
->mask
& LVCF_FMT
)
6634 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
6635 int oldFmt
= lpColumnInfo
->fmt
;
6637 lpColumnInfo
->fmt
= lpColumn
->fmt
;
6638 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
6640 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6641 if (uView
== LVS_REPORT
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
6650 * Sets the column order array
6653 * [I] infoPtr : valid pointer to the listview structure
6654 * [I] iCount : number of elements in column order array
6655 * [I] lpiArray : pointer to column order array
6661 static BOOL
LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
6663 FIXME("iCount %d lpiArray %p\n", iCount
, lpiArray
);
6674 * Sets the width of a column
6677 * [I] infoPtr : valid pointer to the listview structure
6678 * [I] nColumn : column index
6679 * [I] cx : column width
6685 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
6687 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6688 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
6692 TRACE("(nColumn=%d, cx=%d\n", nColumn
, cx
);
6694 /* set column width only if in report or list mode */
6695 if (uView
!= LVS_REPORT
&& uView
!= LVS_LIST
) return FALSE
;
6697 /* take care of invalid cx values */
6698 if(uView
== LVS_REPORT
&& cx
< -2) cx
= LVSCW_AUTOSIZE
;
6699 else if (uView
== LVS_LIST
&& cx
< 1) return FALSE
;
6701 /* resize all columns if in LVS_LIST mode */
6702 if(uView
== LVS_LIST
)
6704 infoPtr
->nItemWidth
= cx
;
6705 LISTVIEW_InvalidateList(infoPtr
);
6709 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6711 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
6716 lvItem
.mask
= LVIF_TEXT
;
6718 lvItem
.iSubItem
= nColumn
;
6719 lvItem
.pszText
= szDispText
;
6720 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6721 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
6723 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
6724 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
6725 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
6727 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
6728 max_cx
+= infoPtr
->iconSize
.cx
;
6729 max_cx
+= TRAILING_LABEL_PADDING
;
6732 /* autosize based on listview items width */
6733 if(cx
== LVSCW_AUTOSIZE
)
6735 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
6737 /* if iCol is the last column make it fill the remainder of the controls width */
6738 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
6743 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6744 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
6746 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
6750 /* Despite what the MS docs say, if this is not the last
6751 column, then MS resizes the column to the width of the
6752 largest text string in the column, including headers
6753 and items. This is different from LVSCW_AUTOSIZE in that
6754 LVSCW_AUTOSIZE ignores the header string length. */
6757 /* retrieve header text */
6758 hdi
.mask
= HDI_TEXT
;
6759 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
6760 hdi
.pszText
= szDispText
;
6761 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, (LPARAM
)&hdi
))
6763 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
6764 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
6767 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
6768 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
6769 /* FIXME: Take into account the header image, if one is present */
6770 SelectObject(hdc
, old_font
);
6771 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6773 cx
= max (cx
, max_cx
);
6777 if (cx
< 0) return FALSE
;
6779 /* call header to update the column change */
6780 hdi
.mask
= HDI_WIDTH
;
6782 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
6783 return Header_SetItemW(infoPtr
->hwndHeader
, nColumn
, (LPARAM
)&hdi
);
6787 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6790 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO
*infoPtr
)
6793 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
6795 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
6796 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
6799 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
6800 ILC_COLOR
| ILC_MASK
, 2, 2);
6801 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
6802 hdc
= CreateCompatibleDC(hdc_wnd
);
6803 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
6804 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
6805 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
6807 rc
.left
= rc
.top
= 0;
6808 rc
.right
= GetSystemMetrics(SM_CXSMICON
);
6809 rc
.bottom
= GetSystemMetrics(SM_CYSMICON
);
6811 hbm_orig
= SelectObject(hdc
, hbm_mask
);
6812 FillRect(hdc
, &rc
, hbr_white
);
6813 InflateRect(&rc
, -3, -3);
6814 FillRect(hdc
, &rc
, hbr_black
);
6816 SelectObject(hdc
, hbm_im
);
6817 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
6818 SelectObject(hdc
, hbm_orig
);
6819 ImageList_Add(himl
, hbm_im
, hbm_mask
);
6821 SelectObject(hdc
, hbm_im
);
6822 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
6823 SelectObject(hdc
, hbm_orig
);
6824 ImageList_Add(himl
, hbm_im
, hbm_mask
);
6826 DeleteObject(hbm_mask
);
6827 DeleteObject(hbm_im
);
6835 * Sets the extended listview style.
6838 * [I] infoPtr : valid pointer to the listview structure
6840 * [I] dwStyle : style
6843 * SUCCESS : previous style
6846 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD dwMask
, DWORD dwStyle
)
6848 DWORD dwOldStyle
= infoPtr
->dwLvExStyle
;
6852 infoPtr
->dwLvExStyle
= (dwOldStyle
& ~dwMask
) | (dwStyle
& dwMask
);
6854 infoPtr
->dwLvExStyle
= dwStyle
;
6856 if((infoPtr
->dwLvExStyle
^ dwOldStyle
) & LVS_EX_CHECKBOXES
)
6858 HIMAGELIST himl
= 0;
6859 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
6860 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
6861 LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
6869 * Sets the new hot cursor used during hot tracking and hover selection.
6872 * [I] infoPtr : valid pointer to the listview structure
6873 * [I] hCursor : the new hot cursor handle
6876 * Returns the previous hot cursor
6878 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
6880 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
6882 infoPtr
->hHotCursor
= hCursor
;
6890 * Sets the hot item index.
6893 * [I] infoPtr : valid pointer to the listview structure
6894 * [I] iIndex : index
6897 * SUCCESS : previous hot item index
6898 * FAILURE : -1 (no hot item)
6900 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
6902 INT iOldIndex
= infoPtr
->nHotItem
;
6904 infoPtr
->nHotItem
= iIndex
;
6912 * Sets the amount of time the cursor must hover over an item before it is selected.
6915 * [I] infoPtr : valid pointer to the listview structure
6916 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6919 * Returns the previous hover time
6921 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
6923 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
6925 infoPtr
->dwHoverTime
= dwHoverTime
;
6927 return oldHoverTime
;
6932 * Sets spacing for icons of LVS_ICON style.
6935 * [I] infoPtr : valid pointer to the listview structure
6936 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6937 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6940 * MAKELONG(oldcx, oldcy)
6942 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
6944 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
6945 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6947 TRACE("requested=(%d,%d)\n", cx
, cy
);
6949 /* this is supported only for LVS_ICON style */
6950 if (uView
!= LVS_ICON
) return oldspacing
;
6952 /* set to defaults, if instructed to */
6953 if (cx
== -1) cx
= GetSystemMetrics(SM_CXICONSPACING
);
6954 if (cy
== -1) cy
= GetSystemMetrics(SM_CYICONSPACING
);
6956 /* if 0 then compute width
6957 * FIXME: Should scan each item and determine max width of
6958 * icon or label, then make that the width */
6960 cx
= infoPtr
->iconSpacing
.cx
;
6962 /* if 0 then compute height */
6964 cy
= infoPtr
->iconSize
.cy
+ 2 * infoPtr
->ntmHeight
+
6965 ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_PADDING
;
6968 infoPtr
->iconSpacing
.cx
= cx
;
6969 infoPtr
->iconSpacing
.cy
= cy
;
6971 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6972 LOWORD(oldspacing
), HIWORD(oldspacing
), cx
, cy
,
6973 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
6974 infoPtr
->ntmHeight
);
6976 /* these depend on the iconSpacing */
6977 LISTVIEW_UpdateItemSize(infoPtr
);
6982 static inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL _small
)
6986 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
6993 size
->cx
= GetSystemMetrics(_small
? SM_CXSMICON
: SM_CXICON
);
6994 size
->cy
= GetSystemMetrics(_small
? SM_CYSMICON
: SM_CYICON
);
7003 * [I] infoPtr : valid pointer to the listview structure
7004 * [I] nType : image list type
7005 * [I] himl : image list handle
7008 * SUCCESS : old image list
7011 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
7013 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7014 INT oldHeight
= infoPtr
->nItemHeight
;
7015 HIMAGELIST himlOld
= 0;
7017 TRACE("(nType=%d, himl=%p\n", nType
, himl
);
7022 himlOld
= infoPtr
->himlNormal
;
7023 infoPtr
->himlNormal
= himl
;
7024 if (uView
== LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
7025 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
7029 himlOld
= infoPtr
->himlSmall
;
7030 infoPtr
->himlSmall
= himl
;
7031 if (uView
!= LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
7035 himlOld
= infoPtr
->himlState
;
7036 infoPtr
->himlState
= himl
;
7037 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
7038 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
7042 ERR("Unknown icon type=%d\n", nType
);
7046 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
7047 if (infoPtr
->nItemHeight
!= oldHeight
)
7048 LISTVIEW_UpdateScroll(infoPtr
);
7055 * Preallocates memory (does *not* set the actual count of items !)
7058 * [I] infoPtr : valid pointer to the listview structure
7059 * [I] nItems : item count (projected number of items to allocate)
7060 * [I] dwFlags : update flags
7066 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
7068 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems
, dwFlags
);
7070 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
7072 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7073 INT nOldCount
= infoPtr
->nItemCount
;
7075 if (nItems
< nOldCount
)
7077 RANGE range
= { nItems
, nOldCount
};
7078 ranges_del(infoPtr
->selectionRanges
, range
);
7079 if (infoPtr
->nFocusedItem
>= nItems
)
7081 infoPtr
->nFocusedItem
= -1;
7082 SetRectEmpty(&infoPtr
->rcFocus
);
7086 infoPtr
->nItemCount
= nItems
;
7087 LISTVIEW_UpdateScroll(infoPtr
);
7089 /* the flags are valid only in ownerdata report and list modes */
7090 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
) dwFlags
= 0;
7092 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
7093 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
7095 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
7096 LISTVIEW_InvalidateList(infoPtr
);
7103 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7104 nFrom
= min(nOldCount
, nItems
);
7105 nTo
= max(nOldCount
, nItems
);
7107 if (uView
== LVS_REPORT
)
7110 rcErase
.top
= nFrom
* infoPtr
->nItemHeight
;
7111 rcErase
.right
= infoPtr
->nItemWidth
;
7112 rcErase
.bottom
= nTo
* infoPtr
->nItemHeight
;
7113 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7114 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7115 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7119 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
7121 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
7122 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
7123 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
7124 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7125 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7126 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7127 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7129 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7131 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7132 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7133 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7134 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7135 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7141 /* According to MSDN for non-LVS_OWNERDATA this is just
7142 * a performance issue. The control allocates its internal
7143 * data structures for the number of items specified. It
7144 * cuts down on the number of memory allocations. Therefore
7145 * we will just issue a WARN here
7147 WARN("for non-ownerdata performance option not implemented.\n");
7155 * Sets the position of an item.
7158 * [I] infoPtr : valid pointer to the listview structure
7159 * [I] nItem : item index
7160 * [I] pt : coordinate
7166 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, POINT pt
)
7168 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7171 TRACE("(nItem=%d, &pt=%s\n", nItem
, debugpoint(&pt
));
7173 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
7174 !(uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)) return FALSE
;
7176 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7178 /* This point value seems to be an undocumented feature.
7179 * The best guess is that it means either at the origin,
7180 * or at true beginning of the list. I will assume the origin. */
7181 if ((pt
.x
== -1) && (pt
.y
== -1))
7184 if (uView
== LVS_ICON
)
7186 pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
7187 pt
.y
-= ICON_TOP_PADDING
;
7192 infoPtr
->bAutoarrange
= FALSE
;
7194 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, FALSE
);
7199 * Sets the state of one or many items.
7202 * [I] infoPtr : valid pointer to the listview structure
7203 * [I] nItem : item index
7204 * [I] lpLVItem : item or subitem info
7210 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
)
7212 BOOL bResult
= TRUE
;
7215 lvItem
.iItem
= nItem
;
7216 lvItem
.iSubItem
= 0;
7217 lvItem
.mask
= LVIF_STATE
;
7218 lvItem
.state
= lpLVItem
->state
;
7219 lvItem
.stateMask
= lpLVItem
->stateMask
;
7220 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
7224 /* apply to all items */
7225 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7226 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) bResult
= FALSE
;
7229 bResult
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
7232 * Update selection mark
7234 * Investigation on windows 2k showed that selection mark was updated
7235 * whenever a new selection was made, but if the selected item was
7236 * unselected it was not updated.
7238 * we are probably still not 100% accurate, but this at least sets the
7239 * proper selection mark when it is needed
7242 if (bResult
&& (lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) &&
7243 ((infoPtr
->nSelectionMark
== -1) || (lvItem
.iItem
<= infoPtr
->nSelectionMark
)))
7246 infoPtr
->nSelectionMark
= -1;
7247 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
7249 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
7251 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
7253 infoPtr
->nSelectionMark
= i
;
7257 else if (ranges_contain(infoPtr
->selectionRanges
, i
))
7259 infoPtr
->nSelectionMark
= i
;
7270 * Sets the text of an item or subitem.
7273 * [I] hwnd : window handle
7274 * [I] nItem : item index
7275 * [I] lpLVItem : item or subitem info
7276 * [I] isW : TRUE if input is Unicode
7282 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
7286 if (nItem
< 0 && nItem
>= infoPtr
->nItemCount
) return FALSE
;
7288 lvItem
.iItem
= nItem
;
7289 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
7290 lvItem
.mask
= LVIF_TEXT
;
7291 lvItem
.pszText
= lpLVItem
->pszText
;
7292 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
7294 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
7296 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
7301 * Set item index that marks the start of a multiple selection.
7304 * [I] infoPtr : valid pointer to the listview structure
7305 * [I] nIndex : index
7308 * Index number or -1 if there is no selection mark.
7310 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
7312 INT nOldIndex
= infoPtr
->nSelectionMark
;
7314 TRACE("(nIndex=%d)\n", nIndex
);
7316 infoPtr
->nSelectionMark
= nIndex
;
7323 * Sets the text background color.
7326 * [I] infoPtr : valid pointer to the listview structure
7327 * [I] clrTextBk : text background color
7333 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrTextBk
)
7335 TRACE("(clrTextBk=%lx)\n", clrTextBk
);
7337 if (infoPtr
->clrTextBk
!= clrTextBk
)
7339 infoPtr
->clrTextBk
= clrTextBk
;
7340 LISTVIEW_InvalidateList(infoPtr
);
7348 * Sets the text foreground color.
7351 * [I] infoPtr : valid pointer to the listview structure
7352 * [I] clrText : text color
7358 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF clrText
)
7360 TRACE("(clrText=%lx)\n", clrText
);
7362 if (infoPtr
->clrText
!= clrText
)
7364 infoPtr
->clrText
= clrText
;
7365 LISTVIEW_InvalidateList(infoPtr
);
7373 * Determines which listview item is located at the specified position.
7376 * [I] infoPtr : valid pointer to the listview structure
7377 * [I] hwndNewToolTip : handle to new ToolTip
7382 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
7384 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
7385 infoPtr
->hwndToolTip
= hwndNewToolTip
;
7386 return hwndOldToolTip
;
7389 /* LISTVIEW_SetUnicodeFormat */
7390 /* LISTVIEW_SetWorkAreas */
7394 * Callback internally used by LISTVIEW_SortItems()
7397 * [I] first : pointer to first ITEM_INFO to compare
7398 * [I] second : pointer to second ITEM_INFO to compare
7399 * [I] lParam : HWND of control
7402 * if first comes before second : negative
7403 * if first comes after second : positive
7404 * if first and second are equivalent : zero
7406 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
7408 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
7409 ITEM_INFO
* lv_first
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)first
, 0 );
7410 ITEM_INFO
* lv_second
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)second
, 0 );
7411 PFNLVCOMPARE CompareFunction
= infoPtr
->pfnCompare
;
7413 /* Forward the call to the client defined callback */
7415 return (CompareFunction
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
7420 * Sorts the listview items.
7423 * [I] infoPtr : valid pointer to the listview structure
7424 * [I] pfnCompare : application-defined value
7425 * [I] lParamSort : pointer to comparision callback
7431 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
, LPARAM lParamSort
)
7433 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7436 LPVOID selectionMarkItem
;
7440 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
7442 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
7444 if (!pfnCompare
) return FALSE
;
7445 if (!infoPtr
->hdpaItems
) return FALSE
;
7447 /* if there are 0 or 1 items, there is no need to sort */
7448 if (infoPtr
->nItemCount
< 2) return TRUE
;
7450 if (infoPtr
->nFocusedItem
>= 0)
7452 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
7453 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
7454 if (lpItem
) lpItem
->state
|= LVIS_FOCUSED
;
7456 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7457 /* clear the lpItem->state for non-selected ones */
7458 /* remove the selection ranges */
7460 infoPtr
->pfnCompare
= pfnCompare
;
7461 infoPtr
->lParamSort
= lParamSort
;
7462 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
7464 /* Adjust selections and indices so that they are the way they should
7465 * be after the sort (otherwise, the list items move around, but
7466 * whatever is at the item's previous original position will be
7469 selectionMarkItem
=(infoPtr
->nSelectionMark
>=0)?DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
):NULL
;
7470 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
7472 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
7473 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
7475 if (lpItem
->state
& LVIS_SELECTED
)
7477 item
.state
= LVIS_SELECTED
;
7478 item
.stateMask
= LVIS_SELECTED
;
7479 LISTVIEW_SetItemState(infoPtr
, i
, &item
);
7481 if (lpItem
->state
& LVIS_FOCUSED
)
7483 infoPtr
->nFocusedItem
= i
;
7484 lpItem
->state
&= ~LVIS_FOCUSED
;
7487 if (selectionMarkItem
!= NULL
)
7488 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
7489 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7491 /* refresh the display */
7492 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
)
7493 LISTVIEW_InvalidateList(infoPtr
);
7500 * Update theme handle after a theme change.
7503 * [I] infoPtr : valid pointer to the listview structure
7507 * FAILURE : something else
7509 static LRESULT
LISTVIEW_ThemeChanged(LISTVIEW_INFO
*infoPtr
)
7511 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
7512 CloseThemeData(theme
);
7513 OpenThemeData(infoPtr
->hwndSelf
, themeClass
);
7519 * Updates an items or rearranges the listview control.
7522 * [I] infoPtr : valid pointer to the listview structure
7523 * [I] nItem : item index
7529 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
7531 TRACE("(nItem=%d)\n", nItem
);
7533 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
7535 /* rearrange with default alignment style */
7536 if (is_autoarrange(infoPtr
))
7537 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
7539 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
7547 * Creates the listview control.
7550 * [I] hwnd : window handle
7551 * [I] lpcs : the create parameters
7557 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
7559 LISTVIEW_INFO
*infoPtr
;
7560 UINT uView
= lpcs
->style
& LVS_TYPEMASK
;
7563 TRACE("(lpcs=%p)\n", lpcs
);
7565 /* initialize info pointer */
7566 infoPtr
= (LISTVIEW_INFO
*)Alloc(sizeof(LISTVIEW_INFO
));
7567 if (!infoPtr
) return -1;
7569 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
7571 infoPtr
->hwndSelf
= hwnd
;
7572 infoPtr
->dwStyle
= lpcs
->style
;
7573 /* determine the type of structures to use */
7574 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
7575 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
7576 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
7578 /* initialize color information */
7579 infoPtr
->clrBk
= CLR_NONE
;
7580 infoPtr
->clrText
= comctl32_color
.clrWindowText
;
7581 infoPtr
->clrTextBk
= CLR_DEFAULT
;
7582 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
7584 /* set default values */
7585 infoPtr
->nFocusedItem
= -1;
7586 infoPtr
->nSelectionMark
= -1;
7587 infoPtr
->nHotItem
= -1;
7588 infoPtr
->bRedraw
= TRUE
;
7589 infoPtr
->bNoItemMetrics
= TRUE
;
7590 infoPtr
->bDoChangeNotify
= TRUE
;
7591 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
7592 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
7593 infoPtr
->nEditLabelItem
= -1;
7594 infoPtr
->dwHoverTime
= -1; /* default system hover time */
7595 infoPtr
->nMeasureItemHeight
= 0;
7597 /* get default font (icon title) */
7598 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
7599 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
7600 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
7601 LISTVIEW_SaveTextMetrics(infoPtr
);
7604 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
,
7605 WS_CHILD
| HDS_HORZ
| (DWORD
)((LVS_NOSORTHEADER
& lpcs
->style
)?0:HDS_BUTTONS
),
7606 0, 0, 0, 0, hwnd
, NULL
,
7607 lpcs
->hInstance
, NULL
);
7608 if (!infoPtr
->hwndHeader
) goto fail
;
7610 /* set header unicode format */
7611 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, (WPARAM
)TRUE
, (LPARAM
)NULL
);
7613 /* set header font */
7614 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, (LPARAM
)TRUE
);
7616 /* allocate memory for the data structure */
7617 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
7618 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
7619 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
7620 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
7621 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
7623 /* initialize the icon sizes */
7624 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, uView
!= LVS_ICON
);
7625 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
7627 /* init item size to avoid division by 0 */
7628 LISTVIEW_UpdateItemSize (infoPtr
);
7630 if (uView
== LVS_REPORT
)
7632 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
))
7634 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
7638 /* set HDS_HIDDEN flag to hide the header bar */
7639 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
,
7640 GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
) | HDS_HIDDEN
);
7644 OpenThemeData(hwnd
, themeClass
);
7649 DestroyWindow(infoPtr
->hwndHeader
);
7650 ranges_destroy(infoPtr
->selectionRanges
);
7651 DPA_Destroy(infoPtr
->hdpaItems
);
7652 DPA_Destroy(infoPtr
->hdpaPosX
);
7653 DPA_Destroy(infoPtr
->hdpaPosY
);
7654 DPA_Destroy(infoPtr
->hdpaColumns
);
7661 * Destroys the listview control.
7664 * [I] infoPtr : valid pointer to the listview structure
7670 static LRESULT
LISTVIEW_Destroy(LISTVIEW_INFO
*infoPtr
)
7672 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
7673 CloseThemeData(theme
);
7679 * Enables the listview control.
7682 * [I] infoPtr : valid pointer to the listview structure
7683 * [I] bEnable : specifies whether to enable or disable the window
7689 static BOOL
LISTVIEW_Enable(LISTVIEW_INFO
*infoPtr
, BOOL bEnable
)
7691 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
7692 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
7698 * Erases the background of the listview control.
7701 * [I] infoPtr : valid pointer to the listview structure
7702 * [I] hdc : device context handle
7708 static inline BOOL
LISTVIEW_EraseBkgnd(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
7712 TRACE("(hdc=%p)\n", hdc
);
7714 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
7716 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
7722 * Helper function for LISTVIEW_[HV]Scroll *only*.
7723 * Performs vertical/horizontal scrolling by a give amount.
7726 * [I] infoPtr : valid pointer to the listview structure
7727 * [I] dx : amount of horizontal scroll
7728 * [I] dy : amount of vertical scroll
7730 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
7732 /* now we can scroll the list */
7733 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
7734 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
7735 /* if we have focus, adjust rect */
7736 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
7737 UpdateWindow(infoPtr
->hwndSelf
);
7742 * Performs vertical scrolling.
7745 * [I] infoPtr : valid pointer to the listview structure
7746 * [I] nScrollCode : scroll code
7747 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7748 * [I] hScrollWnd : scrollbar control window handle
7754 * SB_LINEUP/SB_LINEDOWN:
7755 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7756 * for LVS_REPORT is 1 line
7757 * for LVS_LIST cannot occur
7760 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
7761 INT nScrollDiff
, HWND hScrollWnd
)
7763 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7764 INT nOldScrollPos
, nNewScrollPos
;
7765 SCROLLINFO scrollInfo
;
7768 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
7769 debugscrollcode(nScrollCode
), nScrollDiff
);
7771 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
7773 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7774 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
7776 is_an_icon
= ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
));
7778 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
7780 nOldScrollPos
= scrollInfo
.nPos
;
7781 switch (nScrollCode
)
7787 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
7791 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
7795 nScrollDiff
= -scrollInfo
.nPage
;
7799 nScrollDiff
= scrollInfo
.nPage
;
7802 case SB_THUMBPOSITION
:
7804 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
7811 /* quit right away if pos isn't changing */
7812 if (nScrollDiff
== 0) return 0;
7814 /* calculate new position, and handle overflows */
7815 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
7816 if (nScrollDiff
> 0) {
7817 if (nNewScrollPos
< nOldScrollPos
||
7818 nNewScrollPos
> scrollInfo
.nMax
)
7819 nNewScrollPos
= scrollInfo
.nMax
;
7821 if (nNewScrollPos
> nOldScrollPos
||
7822 nNewScrollPos
< scrollInfo
.nMin
)
7823 nNewScrollPos
= scrollInfo
.nMin
;
7826 /* set the new position, and reread in case it changed */
7827 scrollInfo
.fMask
= SIF_POS
;
7828 scrollInfo
.nPos
= nNewScrollPos
;
7829 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
7831 /* carry on only if it really changed */
7832 if (nNewScrollPos
== nOldScrollPos
) return 0;
7834 /* now adjust to client coordinates */
7835 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
7836 if (uView
== LVS_REPORT
) nScrollDiff
*= infoPtr
->nItemHeight
;
7838 /* and scroll the window */
7839 scroll_list(infoPtr
, 0, nScrollDiff
);
7846 * Performs horizontal scrolling.
7849 * [I] infoPtr : valid pointer to the listview structure
7850 * [I] nScrollCode : scroll code
7851 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7852 * [I] hScrollWnd : scrollbar control window handle
7858 * SB_LINELEFT/SB_LINERIGHT:
7859 * for LVS_ICON, LVS_SMALLICON 1 pixel
7860 * for LVS_REPORT is 1 pixel
7861 * for LVS_LIST is 1 column --> which is a 1 because the
7862 * scroll is based on columns not pixels
7865 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
7866 INT nScrollDiff
, HWND hScrollWnd
)
7868 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7869 INT nOldScrollPos
, nNewScrollPos
;
7870 SCROLLINFO scrollInfo
;
7872 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
7873 debugscrollcode(nScrollCode
), nScrollDiff
);
7875 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
7877 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7878 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
7880 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
7882 nOldScrollPos
= scrollInfo
.nPos
;
7884 switch (nScrollCode
)
7898 nScrollDiff
= -scrollInfo
.nPage
;
7902 nScrollDiff
= scrollInfo
.nPage
;
7905 case SB_THUMBPOSITION
:
7907 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
7914 /* quit right away if pos isn't changing */
7915 if (nScrollDiff
== 0) return 0;
7917 /* calculate new position, and handle overflows */
7918 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
7919 if (nScrollDiff
> 0) {
7920 if (nNewScrollPos
< nOldScrollPos
||
7921 nNewScrollPos
> scrollInfo
.nMax
)
7922 nNewScrollPos
= scrollInfo
.nMax
;
7924 if (nNewScrollPos
> nOldScrollPos
||
7925 nNewScrollPos
< scrollInfo
.nMin
)
7926 nNewScrollPos
= scrollInfo
.nMin
;
7929 /* set the new position, and reread in case it changed */
7930 scrollInfo
.fMask
= SIF_POS
;
7931 scrollInfo
.nPos
= nNewScrollPos
;
7932 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
7934 /* carry on only if it really changed */
7935 if (nNewScrollPos
== nOldScrollPos
) return 0;
7937 if(uView
== LVS_REPORT
)
7938 LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
7940 /* now adjust to client coordinates */
7941 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
7942 if (uView
== LVS_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
7944 /* and scroll the window */
7945 scroll_list(infoPtr
, nScrollDiff
, 0);
7950 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
7952 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7953 INT gcWheelDelta
= 0;
7954 INT pulScrollLines
= 3;
7955 SCROLLINFO scrollInfo
;
7957 TRACE("(wheelDelta=%d)\n", wheelDelta
);
7959 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
7960 gcWheelDelta
-= wheelDelta
;
7962 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7963 scrollInfo
.fMask
= SIF_POS
;
7970 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7971 * should be fixed in the future.
7973 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (gcWheelDelta
< 0) ?
7974 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
, 0);
7978 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
7980 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
7981 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
7982 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, cLineScroll
, 0);
7987 LISTVIEW_HScroll(infoPtr
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0, 0);
7998 * [I] infoPtr : valid pointer to the listview structure
7999 * [I] nVirtualKey : virtual key
8000 * [I] lKeyData : key data
8005 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
8007 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8008 HWND hwndSelf
= infoPtr
->hwndSelf
;
8010 NMLVKEYDOWN nmKeyDown
;
8012 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey
, lKeyData
);
8014 /* send LVN_KEYDOWN notification */
8015 nmKeyDown
.wVKey
= nVirtualKey
;
8016 nmKeyDown
.flags
= 0;
8017 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
8018 if (!IsWindow(hwndSelf
))
8021 switch (nVirtualKey
)
8024 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
8026 if (!notify(infoPtr
, NM_RETURN
)) return 0;
8027 if (!notify(infoPtr
, LVN_ITEMACTIVATE
)) return 0;
8032 if (infoPtr
->nItemCount
> 0)
8037 if (infoPtr
->nItemCount
> 0)
8038 nItem
= infoPtr
->nItemCount
- 1;
8042 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
8046 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
8050 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
8054 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
8058 if (uView
== LVS_REPORT
)
8060 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8061 if (infoPtr
->nFocusedItem
== topidx
)
8062 nItem
= topidx
- LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
8067 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
8068 * LISTVIEW_GetCountPerRow(infoPtr
);
8069 if(nItem
< 0) nItem
= 0;
8073 if (uView
== LVS_REPORT
)
8075 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8076 INT cnt
= LISTVIEW_GetCountPerColumn(infoPtr
);
8077 if (infoPtr
->nFocusedItem
== topidx
+ cnt
- 1)
8078 nItem
= infoPtr
->nFocusedItem
+ cnt
- 1;
8080 nItem
= topidx
+ cnt
- 1;
8083 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
8084 * LISTVIEW_GetCountPerRow(infoPtr
);
8085 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
8089 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
))
8090 LISTVIEW_KeySelection(infoPtr
, nItem
);
8100 * [I] infoPtr : valid pointer to the listview structure
8105 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
8109 /* if we did not have the focus, there's nothing to do */
8110 if (!infoPtr
->bFocus
) return 0;
8112 /* send NM_KILLFOCUS notification */
8113 if (!notify(infoPtr
, NM_KILLFOCUS
)) return 0;
8115 /* if we have a focus rectagle, get rid of it */
8116 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
8118 /* set window focus flag */
8119 infoPtr
->bFocus
= FALSE
;
8121 /* invalidate the selected items before reseting focus flag */
8122 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8129 * Processes double click messages (left mouse button).
8132 * [I] infoPtr : valid pointer to the listview structure
8133 * [I] wKey : key flag
8134 * [I] x,y : mouse coordinate
8139 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8141 LVHITTESTINFO htInfo
;
8143 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8145 /* send NM_RELEASEDCAPTURE notification */
8146 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8151 /* send NM_DBLCLK notification */
8152 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
8153 if (!notify_click(infoPtr
, NM_DBLCLK
, &htInfo
)) return 0;
8155 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8156 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
8163 * Processes mouse down messages (left mouse button).
8166 * infoPtr [I ] valid pointer to the listview structure
8167 * wKey [I ] key flag
8168 * x,y [I ] mouse coordinate
8173 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8175 LVHITTESTINFO lvHitTestInfo
;
8176 static BOOL bGroupSelect
= TRUE
;
8177 POINT pt
= { x
, y
};
8180 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8182 /* send NM_RELEASEDCAPTURE notification */
8183 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8185 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
8187 /* set left button down flag and record the click position */
8188 infoPtr
->bLButtonDown
= TRUE
;
8189 infoPtr
->ptClickPos
= pt
;
8191 lvHitTestInfo
.pt
.x
= x
;
8192 lvHitTestInfo
.pt
.y
= y
;
8194 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8195 TRACE("at %s, nItem=%d\n", debugpoint(&pt
), nItem
);
8196 infoPtr
->nEditLabelItem
= -1;
8197 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8199 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
8201 DWORD state
= STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_STATEIMAGEMASK
));
8202 if(state
== 1 || state
== 2)
8206 lvitem
.state
= INDEXTOSTATEIMAGEMASK(state
);
8207 lvitem
.stateMask
= LVIS_STATEIMAGEMASK
;
8208 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvitem
);
8213 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
8215 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8216 infoPtr
->nEditLabelItem
= nItem
;
8218 LISTVIEW_SetSelection(infoPtr
, nItem
);
8222 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
8226 if (!LISTVIEW_AddGroupSelection(infoPtr
, nItem
)) return 0;
8227 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8228 infoPtr
->nSelectionMark
= nItem
;
8234 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
8235 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8237 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
8238 infoPtr
->nSelectionMark
= nItem
;
8241 else if (wKey
& MK_CONTROL
)
8245 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
8247 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
8248 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8249 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
8250 infoPtr
->nSelectionMark
= nItem
;
8252 else if (wKey
& MK_SHIFT
)
8254 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
8258 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8259 infoPtr
->nEditLabelItem
= nItem
;
8261 /* set selection (clears other pre-existing selections) */
8262 LISTVIEW_SetSelection(infoPtr
, nItem
);
8268 /* remove all selections */
8269 LISTVIEW_DeselectAll(infoPtr
);
8278 * Processes mouse up messages (left mouse button).
8281 * infoPtr [I ] valid pointer to the listview structure
8282 * wKey [I ] key flag
8283 * x,y [I ] mouse coordinate
8288 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8290 LVHITTESTINFO lvHitTestInfo
;
8292 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8294 if (!infoPtr
->bLButtonDown
) return 0;
8296 lvHitTestInfo
.pt
.x
= x
;
8297 lvHitTestInfo
.pt
.y
= y
;
8299 /* send NM_CLICK notification */
8300 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8301 if (!notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
)) return 0;
8303 /* set left button flag */
8304 infoPtr
->bLButtonDown
= FALSE
;
8306 /* if we clicked on a selected item, edit the label */
8307 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
8308 LISTVIEW_EditLabelT(infoPtr
, lvHitTestInfo
.iItem
, TRUE
);
8315 * Destroys the listview control (called after WM_DESTROY).
8318 * [I] infoPtr : valid pointer to the listview structure
8323 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
8327 /* delete all items */
8328 LISTVIEW_DeleteAllItems(infoPtr
);
8330 /* destroy data structure */
8331 DPA_Destroy(infoPtr
->hdpaItems
);
8332 DPA_Destroy(infoPtr
->hdpaPosX
);
8333 DPA_Destroy(infoPtr
->hdpaPosY
);
8334 DPA_Destroy(infoPtr
->hdpaColumns
);
8335 ranges_destroy(infoPtr
->selectionRanges
);
8337 /* destroy image lists */
8338 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
8340 if (infoPtr
->himlNormal
)
8341 ImageList_Destroy(infoPtr
->himlNormal
);
8342 if (infoPtr
->himlSmall
)
8343 ImageList_Destroy(infoPtr
->himlSmall
);
8344 if (infoPtr
->himlState
)
8345 ImageList_Destroy(infoPtr
->himlState
);
8348 /* destroy font, bkgnd brush */
8350 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
8351 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
8353 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
8355 /* free listview info pointer*/
8363 * Handles notifications from header.
8366 * [I] infoPtr : valid pointer to the listview structure
8367 * [I] nCtrlId : control identifier
8368 * [I] lpnmh : notification information
8373 static LRESULT
LISTVIEW_HeaderNotification(LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
8375 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8376 HWND hwndSelf
= infoPtr
->hwndSelf
;
8378 TRACE("(lpnmh=%p)\n", lpnmh
);
8380 if (!lpnmh
|| lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
8382 switch (lpnmh
->hdr
.code
)
8384 case HDN_ITEMCHANGINGW
:
8385 case HDN_ITEMCHANGINGA
:
8386 return notify_forward_header(infoPtr
, lpnmh
);
8387 case HDN_ITEMCHANGEDW
:
8388 case HDN_ITEMCHANGEDA
:
8389 notify_forward_header(infoPtr
, lpnmh
);
8390 if (!IsWindow(hwndSelf
))
8396 COLUMN_INFO
*lpColumnInfo
;
8399 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8403 hdi
.mask
= HDI_WIDTH
;
8404 if (!Header_GetItemW(infoPtr
->hwndHeader
, lpnmh
->iItem
, (LPARAM
)&hdi
)) return 0;
8408 cxy
= lpnmh
->pitem
->cxy
;
8410 /* determine how much we change since the last know position */
8411 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8412 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
8415 lpColumnInfo
->rcHeader
.right
+= dx
;
8416 LISTVIEW_ScrollColumns(infoPtr
, lpnmh
->iItem
+ 1, dx
);
8417 LISTVIEW_UpdateItemSize(infoPtr
);
8418 if (uView
== LVS_REPORT
&& is_redrawing(infoPtr
))
8421 RECT rcCol
= lpColumnInfo
->rcHeader
;
8423 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
8424 OffsetRect(&rcCol
, ptOrigin
.x
, 0);
8426 rcCol
.top
= infoPtr
->rcList
.top
;
8427 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
8429 /* resizing left-aligned columns leaves most of the left side untouched */
8430 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
8432 INT nMaxDirty
= infoPtr
->nEllipsisWidth
+ infoPtr
->ntmMaxCharWidth
+ dx
;
8433 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- nMaxDirty
);
8436 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
8442 case HDN_ITEMCLICKW
:
8443 case HDN_ITEMCLICKA
:
8445 /* Handle sorting by Header Column */
8448 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
8450 nmlv
.iSubItem
= lpnmh
->iItem
;
8451 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
8455 case HDN_DIVIDERDBLCLICKW
:
8456 case HDN_DIVIDERDBLCLICKA
:
8457 LISTVIEW_SetColumnWidth(infoPtr
, lpnmh
->iItem
, LVSCW_AUTOSIZE
);
8466 * Paint non-client area of control.
8469 * [I] infoPtr : valid pointer to the listview structureof the sender
8470 * [I] region : update region
8473 * TRUE - frame was painted
8474 * FALSE - call default window proc
8476 static BOOL
LISTVIEW_NCPaint(LISTVIEW_INFO
*infoPtr
, HRGN region
)
8478 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
8482 int cxEdge
= GetSystemMetrics (SM_CXEDGE
),
8483 cyEdge
= GetSystemMetrics (SM_CYEDGE
);
8485 if (!theme
) return FALSE
;
8487 GetWindowRect(infoPtr
->hwndSelf
, &r
);
8489 cliprgn
= CreateRectRgn (r
.left
+ cxEdge
, r
.top
+ cyEdge
,
8490 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
8491 if (region
!= (HRGN
)1)
8492 CombineRgn (cliprgn
, cliprgn
, region
, RGN_AND
);
8493 OffsetRect(&r
, -r
.left
, -r
.top
);
8495 dc
= GetDCEx(infoPtr
->hwndSelf
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
8496 OffsetRect(&r
, -r
.left
, -r
.top
);
8498 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
8499 DrawThemeParentBackground(infoPtr
->hwndSelf
, dc
, &r
);
8500 DrawThemeBackground (theme
, dc
, 0, 0, &r
, 0);
8501 ReleaseDC(infoPtr
->hwndSelf
, dc
);
8503 /* Call default proc to get the scrollbars etc. painted */
8504 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)cliprgn
, 0);
8511 * Determines the type of structure to use.
8514 * [I] infoPtr : valid pointer to the listview structureof the sender
8515 * [I] hwndFrom : listview window handle
8516 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8521 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
8523 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
8525 if (nCommand
!= NF_REQUERY
) return 0;
8527 infoPtr
->notifyFormat
= SendMessageW(hwndFrom
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
8534 * Paints/Repaints the listview control.
8537 * [I] infoPtr : valid pointer to the listview structure
8538 * [I] hdc : device context handle
8543 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
8545 TRACE("(hdc=%p)\n", hdc
);
8547 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
8549 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8551 infoPtr
->bNoItemMetrics
= FALSE
;
8552 LISTVIEW_UpdateItemSize(infoPtr
);
8553 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
8554 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8555 LISTVIEW_UpdateScroll(infoPtr
);
8558 LISTVIEW_Refresh(infoPtr
, hdc
);
8563 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
8565 if (ps
.fErase
) LISTVIEW_FillBkgnd(infoPtr
, hdc
, &ps
.rcPaint
);
8566 LISTVIEW_Refresh(infoPtr
, hdc
);
8567 EndPaint(infoPtr
->hwndSelf
, &ps
);
8576 * Paints/Repaints the listview control.
8579 * [I] infoPtr : valid pointer to the listview structure
8580 * [I] hdc : device context handle
8581 * [I] options : drawing options
8586 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
8588 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc
, options
);
8590 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
8593 if (options
& PRF_ERASEBKGND
)
8594 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
8596 if (options
& PRF_CLIENT
)
8597 LISTVIEW_Paint(infoPtr
, hdc
);
8605 * Processes double click messages (right mouse button).
8608 * [I] infoPtr : valid pointer to the listview structure
8609 * [I] wKey : key flag
8610 * [I] x,y : mouse coordinate
8615 static LRESULT
LISTVIEW_RButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8617 LVHITTESTINFO lvHitTestInfo
;
8619 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
8621 /* send NM_RELEASEDCAPTURE notification */
8622 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8624 /* send NM_RDBLCLK notification */
8625 lvHitTestInfo
.pt
.x
= x
;
8626 lvHitTestInfo
.pt
.y
= y
;
8627 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8628 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
8635 * Processes mouse down messages (right mouse button).
8638 * [I] infoPtr : valid pointer to the listview structure
8639 * [I] wKey : key flag
8640 * [I] x,y : mouse coordinate
8645 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8647 LVHITTESTINFO lvHitTestInfo
;
8650 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
8652 /* send NM_RELEASEDCAPTURE notification */
8653 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8655 /* make sure the listview control window has the focus */
8656 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
8658 /* set right button down flag */
8659 infoPtr
->bRButtonDown
= TRUE
;
8661 /* determine the index of the selected item */
8662 lvHitTestInfo
.pt
.x
= x
;
8663 lvHitTestInfo
.pt
.y
= y
;
8664 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8666 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8668 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8669 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
8670 !LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8671 LISTVIEW_SetSelection(infoPtr
, nItem
);
8675 LISTVIEW_DeselectAll(infoPtr
);
8683 * Processes mouse up messages (right mouse button).
8686 * [I] infoPtr : valid pointer to the listview structure
8687 * [I] wKey : key flag
8688 * [I] x,y : mouse coordinate
8693 static LRESULT
LISTVIEW_RButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8695 LVHITTESTINFO lvHitTestInfo
;
8698 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
8700 if (!infoPtr
->bRButtonDown
) return 0;
8702 /* set button flag */
8703 infoPtr
->bRButtonDown
= FALSE
;
8705 /* Send NM_RClICK notification */
8706 lvHitTestInfo
.pt
.x
= x
;
8707 lvHitTestInfo
.pt
.y
= y
;
8708 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8709 if (!notify_click(infoPtr
, NM_RCLICK
, &lvHitTestInfo
)) return 0;
8711 /* Change to screen coordinate for WM_CONTEXTMENU */
8712 pt
= lvHitTestInfo
.pt
;
8713 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
8715 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8716 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
8717 (WPARAM
)infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
8728 * [I] infoPtr : valid pointer to the listview structure
8729 * [I] hwnd : window handle of window containing the cursor
8730 * [I] nHittest : hit-test code
8731 * [I] wMouseMsg : ideintifier of the mouse message
8734 * TRUE if cursor is set
8737 static BOOL
LISTVIEW_SetCursor(LISTVIEW_INFO
*infoPtr
, HWND hwnd
, UINT nHittest
, UINT wMouseMsg
)
8739 LVHITTESTINFO lvHitTestInfo
;
8741 if(!(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)) return FALSE
;
8743 if(!infoPtr
->hHotCursor
) return FALSE
;
8745 GetCursorPos(&lvHitTestInfo
.pt
);
8746 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) return FALSE
;
8748 SetCursor(infoPtr
->hHotCursor
);
8758 * [I] infoPtr : valid pointer to the listview structure
8759 * [I] hwndLoseFocus : handle of previously focused window
8764 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
8766 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
8768 /* if we have the focus already, there's nothing to do */
8769 if (infoPtr
->bFocus
) return 0;
8771 /* send NM_SETFOCUS notification */
8772 if (!notify(infoPtr
, NM_SETFOCUS
)) return 0;
8774 /* set window focus flag */
8775 infoPtr
->bFocus
= TRUE
;
8777 /* put the focus rect back on */
8778 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
8780 /* redraw all visible selected items */
8781 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8791 * [I] infoPtr : valid pointer to the listview structure
8792 * [I] fRedraw : font handle
8793 * [I] fRedraw : redraw flag
8798 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
8800 HFONT oldFont
= infoPtr
->hFont
;
8802 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
8804 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
8805 if (infoPtr
->hFont
== oldFont
) return 0;
8807 LISTVIEW_SaveTextMetrics(infoPtr
);
8809 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
)
8810 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
8812 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
8819 * Message handling for WM_SETREDRAW.
8820 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8823 * [I] infoPtr : valid pointer to the listview structure
8824 * [I] bRedraw: state of redraw flag
8827 * DefWinProc return value
8829 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
8831 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr
->bRedraw
, bRedraw
);
8833 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
8834 if ((infoPtr
->bRedraw
&& bRedraw
) || (!infoPtr
->bRedraw
&& !bRedraw
)) return 0;
8836 infoPtr
->bRedraw
= bRedraw
;
8838 if(!bRedraw
) return 0;
8840 if (is_autoarrange(infoPtr
))
8841 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8842 LISTVIEW_UpdateScroll(infoPtr
);
8844 /* despite what the WM_SETREDRAW docs says, apps expect us
8845 * to invalidate the listview here... stupid! */
8846 LISTVIEW_InvalidateList(infoPtr
);
8853 * Resizes the listview control. This function processes WM_SIZE
8854 * messages. At this time, the width and height are not used.
8857 * [I] infoPtr : valid pointer to the listview structure
8858 * [I] Width : new width
8859 * [I] Height : new height
8864 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
8866 RECT rcOld
= infoPtr
->rcList
;
8868 TRACE("(width=%d, height=%d)\n", Width
, Height
);
8870 LISTVIEW_UpdateSize(infoPtr
);
8871 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
8873 /* do not bother with display related stuff if we're not redrawing */
8874 if (!is_redrawing(infoPtr
)) return 0;
8876 if (is_autoarrange(infoPtr
))
8877 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8879 LISTVIEW_UpdateScroll(infoPtr
);
8881 /* refresh all only for lists whose height changed significantly */
8882 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
&&
8883 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
8884 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
8885 LISTVIEW_InvalidateList(infoPtr
);
8892 * Sets the size information.
8895 * [I] infoPtr : valid pointer to the listview structure
8900 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
8902 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8904 TRACE("uView=%d, rcList(old)=%s\n", uView
, debugrect(&infoPtr
->rcList
));
8906 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
8908 if (uView
== LVS_LIST
)
8910 /* Apparently the "LIST" style is supposed to have the same
8911 * number of items in a column even if there is no scroll bar.
8912 * Since if a scroll bar already exists then the bottom is already
8913 * reduced, only reduce if the scroll bar does not currently exist.
8914 * The "2" is there to mimic the native control. I think it may be
8915 * related to either padding or edges. (GLA 7/2002)
8917 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_HSCROLL
))
8918 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
8919 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
8921 else if (uView
== LVS_REPORT
&& !(infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
))
8926 hl
.prc
= &infoPtr
->rcList
;
8928 Header_Layout(infoPtr
->hwndHeader
, &hl
);
8930 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
8932 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
8935 TRACE(" rcList=%s\n", debugrect(&infoPtr
->rcList
));
8940 * Processes WM_STYLECHANGED messages.
8943 * [I] infoPtr : valid pointer to the listview structure
8944 * [I] wStyleType : window style type (normal or extended)
8945 * [I] lpss : window style information
8950 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
8951 const STYLESTRUCT
*lpss
)
8953 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
8954 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
8956 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8957 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
8959 if (wStyleType
!= GWL_STYLE
) return 0;
8961 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8962 /* what if LVS_OWNERDATA changed? */
8963 /* or LVS_SINGLESEL */
8964 /* or LVS_SORT{AS,DES}CENDING */
8966 infoPtr
->dwStyle
= lpss
->styleNew
;
8968 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
8969 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
8970 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
8972 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
8973 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
8974 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
8976 if (uNewView
!= uOldView
)
8978 SIZE oldIconSize
= infoPtr
->iconSize
;
8981 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8982 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
8984 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
8985 SetRectEmpty(&infoPtr
->rcFocus
);
8987 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
8988 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
8990 if (uNewView
== LVS_ICON
)
8992 if ((infoPtr
->iconSize
.cx
!= oldIconSize
.cx
) || (infoPtr
->iconSize
.cy
!= oldIconSize
.cy
))
8994 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8995 oldIconSize
.cx
, oldIconSize
.cy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
8996 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
8999 else if (uNewView
== LVS_REPORT
)
9004 hl
.prc
= &infoPtr
->rcList
;
9006 Header_Layout(infoPtr
->hwndHeader
, &hl
);
9007 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
9010 LISTVIEW_UpdateItemSize(infoPtr
);
9013 if (uNewView
== LVS_REPORT
)
9014 ShowWindow(infoPtr
->hwndHeader
, (lpss
->styleNew
& LVS_NOCOLUMNHEADER
) ? SW_HIDE
: SW_SHOWNORMAL
);
9016 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
9017 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
9018 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9020 /* update the size of the client area */
9021 LISTVIEW_UpdateSize(infoPtr
);
9023 /* add scrollbars if needed */
9024 LISTVIEW_UpdateScroll(infoPtr
);
9026 /* invalidate client area + erase background */
9027 LISTVIEW_InvalidateList(infoPtr
);
9034 * Window procedure of the listview control.
9037 static LRESULT WINAPI
9038 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9040 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9042 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg
, wParam
, lParam
);
9044 if (!infoPtr
&& (uMsg
!= WM_CREATE
))
9045 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9049 case LVM_APPROXIMATEVIEWRECT
:
9050 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
9051 LOWORD(lParam
), HIWORD(lParam
));
9053 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
9055 /* case LVM_CANCELEDITLABEL: */
9057 case LVM_CREATEDRAGIMAGE
:
9058 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9060 case LVM_DELETEALLITEMS
:
9061 return LISTVIEW_DeleteAllItems(infoPtr
);
9063 case LVM_DELETECOLUMN
:
9064 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
9066 case LVM_DELETEITEM
:
9067 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
9069 case LVM_EDITLABELW
:
9070 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, TRUE
);
9072 case LVM_EDITLABELA
:
9073 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, FALSE
);
9075 /* case LVM_ENABLEGROUPVIEW: */
9077 case LVM_ENSUREVISIBLE
:
9078 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
9081 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
9084 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
9086 case LVM_GETBKCOLOR
:
9087 return infoPtr
->clrBk
;
9089 /* case LVM_GETBKIMAGE: */
9091 case LVM_GETCALLBACKMASK
:
9092 return infoPtr
->uCallbackMask
;
9094 case LVM_GETCOLUMNA
:
9095 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9097 case LVM_GETCOLUMNW
:
9098 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9100 case LVM_GETCOLUMNORDERARRAY
:
9101 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9103 case LVM_GETCOLUMNWIDTH
:
9104 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
9106 case LVM_GETCOUNTPERPAGE
:
9107 return LISTVIEW_GetCountPerPage(infoPtr
);
9109 case LVM_GETEDITCONTROL
:
9110 return (LRESULT
)infoPtr
->hwndEdit
;
9112 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
9113 return infoPtr
->dwLvExStyle
;
9115 /* case LVM_GETGROUPINFO: */
9117 /* case LVM_GETGROUPMETRICS: */
9120 return (LRESULT
)infoPtr
->hwndHeader
;
9122 case LVM_GETHOTCURSOR
:
9123 return (LRESULT
)infoPtr
->hHotCursor
;
9125 case LVM_GETHOTITEM
:
9126 return infoPtr
->nHotItem
;
9128 case LVM_GETHOVERTIME
:
9129 return infoPtr
->dwHoverTime
;
9131 case LVM_GETIMAGELIST
:
9132 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
9134 /* case LVM_GETINSERTMARK: */
9136 /* case LVM_GETINSERTMARKCOLOR: */
9138 /* case LVM_GETINSERTMARKRECT: */
9140 case LVM_GETISEARCHSTRINGA
:
9141 case LVM_GETISEARCHSTRINGW
:
9142 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9146 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9149 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9151 case LVM_GETITEMCOUNT
:
9152 return infoPtr
->nItemCount
;
9154 case LVM_GETITEMPOSITION
:
9155 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9157 case LVM_GETITEMRECT
:
9158 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
9160 case LVM_GETITEMSPACING
:
9161 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
9163 case LVM_GETITEMSTATE
:
9164 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
9166 case LVM_GETITEMTEXTA
:
9167 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9169 case LVM_GETITEMTEXTW
:
9170 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9172 case LVM_GETNEXTITEM
:
9173 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
9175 case LVM_GETNUMBEROFWORKAREAS
:
9176 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9180 if (!lParam
) return FALSE
;
9181 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
9184 /* case LVM_GETOUTLINECOLOR: */
9186 /* case LVM_GETSELECTEDCOLUMN: */
9188 case LVM_GETSELECTEDCOUNT
:
9189 return LISTVIEW_GetSelectedCount(infoPtr
);
9191 case LVM_GETSELECTIONMARK
:
9192 return infoPtr
->nSelectionMark
;
9194 case LVM_GETSTRINGWIDTHA
:
9195 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, FALSE
);
9197 case LVM_GETSTRINGWIDTHW
:
9198 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, TRUE
);
9200 case LVM_GETSUBITEMRECT
:
9201 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
9203 case LVM_GETTEXTBKCOLOR
:
9204 return infoPtr
->clrTextBk
;
9206 case LVM_GETTEXTCOLOR
:
9207 return infoPtr
->clrText
;
9209 /* case LVM_GETTILEINFO: */
9211 /* case LVM_GETTILEVIEWINFO: */
9213 case LVM_GETTOOLTIPS
:
9214 if( !infoPtr
->hwndToolTip
)
9215 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
9216 return (LRESULT
)infoPtr
->hwndToolTip
;
9218 case LVM_GETTOPINDEX
:
9219 return LISTVIEW_GetTopIndex(infoPtr
);
9221 /*case LVM_GETUNICODEFORMAT:
9222 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9225 /* case LVM_GETVIEW: */
9227 case LVM_GETVIEWRECT
:
9228 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
9230 case LVM_GETWORKAREAS
:
9231 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9234 /* case LVM_HASGROUP: */
9237 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, FALSE
);
9239 case LVM_INSERTCOLUMNA
:
9240 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9242 case LVM_INSERTCOLUMNW
:
9243 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9245 /* case LVM_INSERTGROUP: */
9247 /* case LVM_INSERTGROUPSORTED: */
9249 case LVM_INSERTITEMA
:
9250 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9252 case LVM_INSERTITEMW
:
9253 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9255 /* case LVM_INSERTMARKHITTEST: */
9257 /* case LVM_ISGROUPVIEWENABLED: */
9259 /* case LVM_MAPIDTOINDEX: */
9261 /* case LVM_MAPINDEXTOID: */
9263 /* case LVM_MOVEGROUP: */
9265 /* case LVM_MOVEITEMTOGROUP: */
9267 case LVM_REDRAWITEMS
:
9268 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9270 /* case LVM_REMOVEALLGROUPS: */
9272 /* case LVM_REMOVEGROUP: */
9275 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9277 case LVM_SETBKCOLOR
:
9278 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
9280 /* case LVM_SETBKIMAGE: */
9282 case LVM_SETCALLBACKMASK
:
9283 infoPtr
->uCallbackMask
= (UINT
)wParam
;
9286 case LVM_SETCOLUMNA
:
9287 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9289 case LVM_SETCOLUMNW
:
9290 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9292 case LVM_SETCOLUMNORDERARRAY
:
9293 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9295 case LVM_SETCOLUMNWIDTH
:
9296 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
9298 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
9299 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
9301 /* case LVM_SETGROUPINFO: */
9303 /* case LVM_SETGROUPMETRICS: */
9305 case LVM_SETHOTCURSOR
:
9306 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
9308 case LVM_SETHOTITEM
:
9309 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
9311 case LVM_SETHOVERTIME
:
9312 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)wParam
);
9314 case LVM_SETICONSPACING
:
9315 return LISTVIEW_SetIconSpacing(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
9317 case LVM_SETIMAGELIST
:
9318 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
9320 /* case LVM_SETINFOTIP: */
9322 /* case LVM_SETINSERTMARK: */
9324 /* case LVM_SETINSERTMARKCOLOR: */
9327 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9330 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9332 case LVM_SETITEMCOUNT
:
9333 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
9335 case LVM_SETITEMPOSITION
:
9338 pt
.x
= (short)LOWORD(lParam
);
9339 pt
.y
= (short)HIWORD(lParam
);
9340 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, pt
);
9343 case LVM_SETITEMPOSITION32
:
9344 if (lParam
== 0) return FALSE
;
9345 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, *((POINT
*)lParam
));
9347 case LVM_SETITEMSTATE
:
9348 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
9350 case LVM_SETITEMTEXTA
:
9351 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9353 case LVM_SETITEMTEXTW
:
9354 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9356 /* case LVM_SETOUTLINECOLOR: */
9358 /* case LVM_SETSELECTEDCOLUMN: */
9360 case LVM_SETSELECTIONMARK
:
9361 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
9363 case LVM_SETTEXTBKCOLOR
:
9364 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
9366 case LVM_SETTEXTCOLOR
:
9367 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
9369 /* case LVM_SETTILEINFO: */
9371 /* case LVM_SETTILEVIEWINFO: */
9373 /* case LVM_SETTILEWIDTH: */
9375 case LVM_SETTOOLTIPS
:
9376 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
9378 /* case LVM_SETUNICODEFORMAT: */
9380 /* case LVM_SETVIEW: */
9382 /* case LVM_SETWORKAREAS: */
9384 /* case LVM_SORTGROUPS: */
9387 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
);
9389 /* LVM_SORTITEMSEX: */
9391 case LVM_SUBITEMHITTEST
:
9392 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
9395 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
9398 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
9401 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
9404 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
9407 return LISTVIEW_Destroy(infoPtr
);
9410 return LISTVIEW_Enable(infoPtr
, (BOOL
)wParam
);
9413 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
9416 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
9419 return (LRESULT
)infoPtr
->hFont
;
9422 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
9425 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
9428 return LISTVIEW_KillFocus(infoPtr
);
9430 case WM_LBUTTONDBLCLK
:
9431 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9433 case WM_LBUTTONDOWN
:
9434 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9437 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9440 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9443 return LISTVIEW_MouseHover(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9446 return LISTVIEW_NCDestroy(infoPtr
);
9449 if (LISTVIEW_NCPaint(infoPtr
, (HRGN
)wParam
))
9454 if (lParam
&& ((LPNMHDR
)lParam
)->hwndFrom
== infoPtr
->hwndHeader
)
9455 return LISTVIEW_HeaderNotification(infoPtr
, (LPNMHEADERW
)lParam
);
9458 case WM_NOTIFYFORMAT
:
9459 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
9461 case WM_PRINTCLIENT
:
9462 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
9465 return LISTVIEW_Paint(infoPtr
, (HDC
)wParam
);
9467 case WM_RBUTTONDBLCLK
:
9468 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9470 case WM_RBUTTONDOWN
:
9471 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9474 return LISTVIEW_RButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9477 if(LISTVIEW_SetCursor(infoPtr
, (HWND
)wParam
, LOWORD(lParam
), HIWORD(lParam
)))
9482 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
9485 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
9488 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
9491 return LISTVIEW_Size(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
9493 case WM_STYLECHANGED
:
9494 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
9496 case WM_SYSCOLORCHANGE
:
9497 COMCTL32_RefreshSysColors();
9500 /* case WM_TIMER: */
9501 case WM_THEMECHANGED
:
9502 return LISTVIEW_ThemeChanged(infoPtr
);
9505 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
9508 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
9509 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9510 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
9512 case WM_WINDOWPOSCHANGED
:
9513 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
9515 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9516 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
9517 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
9519 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
9521 MEASUREITEMSTRUCT mis
;
9522 mis
.CtlType
= ODT_LISTVIEW
;
9523 mis
.CtlID
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
9527 mis
.itemHeight
= infoPtr
->nItemHeight
;
9528 SendMessageW(infoPtr
->hwndNotify
, WM_MEASUREITEM
, mis
.CtlID
, (LPARAM
)&mis
);
9529 if (infoPtr
->nItemHeight
!= max(mis
.itemHeight
, 1))
9530 infoPtr
->nMeasureItemHeight
= infoPtr
->nItemHeight
= max(mis
.itemHeight
, 1);
9533 LISTVIEW_UpdateSize(infoPtr
);
9534 LISTVIEW_UpdateScroll(infoPtr
);
9536 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9538 /* case WM_WININICHANGE: */
9541 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
))
9542 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg
, wParam
, lParam
);
9545 /* call default window procedure */
9546 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9553 * Registers the window class.
9561 void LISTVIEW_Register(void)
9565 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
9566 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
9567 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
9568 wndClass
.cbClsExtra
= 0;
9569 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
9570 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
9571 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
9572 wndClass
.lpszClassName
= WC_LISTVIEWW
;
9573 RegisterClassW(&wndClass
);
9578 * Unregisters the window class.
9586 void LISTVIEW_Unregister(void)
9588 UnregisterClassW(WC_LISTVIEWW
, NULL
);
9593 * Handle any WM_COMMAND messages
9596 * [I] infoPtr : valid pointer to the listview structure
9597 * [I] wParam : the first message parameter
9598 * [I] lParam : the second message parameter
9603 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
9605 switch (HIWORD(wParam
))
9610 * Adjust the edit window size
9613 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
9614 HFONT hFont
, hOldFont
= 0;
9619 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
9620 len
= GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
9621 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
9623 /* Select font to get the right dimension of the string */
9624 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
9627 hOldFont
= SelectObject(hdc
, hFont
);
9630 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
9632 TEXTMETRICW textMetric
;
9634 /* Add Extra spacing for the next character */
9635 GetTextMetricsW(hdc
, &textMetric
);
9636 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
9644 rect
.bottom
- rect
.top
,
9645 SWP_DRAWFRAME
|SWP_NOMOVE
);
9648 SelectObject(hdc
, hOldFont
);
9650 ReleaseDC(infoPtr
->hwndEdit
, hdc
);
9656 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);
9665 * Subclassed edit control windproc function
9668 * [I] hwnd : the edit window handle
9669 * [I] uMsg : the message that is to be processed
9670 * [I] wParam : first message parameter
9671 * [I] lParam : second message parameter
9672 * [I] isW : TRUE if input is Unicode
9677 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
9679 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
9680 BOOL cancel
= FALSE
;
9682 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9683 hwnd
, uMsg
, wParam
, lParam
, isW
);
9688 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
9695 WNDPROC editProc
= infoPtr
->EditWndProc
;
9696 infoPtr
->EditWndProc
= 0;
9697 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
9698 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
9702 if (VK_ESCAPE
== (INT
)wParam
)
9707 else if (VK_RETURN
== (INT
)wParam
)
9711 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
9715 if (infoPtr
->hwndEdit
)
9717 LPWSTR buffer
= NULL
;
9719 infoPtr
->hwndEdit
= 0;
9722 DWORD len
= isW
? GetWindowTextLengthW(hwnd
) : GetWindowTextLengthA(hwnd
);
9726 if ( (buffer
= Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))) )
9728 if (isW
) GetWindowTextW(hwnd
, buffer
, len
+1);
9729 else GetWindowTextA(hwnd
, (CHAR
*)buffer
, len
+1);
9733 LISTVIEW_EndEditLabelT(infoPtr
, buffer
, isW
);
9735 if (buffer
) Free(buffer
);
9739 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
9745 * Subclassed edit control Unicode windproc function
9748 * [I] hwnd : the edit window handle
9749 * [I] uMsg : the message that is to be processed
9750 * [I] wParam : first message parameter
9751 * [I] lParam : second message parameter
9755 LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9757 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
9762 * Subclassed edit control ANSI windproc function
9765 * [I] hwnd : the edit window handle
9766 * [I] uMsg : the message that is to be processed
9767 * [I] wParam : first message parameter
9768 * [I] lParam : second message parameter
9772 LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9774 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
9779 * Creates a subclassed edit cotrol
9782 * [I] infoPtr : valid pointer to the listview structure
9783 * [I] text : initial text for the edit
9784 * [I] style : the window style
9785 * [I] isW : TRUE if input is Unicode
9789 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, DWORD style
,
9790 INT x
, INT y
, INT width
, INT height
, BOOL isW
)
9792 WCHAR editName
[5] = { 'E', 'd', 'i', 't', '\0' };
9797 TEXTMETRICW textMetric
;
9798 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
9800 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text
, isW
), isW
);
9802 style
|= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
;
9803 hdc
= GetDC(infoPtr
->hwndSelf
);
9805 /* Select the font to get appropriate metric dimensions */
9806 if(infoPtr
->hFont
!= 0)
9807 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
9809 /*Get String Length in pixels */
9810 if(!GetTextExtentPoint32W(hdc
, text
, lstrlenW(text
), &sz
))
9813 /*Add Extra spacing for the next character */
9814 GetTextMetricsW(hdc
, &textMetric
);
9815 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
9817 if(infoPtr
->hFont
!= 0)
9818 SelectObject(hdc
, hOldFont
);
9820 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
9822 hedit
= CreateWindowW(editName
, text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
9824 hedit
= CreateWindowA("Edit", (LPCSTR
)text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
9826 if (!hedit
) return 0;
9828 infoPtr
->EditWndProc
= (WNDPROC
)
9829 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
9830 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
9832 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);