4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
35 * Default Message Processing
36 * -- EN_KILLFOCUS should be handled in WM_COMMAND
37 * -- WM_CREATE: create the icon and small icon image lists at this point only if
38 * the LVS_SHAREIMAGELISTS style is not specified.
39 * -- WM_ERASEBKGND: forward this message to the parent window if the bkgnd
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_[GS]etColumnOrderArray stubs
58 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
59 * -- LISTVIEW_SetIconSpacing is incomplete
60 * -- LISTVIEW_SortItems is broken
61 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
64 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
65 * linear in the number of items in the list, and this is
66 * unacceptable for large lists.
67 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
68 * instead of inserting in the right spot
69 * -- we should keep an ordered array of coordinates in iconic mode
70 * this would allow to frame items (iterator_frameditems),
71 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
79 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
86 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
89 * -- LVS_TYPESTYLEMASK
92 * -- LVS_EX_BORDERSELECT
95 * -- LVS_EX_HEADERDRAGDROP
98 * -- LVS_EX_MULTIWORKAREAS
99 * -- LVS_EX_ONECLICKACTIVATE
101 * -- LVS_EX_SIMPLESELECT
102 * -- LVS_EX_TRACKSELECT
103 * -- LVS_EX_TWOCLICKACTIVATE
104 * -- LVS_EX_UNDERLINECOLD
105 * -- LVS_EX_UNDERLINEHOT
108 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
111 * -- LVN_MARQUEEBEGIN
118 * -- LVM_CANCELEDITLABEL
119 * -- LVM_ENABLEGROUPVIEW
120 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
121 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
122 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
123 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
124 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
125 * -- LVM_GETINSERTMARKRECT
126 * -- LVM_GETNUMBEROFWORKAREAS
127 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
128 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
129 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
130 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
131 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
132 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
133 * -- LVM_GETVIEW, LVM_SETVIEW
134 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
135 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
136 * -- LVM_INSERTGROUPSORTED
137 * -- LVM_INSERTMARKHITTEST
138 * -- LVM_ISGROUPVIEWENABLED
139 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
141 * -- LVM_MOVEITEMTOGROUP
143 * -- LVM_SETTILEWIDTH
148 * -- ListView_GetCheckSate, ListView_SetCheckState
149 * -- ListView_GetHoverTime, ListView_SetHoverTime
150 * -- ListView_GetISearchString
151 * -- ListView_GetNumberOfWorkAreas
152 * -- ListView_GetOrigin
153 * -- ListView_GetTextBkColor
154 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
155 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
156 * -- ListView_SortItemsEx
161 * Known differences in message stream from native control (not known if
162 * these differences cause problems):
163 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
164 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
165 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
166 * processing for "USEDOUBLECLICKTIME".
170 #include "wine/port.h"
185 #include "commctrl.h"
186 #include "comctl32.h"
188 #include "wine/debug.h"
189 #include "wine/unicode.h"
191 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
193 /* make sure you set this to 0 for production use! */
194 #define DEBUG_RANGES 1
196 typedef struct tagCOLUMN_INFO
198 RECT rcHeader
; /* tracks the header's rectangle */
199 int fmt
; /* same as LVCOLUMN.fmt */
202 typedef struct tagITEMHDR
206 } ITEMHDR
, *LPITEMHDR
;
208 typedef struct tagSUBITEM_INFO
214 typedef struct tagITEM_INFO
222 typedef struct tagRANGE
228 typedef struct tagRANGES
233 typedef struct tagITERATOR
242 typedef struct tagLISTVIEW_INFO
249 COLORREF clrTextBkDefault
;
250 HIMAGELIST himlNormal
;
251 HIMAGELIST himlSmall
;
252 HIMAGELIST himlState
;
255 POINT ptClickPos
; /* point where the user clicked */
256 BOOL bNoItemMetrics
; /* flags if item metrics are not yet computed */
259 RANGES selectionRanges
;
264 RECT rcList
; /* This rectangle is really the window
265 * client rectangle possibly reduced by the
266 * horizontal scroll bar and/or header - see
267 * LISTVIEW_UpdateSize. This rectangle offset
268 * by the LISTVIEW_GetOrigin value is in
269 * client coordinates */
278 INT ntmHeight
; /* Some cached metrics of the font used */
279 INT ntmAveCharWidth
; /* by the listview to draw items */
280 BOOL bRedraw
; /* Turns on/off repaints & invalidations */
281 BOOL bAutoarrange
; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
283 BOOL bDoChangeNotify
; /* send change notification messages? */
286 DWORD dwStyle
; /* the cached window GWL_STYLE */
287 DWORD dwLvExStyle
; /* extended listview style */
288 INT nItemCount
; /* the number of items in the list */
289 HDPA hdpaItems
; /* array ITEM_INFO pointers */
290 HDPA hdpaPosX
; /* maintains the (X, Y) coordinates of the */
291 HDPA hdpaPosY
; /* items in LVS_ICON, and LVS_SMALLICON modes */
292 HDPA hdpaColumns
; /* array of COLUMN_INFO pointers */
293 POINT currIconPos
; /* this is the position next icon will be placed */
294 PFNLVCOMPARE pfnCompare
;
302 DWORD cditemmode
; /* Keep the custom draw flags for an item/row */
304 DWORD lastKeyPressTimestamp
;
306 INT nSearchParamLength
;
307 WCHAR szSearchParam
[ MAX_PATH
];
314 /* How many we debug buffer to allocate */
315 #define DEBUG_BUFFERS 20
316 /* The size of a single debug bbuffer */
317 #define DEBUG_BUFFER_SIZE 256
319 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
320 #define SB_INTERNAL -1
322 /* maximum size of a label */
323 #define DISP_TEXT_SIZE 512
325 /* padding for items in list and small icon display modes */
326 #define WIDTH_PADDING 12
328 /* padding for items in list, report and small icon display modes */
329 #define HEIGHT_PADDING 1
331 /* offset of items in report display mode */
332 #define REPORT_MARGINX 2
334 /* padding for icon in large icon display mode
335 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
336 * that HITTEST will see.
337 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
338 * ICON_TOP_PADDING - sum of the two above.
339 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
340 * LABEL_HOR_PADDING - between text and sides of box
341 * LABEL_VERT_PADDING - between bottom of text and end of box
343 * ICON_LR_PADDING - additional width above icon size.
344 * ICON_LR_HALF - half of the above value
346 #define ICON_TOP_PADDING_NOTHITABLE 2
347 #define ICON_TOP_PADDING_HITABLE 2
348 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
349 #define ICON_BOTTOM_PADDING 4
350 #define LABEL_HOR_PADDING 5
351 #define LABEL_VERT_PADDING 7
352 #define ICON_LR_PADDING 16
353 #define ICON_LR_HALF (ICON_LR_PADDING/2)
355 /* default label width for items in list and small icon display modes */
356 #define DEFAULT_LABEL_WIDTH 40
358 /* default column width for items in list display mode */
359 #define DEFAULT_COLUMN_WIDTH 128
361 /* Size of "line" scroll for V & H scrolls */
362 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
364 /* Padding betwen image and label */
365 #define IMAGE_PADDING 2
367 /* Padding behind the label */
368 #define TRAILING_LABEL_PADDING 12
369 #define TRAILING_HEADER_PADDING 11
371 /* Border for the icon caption */
372 #define CAPTION_BORDER 2
374 /* Standard DrawText flags */
375 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
376 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
377 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
379 /* The time in milliseconds to reset the search in the list */
380 #define KEY_DELAY 450
382 /* Dump the LISTVIEW_INFO structure to the debug channel */
383 #define LISTVIEW_DUMP(iP) do { \
384 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
385 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
386 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
387 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
388 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
389 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
390 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
391 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
392 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
393 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
397 * forward declarations
399 static BOOL
LISTVIEW_GetItemT(LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
400 static void LISTVIEW_GetItemBox(LISTVIEW_INFO
*, INT
, LPRECT
);
401 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO
*, INT
, LPPOINT
);
402 static BOOL
LISTVIEW_GetItemPosition(LISTVIEW_INFO
*, INT
, LPPOINT
);
403 static BOOL
LISTVIEW_GetItemRect(LISTVIEW_INFO
*, INT
, LPRECT
);
404 static INT
LISTVIEW_GetLabelWidth(LISTVIEW_INFO
*, INT
);
405 static void LISTVIEW_GetOrigin(LISTVIEW_INFO
*, LPPOINT
);
406 static BOOL
LISTVIEW_GetViewRect(LISTVIEW_INFO
*, LPRECT
);
407 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*, INT
);
408 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*, const LVITEMW
*, BOOL
);
409 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO
*);
410 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*, INT
);
411 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
412 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*, INT
, BOOL
);
413 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*, WPARAM
, LPARAM
);
414 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*, PFNLVCOMPARE
, LPARAM
);
415 static INT
LISTVIEW_GetStringWidthT(LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
416 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
);
417 static UINT
LISTVIEW_GetItemState(LISTVIEW_INFO
*, INT
, UINT
);
418 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, const LVITEMW
*);
419 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
420 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
421 static INT
LISTVIEW_GetTopIndex(LISTVIEW_INFO
*);
422 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
423 static HWND
CreateEditLabelT(LISTVIEW_INFO
*, LPCWSTR
, DWORD
, INT
, INT
, INT
, INT
, BOOL
);
424 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*, INT
, HIMAGELIST
);
425 static INT
LISTVIEW_HitTest(LISTVIEW_INFO
*, LPLVHITTESTINFO
, BOOL
, BOOL
);
427 /******** Text handling functions *************************************/
429 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
430 * text string. The string may be ANSI or Unicode, in which case
431 * the boolean isW tells us the type of the string.
433 * The name of the function tell what type of strings it expects:
434 * W: Unicode, T: ANSI/Unicode - function of isW
437 static inline BOOL
is_textW(LPCWSTR text
)
439 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
442 static inline BOOL
is_textT(LPCWSTR text
, BOOL isW
)
444 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
445 return is_textW(text
);
448 static inline int textlenT(LPCWSTR text
, BOOL isW
)
450 return !is_textT(text
, isW
) ? 0 :
451 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
454 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
457 if (isSrcW
) lstrcpynW(dest
, src
, max
);
458 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
460 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
461 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
464 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
466 LPWSTR wstr
= (LPWSTR
)text
;
468 if (!isW
&& is_textT(text
, isW
))
470 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
471 wstr
= Alloc(len
* sizeof(WCHAR
));
472 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
474 TRACE(" wstr=%s\n", text
== LPSTR_TEXTCALLBACKW
? "(callback)" : debugstr_w(wstr
));
478 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
480 if (!isW
&& is_textT(wstr
, isW
)) Free (wstr
);
484 * dest is a pointer to a Unicode string
485 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
487 static BOOL
textsetptrT(LPWSTR
*dest
, LPWSTR src
, BOOL isW
)
491 if (src
== LPSTR_TEXTCALLBACKW
)
493 if (is_textW(*dest
)) Free(*dest
);
494 *dest
= LPSTR_TEXTCALLBACKW
;
498 LPWSTR pszText
= textdupTtoW(src
, isW
);
499 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
500 bResult
= Str_SetPtrW(dest
, pszText
);
501 textfreeT(pszText
, isW
);
507 * compares a Unicode to a Unicode/ANSI text string
509 static inline int textcmpWT(LPCWSTR aw
, LPCWSTR bt
, BOOL isW
)
511 if (!aw
) return bt
? -1 : 0;
512 if (!bt
) return aw
? 1 : 0;
513 if (aw
== LPSTR_TEXTCALLBACKW
)
514 return bt
== LPSTR_TEXTCALLBACKW
? 0 : -1;
515 if (bt
!= LPSTR_TEXTCALLBACKW
)
517 LPWSTR bw
= textdupTtoW(bt
, isW
);
518 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
526 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
530 n
= min(min(n
, strlenW(s1
)), strlenW(s2
));
531 res
= CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
);
532 return res
? res
- sizeof(WCHAR
) : res
;
535 /******** Debugging functions *****************************************/
537 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
539 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
540 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
543 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
545 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
546 n
= min(textlenT(text
, isW
), n
);
547 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
550 static char* debug_getbuf()
552 static int index
= 0;
553 static char buffers
[DEBUG_BUFFERS
][DEBUG_BUFFER_SIZE
];
554 return buffers
[index
++ % DEBUG_BUFFERS
];
557 static inline const char* debugrange(const RANGE
*lprng
)
561 char* buf
= debug_getbuf();
562 snprintf(buf
, DEBUG_BUFFER_SIZE
, "[%d, %d)", lprng
->lower
, lprng
->upper
);
564 } else return "(null)";
567 static inline const char* debugpoint(const POINT
*lppt
)
571 char* buf
= debug_getbuf();
572 snprintf(buf
, DEBUG_BUFFER_SIZE
, "(%ld, %ld)", lppt
->x
, lppt
->y
);
574 } else return "(null)";
577 static inline const char* debugrect(const RECT
*rect
)
581 char* buf
= debug_getbuf();
582 snprintf(buf
, DEBUG_BUFFER_SIZE
, "[(%ld, %ld);(%ld, %ld)]",
583 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
585 } else return "(null)";
588 static const char* debugscrollinfo(const SCROLLINFO
*pScrollInfo
)
590 char* buf
= debug_getbuf(), *text
= buf
;
591 int len
, size
= DEBUG_BUFFER_SIZE
;
593 if (pScrollInfo
== NULL
) return "(null)";
594 len
= snprintf(buf
, size
, "{cbSize=%d, ", pScrollInfo
->cbSize
);
595 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
596 if (pScrollInfo
->fMask
& SIF_RANGE
)
597 len
= snprintf(buf
, size
, "nMin=%d, nMax=%d, ", pScrollInfo
->nMin
, pScrollInfo
->nMax
);
599 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
600 if (pScrollInfo
->fMask
& SIF_PAGE
)
601 len
= snprintf(buf
, size
, "nPage=%u, ", pScrollInfo
->nPage
);
603 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
604 if (pScrollInfo
->fMask
& SIF_POS
)
605 len
= snprintf(buf
, size
, "nPos=%d, ", pScrollInfo
->nPos
);
607 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
608 if (pScrollInfo
->fMask
& SIF_TRACKPOS
)
609 len
= snprintf(buf
, size
, "nTrackPos=%d, ", pScrollInfo
->nTrackPos
);
611 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
614 buf
= text
+ strlen(text
);
616 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
620 static const char* debugnmlistview(const NMLISTVIEW
*plvnm
)
624 char* buf
= debug_getbuf();
625 snprintf(buf
, DEBUG_BUFFER_SIZE
, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
626 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
627 plvnm
->iItem
, plvnm
->iSubItem
, plvnm
->uNewState
, plvnm
->uOldState
,
628 plvnm
->uChanged
, debugpoint(&plvnm
->ptAction
), plvnm
->lParam
);
630 } else return "(null)";
633 static const char* debuglvitem_t(const LVITEMW
*lpLVItem
, BOOL isW
)
635 char* buf
= debug_getbuf(), *text
= buf
;
636 int len
, size
= DEBUG_BUFFER_SIZE
;
638 if (lpLVItem
== NULL
) return "(null)";
639 len
= snprintf(buf
, size
, "{iItem=%d, iSubItem=%d, ", lpLVItem
->iItem
, lpLVItem
->iSubItem
);
640 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
641 if (lpLVItem
->mask
& LVIF_STATE
)
642 len
= snprintf(buf
, size
, "state=%x, stateMask=%x, ", lpLVItem
->state
, lpLVItem
->stateMask
);
644 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
645 if (lpLVItem
->mask
& LVIF_TEXT
)
646 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem
->pszText
, isW
, 80), lpLVItem
->cchTextMax
);
648 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
649 if (lpLVItem
->mask
& LVIF_IMAGE
)
650 len
= snprintf(buf
, size
, "iImage=%d, ", lpLVItem
->iImage
);
652 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
653 if (lpLVItem
->mask
& LVIF_PARAM
)
654 len
= snprintf(buf
, size
, "lParam=%lx, ", lpLVItem
->lParam
);
656 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
657 if (lpLVItem
->mask
& LVIF_INDENT
)
658 len
= snprintf(buf
, size
, "iIndent=%d, ", lpLVItem
->iIndent
);
660 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
663 buf
= text
+ strlen(text
);
665 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
669 static const char* debuglvcolumn_t(const LVCOLUMNW
*lpColumn
, BOOL isW
)
671 char* buf
= debug_getbuf(), *text
= buf
;
672 int len
, size
= DEBUG_BUFFER_SIZE
;
674 if (lpColumn
== NULL
) return "(null)";
675 len
= snprintf(buf
, size
, "{");
676 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
677 if (lpColumn
->mask
& LVCF_SUBITEM
)
678 len
= snprintf(buf
, size
, "iSubItem=%d, ", lpColumn
->iSubItem
);
680 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
681 if (lpColumn
->mask
& LVCF_FMT
)
682 len
= snprintf(buf
, size
, "fmt=%x, ", lpColumn
->fmt
);
684 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
685 if (lpColumn
->mask
& LVCF_WIDTH
)
686 len
= snprintf(buf
, size
, "cx=%d, ", lpColumn
->cx
);
688 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
689 if (lpColumn
->mask
& LVCF_TEXT
)
690 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn
->pszText
, isW
, 80), lpColumn
->cchTextMax
);
692 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
693 if (lpColumn
->mask
& LVCF_IMAGE
)
694 len
= snprintf(buf
, size
, "iImage=%d, ", lpColumn
->iImage
);
696 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
697 if (lpColumn
->mask
& LVCF_ORDER
)
698 len
= snprintf(buf
, size
, "iOrder=%d, ", lpColumn
->iOrder
);
700 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
703 buf
= text
+ strlen(text
);
705 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
709 static const char* debuglvhittestinfo(const LVHITTESTINFO
*lpht
)
713 char* buf
= debug_getbuf();
714 snprintf(buf
, DEBUG_BUFFER_SIZE
, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
715 debugpoint(&lpht
->pt
), lpht
->flags
, lpht
->iItem
, lpht
->iSubItem
);
717 } else return "(null)";
720 /* Return the corresponding text for a given scroll value */
721 static inline LPCSTR
debugscrollcode(int nScrollCode
)
725 case SB_LINELEFT
: return "SB_LINELEFT";
726 case SB_LINERIGHT
: return "SB_LINERIGHT";
727 case SB_PAGELEFT
: return "SB_PAGELEFT";
728 case SB_PAGERIGHT
: return "SB_PAGERIGHT";
729 case SB_THUMBPOSITION
: return "SB_THUMBPOSITION";
730 case SB_THUMBTRACK
: return "SB_THUMBTRACK";
731 case SB_ENDSCROLL
: return "SB_ENDSCROLL";
732 case SB_INTERNAL
: return "SB_INTERNAL";
733 default: return "unknown";
738 /******** Notification functions i************************************/
740 static LRESULT
notify_hdr(LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
744 TRACE("(code=%d)\n", code
);
746 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
747 pnmh
->idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
749 result
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
750 (WPARAM
)pnmh
->idFrom
, (LPARAM
)pnmh
);
752 TRACE(" <= %ld\n", result
);
757 static inline LRESULT
notify(LISTVIEW_INFO
*infoPtr
, INT code
)
760 return notify_hdr(infoPtr
, code
, &nmh
);
763 static inline void notify_itemactivate(LISTVIEW_INFO
*infoPtr
, LVHITTESTINFO
*htInfo
)
774 item
.mask
= LVIF_PARAM
|LVIF_STATE
;
775 item
.iItem
= htInfo
->iItem
;
777 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) {
778 nmia
.lParam
= item
.lParam
;
779 nmia
.uOldState
= item
.state
;
780 nmia
.uNewState
= item
.state
| LVIS_ACTIVATING
;
781 nmia
.uChanged
= LVIF_STATE
;
784 nmia
.iItem
= htInfo
->iItem
;
785 nmia
.iSubItem
= htInfo
->iSubItem
;
786 nmia
.ptAction
= htInfo
->pt
;
788 if (GetKeyState(VK_SHIFT
) & 0x8000) nmia
.uKeyFlags
|= LVKF_SHIFT
;
789 if (GetKeyState(VK_CONTROL
) & 0x8000) nmia
.uKeyFlags
|= LVKF_CONTROL
;
790 if (GetKeyState(VK_MENU
) & 0x8000) nmia
.uKeyFlags
|= LVKF_ALT
;
792 notify_hdr(infoPtr
, LVN_ITEMACTIVATE
, (LPNMHDR
)&nmia
);
795 static inline LRESULT
notify_listview(LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
797 TRACE("(code=%d, plvnm=%s)\n", code
, debugnmlistview(plvnm
));
798 return notify_hdr(infoPtr
, code
, (LPNMHDR
)plvnm
);
801 static LRESULT
notify_click(LISTVIEW_INFO
*infoPtr
, INT code
, LVHITTESTINFO
*lvht
)
806 TRACE("code=%d, lvht=%s\n", code
, debuglvhittestinfo(lvht
));
807 ZeroMemory(&nmlv
, sizeof(nmlv
));
808 nmlv
.iItem
= lvht
->iItem
;
809 nmlv
.iSubItem
= lvht
->iSubItem
;
810 nmlv
.ptAction
= lvht
->pt
;
811 item
.mask
= LVIF_PARAM
;
812 item
.iItem
= lvht
->iItem
;
814 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
815 return notify_listview(infoPtr
, code
, &nmlv
);
818 static void notify_deleteitem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
823 ZeroMemory(&nmlv
, sizeof (NMLISTVIEW
));
825 item
.mask
= LVIF_PARAM
;
828 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
829 notify_listview(infoPtr
, LVN_DELETEITEM
, &nmlv
);
832 static int get_ansi_notification(INT unicodeNotificationCode
)
834 switch (unicodeNotificationCode
)
836 case LVN_BEGINLABELEDITW
: return LVN_BEGINLABELEDITA
;
837 case LVN_ENDLABELEDITW
: return LVN_ENDLABELEDITA
;
838 case LVN_GETDISPINFOW
: return LVN_GETDISPINFOA
;
839 case LVN_SETDISPINFOW
: return LVN_SETDISPINFOA
;
840 case LVN_ODFINDITEMW
: return LVN_ODFINDITEMA
;
841 case LVN_GETINFOTIPW
: return LVN_GETINFOTIPA
;
843 ERR("unknown notification %x\n", unicodeNotificationCode
);
849 Send notification. depends on dispinfoW having same
850 structure as dispinfoA.
851 infoPtr : listview struct
852 notificationCode : *Unicode* notification code
853 pdi : dispinfo structure (can be unicode or ansi)
854 isW : TRUE if dispinfo is Unicode
856 static BOOL
notify_dispinfoT(LISTVIEW_INFO
*infoPtr
, INT notificationCode
, LPNMLVDISPINFOW pdi
, BOOL isW
)
858 BOOL bResult
= FALSE
;
859 BOOL convertToAnsi
= FALSE
, convertToUnicode
= FALSE
;
860 INT cchTempBufMax
= 0, savCchTextMax
= 0, realNotifCode
;
861 LPWSTR pszTempBuf
= NULL
, savPszText
= NULL
;
863 if ((pdi
->item
.mask
& LVIF_TEXT
) && is_textT(pdi
->item
.pszText
, isW
))
865 convertToAnsi
= (isW
&& infoPtr
->notifyFormat
== NFR_ANSI
);
866 convertToUnicode
= (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
);
869 if (convertToAnsi
|| convertToUnicode
)
871 if (notificationCode
!= LVN_GETDISPINFOW
)
873 cchTempBufMax
= convertToUnicode
?
874 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
875 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
879 cchTempBufMax
= pdi
->item
.cchTextMax
;
880 *pdi
->item
.pszText
= 0; /* make sure we don't process garbage */
883 pszTempBuf
= Alloc( (convertToUnicode
? sizeof(WCHAR
) : sizeof(CHAR
)) * cchTempBufMax
);
884 if (!pszTempBuf
) return FALSE
;
886 if (convertToUnicode
)
887 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
888 pszTempBuf
, cchTempBufMax
);
890 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) pszTempBuf
,
891 cchTempBufMax
, NULL
, NULL
);
893 savCchTextMax
= pdi
->item
.cchTextMax
;
894 savPszText
= pdi
->item
.pszText
;
895 pdi
->item
.pszText
= pszTempBuf
;
896 pdi
->item
.cchTextMax
= cchTempBufMax
;
899 if (infoPtr
->notifyFormat
== NFR_ANSI
)
900 realNotifCode
= get_ansi_notification(notificationCode
);
902 realNotifCode
= notificationCode
;
903 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi
->item
, infoPtr
->notifyFormat
!= NFR_ANSI
));
904 bResult
= notify_hdr(infoPtr
, realNotifCode
, &pdi
->hdr
);
906 if (convertToUnicode
|| convertToAnsi
)
908 if (convertToUnicode
) /* note : pointer can be changed by app ! */
909 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) savPszText
,
910 savCchTextMax
, NULL
, NULL
);
912 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
913 savPszText
, savCchTextMax
);
914 pdi
->item
.pszText
= savPszText
; /* restores our buffer */
915 pdi
->item
.cchTextMax
= savCchTextMax
;
921 static void customdraw_fill(NMLVCUSTOMDRAW
*lpnmlvcd
, LISTVIEW_INFO
*infoPtr
, HDC hdc
,
922 const RECT
*rcBounds
, const LVITEMW
*lplvItem
)
924 ZeroMemory(lpnmlvcd
, sizeof(NMLVCUSTOMDRAW
));
925 lpnmlvcd
->nmcd
.hdc
= hdc
;
926 lpnmlvcd
->nmcd
.rc
= *rcBounds
;
927 lpnmlvcd
->clrTextBk
= infoPtr
->clrTextBk
;
928 lpnmlvcd
->clrText
= infoPtr
->clrText
;
929 if (!lplvItem
) return;
930 lpnmlvcd
->nmcd
.dwItemSpec
= lplvItem
->iItem
+ 1;
931 lpnmlvcd
->iSubItem
= lplvItem
->iSubItem
;
932 if (lplvItem
->state
& LVIS_SELECTED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_SELECTED
;
933 if (lplvItem
->state
& LVIS_FOCUSED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_FOCUS
;
934 if (lplvItem
->iItem
== infoPtr
->nHotItem
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_HOT
;
935 lpnmlvcd
->nmcd
.lItemlParam
= lplvItem
->lParam
;
938 static inline DWORD
notify_customdraw (LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, NMLVCUSTOMDRAW
*lpnmlvcd
)
940 BOOL isForItem
= (lpnmlvcd
->nmcd
.dwItemSpec
!= 0);
943 lpnmlvcd
->nmcd
.dwDrawStage
= dwDrawStage
;
944 if (isForItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_ITEM
;
945 if (lpnmlvcd
->iSubItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_SUBITEM
;
946 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
--;
947 result
= notify_hdr(infoPtr
, NM_CUSTOMDRAW
, &lpnmlvcd
->nmcd
.hdr
);
948 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
++;
952 static void prepaint_setup (LISTVIEW_INFO
*infoPtr
, HDC hdc
, NMLVCUSTOMDRAW
*lpnmlvcd
)
954 /* apprently, for selected items, we have to override the returned values */
955 if (lpnmlvcd
->nmcd
.uItemState
& CDIS_SELECTED
)
959 lpnmlvcd
->clrTextBk
= comctl32_color
.clrHighlight
;
960 lpnmlvcd
->clrText
= comctl32_color
.clrHighlightText
;
962 else if (infoPtr
->dwStyle
& LVS_SHOWSELALWAYS
)
964 lpnmlvcd
->clrTextBk
= comctl32_color
.clr3dFace
;
965 lpnmlvcd
->clrText
= comctl32_color
.clrBtnText
;
969 /* Set the text attributes */
970 if (lpnmlvcd
->clrTextBk
!= CLR_NONE
)
972 SetBkMode(hdc
, OPAQUE
);
973 if (lpnmlvcd
->clrTextBk
== CLR_DEFAULT
)
974 SetBkColor(hdc
, infoPtr
->clrTextBkDefault
);
976 SetBkColor(hdc
,lpnmlvcd
->clrTextBk
);
979 SetBkMode(hdc
, TRANSPARENT
);
980 SetTextColor(hdc
, lpnmlvcd
->clrText
);
983 static inline DWORD
notify_postpaint (LISTVIEW_INFO
*infoPtr
, NMLVCUSTOMDRAW
*lpnmlvcd
)
985 return notify_customdraw(infoPtr
, CDDS_POSTPAINT
, lpnmlvcd
);
988 /******** Item iterator functions **********************************/
990 static RANGES
ranges_create(int count
);
991 static void ranges_destroy(RANGES ranges
);
992 static BOOL
ranges_add(RANGES ranges
, RANGE range
);
993 static BOOL
ranges_del(RANGES ranges
, RANGE range
);
994 static void ranges_dump(RANGES ranges
);
996 static inline BOOL
ranges_additem(RANGES ranges
, INT nItem
)
998 RANGE range
= { nItem
, nItem
+ 1 };
1000 return ranges_add(ranges
, range
);
1003 static inline BOOL
ranges_delitem(RANGES ranges
, INT nItem
)
1005 RANGE range
= { nItem
, nItem
+ 1 };
1007 return ranges_del(ranges
, range
);
1011 * ITERATOR DOCUMENTATION
1013 * The iterator functions allow for easy, and convenient iteration
1014 * over items of iterest in the list. Typically, you create a
1015 * iterator, use it, and destroy it, as such:
1018 * iterator_xxxitems(&i, ...);
1019 * while (iterator_{prev,next}(&i)
1021 * //code which uses i.nItem
1023 * iterator_destroy(&i);
1025 * where xxx is either: framed, or visible.
1026 * Note that it is important that the code destroys the iterator
1027 * after it's done with it, as the creation of the iterator may
1028 * allocate memory, which thus needs to be freed.
1030 * You can iterate both forwards, and backwards through the list,
1031 * by using iterator_next or iterator_prev respectively.
1033 * Lower numbered items are draw on top of higher number items in
1034 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1035 * items may overlap). So, to test items, you should use
1037 * which lists the items top to bottom (in Z-order).
1038 * For drawing items, you should use
1040 * which lists the items bottom to top (in Z-order).
1041 * If you keep iterating over the items after the end-of-items
1042 * marker (-1) is returned, the iterator will start from the
1043 * beginning. Typically, you don't need to test for -1,
1044 * because iterator_{next,prev} will return TRUE if more items
1045 * are to be iterated over, or FALSE otherwise.
1047 * Note: the iterator is defined to be bidirectional. That is,
1048 * any number of prev followed by any number of next, or
1049 * five versa, should leave the iterator at the same item:
1050 * prev * n, next * n = next * n, prev * n
1052 * The iterator has a notion of an out-of-order, special item,
1053 * which sits at the start of the list. This is used in
1054 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1055 * which needs to be first, as it may overlap other items.
1057 * The code is a bit messy because we have:
1058 * - a special item to deal with
1059 * - simple range, or composite range
1061 * If you find bugs, or want to add features, please make sure you
1062 * always check/modify *both* iterator_prev, and iterator_next.
1066 * This function iterates through the items in increasing order,
1067 * but prefixed by the special item, then -1. That is:
1068 * special, 1, 2, 3, ..., n, -1.
1069 * Each item is listed only once.
1071 static inline BOOL
iterator_next(ITERATOR
* i
)
1075 i
->nItem
= i
->nSpecial
;
1076 if (i
->nItem
!= -1) return TRUE
;
1078 if (i
->nItem
== i
->nSpecial
)
1080 if (i
->ranges
) i
->index
= 0;
1086 if (i
->nItem
== i
->nSpecial
) i
->nItem
++;
1087 if (i
->nItem
< i
->range
.upper
) return TRUE
;
1092 if (i
->index
< DPA_GetPtrCount(i
->ranges
->hdpa
))
1093 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, i
->index
++);
1096 else if (i
->nItem
>= i
->range
.upper
) goto end
;
1098 i
->nItem
= i
->range
.lower
;
1099 if (i
->nItem
>= 0) goto testitem
;
1106 * This function iterates through the items in decreasing order,
1107 * followed by the special item, then -1. That is:
1108 * n, n-1, ..., 3, 2, 1, special, -1.
1109 * Each item is listed only once.
1111 static inline BOOL
iterator_prev(ITERATOR
* i
)
1118 if (i
->ranges
) i
->index
= DPA_GetPtrCount(i
->ranges
->hdpa
);
1121 if (i
->nItem
== i
->nSpecial
)
1129 if (i
->nItem
== i
->nSpecial
) i
->nItem
--;
1130 if (i
->nItem
>= i
->range
.lower
) return TRUE
;
1136 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, --i
->index
);
1139 else if (!start
&& i
->nItem
< i
->range
.lower
) goto end
;
1141 i
->nItem
= i
->range
.upper
;
1142 if (i
->nItem
> 0) goto testitem
;
1144 return (i
->nItem
= i
->nSpecial
) != -1;
1147 static RANGE
iterator_range(ITERATOR
* i
)
1151 if (!i
->ranges
) return i
->range
;
1153 if (DPA_GetPtrCount(i
->ranges
->hdpa
) > 0)
1155 range
.lower
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, 0)).lower
;
1156 range
.upper
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, DPA_GetPtrCount(i
->ranges
->hdpa
) - 1)).upper
;
1158 else range
.lower
= range
.upper
= 0;
1164 * Releases resources associated with this ierator.
1166 static inline void iterator_destroy(ITERATOR
* i
)
1168 ranges_destroy(i
->ranges
);
1172 * Create an empty iterator.
1174 static inline BOOL
iterator_empty(ITERATOR
* i
)
1176 ZeroMemory(i
, sizeof(*i
));
1177 i
->nItem
= i
->nSpecial
= i
->range
.lower
= i
->range
.upper
= -1;
1182 * Create an iterator over a range.
1184 static inline BOOL
iterator_rangeitems(ITERATOR
* i
, RANGE range
)
1192 * Create an iterator over a bunch of ranges.
1193 * Please note that the iterator will take ownership of the ranges,
1194 * and will free them upon destruction.
1196 static inline BOOL
iterator_rangesitems(ITERATOR
* i
, RANGES ranges
)
1204 * Creates an iterator over the items which intersect lprc.
1206 static BOOL
iterator_frameditems(ITERATOR
* i
, LISTVIEW_INFO
* infoPtr
, const RECT
*lprc
)
1208 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1209 RECT frame
= *lprc
, rcItem
, rcTemp
;
1212 /* in case we fail, we want to return an empty iterator */
1213 if (!iterator_empty(i
)) return FALSE
;
1215 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1217 TRACE("(lprc=%s)\n", debugrect(lprc
));
1218 OffsetRect(&frame
, -Origin
.x
, -Origin
.y
);
1220 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
1224 if (uView
== LVS_ICON
&& infoPtr
->nFocusedItem
!= -1)
1226 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcItem
);
1227 if (IntersectRect(&rcTemp
, &rcItem
, lprc
))
1228 i
->nSpecial
= infoPtr
->nFocusedItem
;
1230 if (!(iterator_rangesitems(i
, ranges_create(50)))) return FALSE
;
1231 /* to do better here, we need to have PosX, and PosY sorted */
1232 TRACE("building icon ranges:\n");
1233 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
1235 rcItem
.left
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1236 rcItem
.top
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1237 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1238 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1239 if (IntersectRect(&rcTemp
, &rcItem
, &frame
))
1240 ranges_additem(i
->ranges
, nItem
);
1244 else if (uView
== LVS_REPORT
)
1248 if (frame
.left
>= infoPtr
->nItemWidth
) return TRUE
;
1249 if (frame
.top
>= infoPtr
->nItemHeight
* infoPtr
->nItemCount
) return TRUE
;
1251 range
.lower
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1252 range
.upper
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, infoPtr
->nItemCount
- 1) + 1;
1253 if (range
.upper
<= range
.lower
) return TRUE
;
1254 if (!iterator_rangeitems(i
, range
)) return FALSE
;
1255 TRACE(" report=%s\n", debugrange(&i
->range
));
1259 INT nPerCol
= max((infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
, 1);
1260 INT nFirstRow
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1261 INT nLastRow
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, nPerCol
- 1);
1262 INT nFirstCol
= max(frame
.left
/ infoPtr
->nItemWidth
, 0);
1263 INT nLastCol
= min((frame
.right
- 1) / infoPtr
->nItemWidth
, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1264 INT lower
= nFirstCol
* nPerCol
+ nFirstRow
;
1268 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1269 nPerCol
, nFirstRow
, nLastRow
, nFirstCol
, nLastCol
, lower
);
1271 if (nLastCol
< nFirstCol
|| nLastRow
< nFirstRow
) return TRUE
;
1273 if (!(iterator_rangesitems(i
, ranges_create(nLastCol
- nFirstCol
+ 1)))) return FALSE
;
1274 TRACE("building list ranges:\n");
1275 for (nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++)
1277 item_range
.lower
= nCol
* nPerCol
+ nFirstRow
;
1278 if(item_range
.lower
>= infoPtr
->nItemCount
) break;
1279 item_range
.upper
= min(nCol
* nPerCol
+ nLastRow
+ 1, infoPtr
->nItemCount
);
1280 TRACE(" list=%s\n", debugrange(&item_range
));
1281 ranges_add(i
->ranges
, item_range
);
1289 * Creates an iterator over the items which intersect the visible region of hdc.
1291 static BOOL
iterator_visibleitems(ITERATOR
*i
, LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1293 POINT Origin
, Position
;
1294 RECT rcItem
, rcClip
;
1297 rgntype
= GetClipBox(hdc
, &rcClip
);
1298 if (rgntype
== NULLREGION
) return iterator_empty(i
);
1299 if (!iterator_frameditems(i
, infoPtr
, &rcClip
)) return FALSE
;
1300 if (rgntype
== SIMPLEREGION
) return TRUE
;
1302 /* first deal with the special item */
1303 if (i
->nSpecial
!= -1)
1305 LISTVIEW_GetItemBox(infoPtr
, i
->nSpecial
, &rcItem
);
1306 if (!RectVisible(hdc
, &rcItem
)) i
->nSpecial
= -1;
1309 /* if we can't deal with the region, we'll just go with the simple range */
1310 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1311 TRACE("building visible range:\n");
1312 if (!i
->ranges
&& i
->range
.lower
< i
->range
.upper
)
1314 if (!(i
->ranges
= ranges_create(50))) return TRUE
;
1315 if (!ranges_add(i
->ranges
, i
->range
))
1317 ranges_destroy(i
->ranges
);
1323 /* now delete the invisible items from the list */
1324 while(iterator_next(i
))
1326 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
1327 rcItem
.left
= Position
.x
+ Origin
.x
;
1328 rcItem
.top
= Position
.y
+ Origin
.y
;
1329 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1330 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1331 if (!RectVisible(hdc
, &rcItem
))
1332 ranges_delitem(i
->ranges
, i
->nItem
);
1334 /* the iterator should restart on the next iterator_next */
1340 /******** Misc helper functions ************************************/
1342 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
1343 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
1345 if (isW
) return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
1346 else return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
1349 static inline BOOL
is_autoarrange(LISTVIEW_INFO
*infoPtr
)
1351 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1353 return ((infoPtr
->dwStyle
& LVS_AUTOARRANGE
) || infoPtr
->bAutoarrange
) &&
1354 (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
);
1357 /******** Internal API functions ************************************/
1359 static inline COLUMN_INFO
* LISTVIEW_GetColumnInfo(LISTVIEW_INFO
*infoPtr
, INT nSubItem
)
1361 static COLUMN_INFO mainItem
;
1363 if (nSubItem
== 0 && DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0) return &mainItem
;
1364 assert (nSubItem
>= 0 && nSubItem
< DPA_GetPtrCount(infoPtr
->hdpaColumns
));
1365 return (COLUMN_INFO
*)DPA_GetPtr(infoPtr
->hdpaColumns
, nSubItem
);
1368 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO
*infoPtr
, INT nSubItem
, RECT
*lprc
)
1370 *lprc
= LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->rcHeader
;
1373 static inline BOOL
LISTVIEW_GetItemW(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
1375 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
1378 /* Listview invalidation functions: use _only_ these functions to invalidate */
1380 static inline BOOL
is_redrawing(LISTVIEW_INFO
*infoPtr
)
1382 return infoPtr
->bRedraw
;
1385 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO
*infoPtr
, const RECT
* rect
)
1387 if(!is_redrawing(infoPtr
)) return;
1388 TRACE(" invalidating rect=%s\n", debugrect(rect
));
1389 InvalidateRect(infoPtr
->hwndSelf
, rect
, TRUE
);
1392 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1396 if(!is_redrawing(infoPtr
)) return;
1397 LISTVIEW_GetItemBox(infoPtr
, nItem
, &rcBox
);
1398 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1401 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
)
1403 POINT Origin
, Position
;
1406 if(!is_redrawing(infoPtr
)) return;
1407 assert ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
);
1408 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1409 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
1410 LISTVIEW_GetHeaderRect(infoPtr
, nSubItem
, &rcBox
);
1412 rcBox
.bottom
= infoPtr
->nItemHeight
;
1413 OffsetRect(&rcBox
, Origin
.x
+ Position
.x
, Origin
.y
+ Position
.y
);
1414 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1417 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO
*infoPtr
)
1419 LISTVIEW_InvalidateRect(infoPtr
, NULL
);
1422 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
1426 if(!is_redrawing(infoPtr
)) return;
1427 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
1428 rcCol
.top
= infoPtr
->rcList
.top
;
1429 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
1430 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
1435 * Retrieves the number of items that can fit vertically in the client area.
1438 * [I] infoPtr : valid pointer to the listview structure
1441 * Number of items per row.
1443 static inline INT
LISTVIEW_GetCountPerRow(LISTVIEW_INFO
*infoPtr
)
1445 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1447 return max(nListWidth
/infoPtr
->nItemWidth
, 1);
1452 * Retrieves the number of items that can fit horizontally in the client
1456 * [I] infoPtr : valid pointer to the listview structure
1459 * Number of items per column.
1461 static inline INT
LISTVIEW_GetCountPerColumn(LISTVIEW_INFO
*infoPtr
)
1463 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1465 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
1469 /*************************************************************************
1470 * LISTVIEW_ProcessLetterKeys
1472 * Processes keyboard messages generated by pressing the letter keys
1474 * What this does is perform a case insensitive search from the
1475 * current position with the following quirks:
1476 * - If two chars or more are pressed in quick succession we search
1477 * for the corresponding string (e.g. 'abc').
1478 * - If there is a delay we wipe away the current search string and
1479 * restart with just that char.
1480 * - If the user keeps pressing the same character, whether slowly or
1481 * fast, so that the search string is entirely composed of this
1482 * character ('aaaaa' for instance), then we search for first item
1483 * that starting with that character.
1484 * - If the user types the above character in quick succession, then
1485 * we must also search for the corresponding string ('aaaaa'), and
1486 * go to that string if there is a match.
1489 * [I] hwnd : handle to the window
1490 * [I] charCode : the character code, the actual character
1491 * [I] keyData : key data
1499 * - The current implementation has a list of characters it will
1500 * accept and it ignores averything else. In particular it will
1501 * ignore accentuated characters which seems to match what
1502 * Windows does. But I'm not sure it makes sense to follow
1504 * - We don't sound a beep when the search fails.
1508 * TREEVIEW_ProcessLetterKeys
1510 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
1515 WCHAR buffer
[MAX_PATH
];
1516 DWORD lastKeyPressTimestamp
= infoPtr
->lastKeyPressTimestamp
;
1518 /* simple parameter checking */
1519 if (!charCode
|| !keyData
) return 0;
1521 /* only allow the valid WM_CHARs through */
1522 if (!isalnum(charCode
) &&
1523 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
1524 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
1525 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
1526 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
1527 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
1528 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
1529 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
1530 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
1531 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
1534 /* if there's one item or less, there is no where to go */
1535 if (infoPtr
->nItemCount
<= 1) return 0;
1537 /* update the search parameters */
1538 infoPtr
->lastKeyPressTimestamp
= GetTickCount();
1539 if (infoPtr
->lastKeyPressTimestamp
- lastKeyPressTimestamp
< KEY_DELAY
) {
1540 if (infoPtr
->nSearchParamLength
< MAX_PATH
)
1541 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++]=charCode
;
1542 if (infoPtr
->charCode
!= charCode
)
1543 infoPtr
->charCode
= charCode
= 0;
1545 infoPtr
->charCode
=charCode
;
1546 infoPtr
->szSearchParam
[0]=charCode
;
1547 infoPtr
->nSearchParamLength
=1;
1548 /* Redundant with the 1 char string */
1552 /* and search from the current position */
1554 if (infoPtr
->nFocusedItem
>= 0) {
1555 endidx
=infoPtr
->nFocusedItem
;
1557 /* if looking for single character match,
1558 * then we must always move forward
1560 if (infoPtr
->nSearchParamLength
== 1)
1563 endidx
=infoPtr
->nItemCount
;
1567 if (idx
== infoPtr
->nItemCount
) {
1568 if (endidx
== infoPtr
->nItemCount
|| endidx
== 0)
1574 item
.mask
= LVIF_TEXT
;
1577 item
.pszText
= buffer
;
1578 item
.cchTextMax
= MAX_PATH
;
1579 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
1581 /* check for a match */
1582 if (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,infoPtr
->nSearchParamLength
) == 0) {
1585 } else if ( (charCode
!= 0) && (nItem
== -1) && (nItem
!= infoPtr
->nFocusedItem
) &&
1586 (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,1) == 0) ) {
1587 /* This would work but we must keep looking for a longer match */
1591 } while (idx
!= endidx
);
1594 LISTVIEW_KeySelection(infoPtr
, nItem
);
1599 /*************************************************************************
1600 * LISTVIEW_UpdateHeaderSize [Internal]
1602 * Function to resize the header control
1605 * [I] hwnd : handle to a window
1606 * [I] nNewScrollPos : scroll pos to set
1611 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
1616 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
1618 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
1619 point
[0].x
= winRect
.left
;
1620 point
[0].y
= winRect
.top
;
1621 point
[1].x
= winRect
.right
;
1622 point
[1].y
= winRect
.bottom
;
1624 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
1625 point
[0].x
= -nNewScrollPos
;
1626 point
[1].x
+= nNewScrollPos
;
1628 SetWindowPos(infoPtr
->hwndHeader
,0,
1629 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
1630 SWP_NOZORDER
| SWP_NOACTIVATE
);
1635 * Update the scrollbars. This functions should be called whenever
1636 * the content, size or view changes.
1639 * [I] infoPtr : valid pointer to the listview structure
1644 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO
*infoPtr
)
1646 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1647 SCROLLINFO horzInfo
, vertInfo
;
1649 if ((infoPtr
->dwStyle
& LVS_NOSCROLL
) || !is_redrawing(infoPtr
)) return;
1651 ZeroMemory(&horzInfo
, sizeof(SCROLLINFO
));
1652 horzInfo
.cbSize
= sizeof(SCROLLINFO
);
1653 horzInfo
.nPage
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1655 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1656 if (uView
== LVS_LIST
)
1658 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
1659 horzInfo
.nMax
= (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
;
1661 /* scroll by at least one column per page */
1662 if(horzInfo
.nPage
< infoPtr
->nItemWidth
)
1663 horzInfo
.nPage
= infoPtr
->nItemWidth
;
1665 horzInfo
.nPage
/= infoPtr
->nItemWidth
;
1667 else if (uView
== LVS_REPORT
)
1669 horzInfo
.nMax
= infoPtr
->nItemWidth
;
1671 else /* LVS_ICON, or LVS_SMALLICON */
1675 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) horzInfo
.nMax
= rcView
.right
- rcView
.left
;
1678 horzInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1679 horzInfo
.nMax
= max(horzInfo
.nMax
- 1, 0);
1680 SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
, TRUE
);
1681 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo
));
1683 /* Setting the horizontal scroll can change the listview size
1684 * (and potentially everything else) so we need to recompute
1685 * everything again for the vertical scroll
1688 ZeroMemory(&vertInfo
, sizeof(SCROLLINFO
));
1689 vertInfo
.cbSize
= sizeof(SCROLLINFO
);
1690 vertInfo
.nPage
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1692 if (uView
== LVS_REPORT
)
1694 vertInfo
.nMax
= infoPtr
->nItemCount
;
1696 /* scroll by at least one page */
1697 if(vertInfo
.nPage
< infoPtr
->nItemHeight
)
1698 vertInfo
.nPage
= infoPtr
->nItemHeight
;
1700 vertInfo
.nPage
/= infoPtr
->nItemHeight
;
1702 else if (uView
!= LVS_LIST
) /* LVS_ICON, or LVS_SMALLICON */
1706 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) vertInfo
.nMax
= rcView
.bottom
- rcView
.top
;
1709 vertInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1710 vertInfo
.nMax
= max(vertInfo
.nMax
- 1, 0);
1711 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &vertInfo
, TRUE
);
1712 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo
));
1714 /* Update the Header Control */
1715 if (uView
== LVS_REPORT
)
1717 horzInfo
.fMask
= SIF_POS
;
1718 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
);
1719 LISTVIEW_UpdateHeaderSize(infoPtr
, horzInfo
.nPos
);
1726 * Shows/hides the focus rectangle.
1729 * [I] infoPtr : valid pointer to the listview structure
1730 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1735 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO
*infoPtr
, BOOL fShow
)
1737 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1740 TRACE("fShow=%d, nItem=%d\n", fShow
, infoPtr
->nFocusedItem
);
1742 if (infoPtr
->nFocusedItem
< 0) return;
1744 /* we need some gymnastics in ICON mode to handle large items */
1745 if ( (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
1749 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcBox
);
1750 if ((rcBox
.bottom
- rcBox
.top
) > infoPtr
->nItemHeight
)
1752 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1757 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) return;
1759 /* for some reason, owner draw should work only in report mode */
1760 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
1765 item
.iItem
= infoPtr
->nFocusedItem
;
1767 item
.mask
= LVIF_PARAM
;
1768 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto done
;
1770 ZeroMemory(&dis
, sizeof(dis
));
1771 dis
.CtlType
= ODT_LISTVIEW
;
1772 dis
.CtlID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1773 dis
.itemID
= item
.iItem
;
1774 dis
.itemAction
= ODA_FOCUS
;
1775 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
1776 dis
.hwndItem
= infoPtr
->hwndSelf
;
1778 LISTVIEW_GetItemBox(infoPtr
, dis
.itemID
, &dis
.rcItem
);
1779 dis
.itemData
= item
.lParam
;
1781 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
1785 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
1788 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1792 * Invalidates all visible selected items.
1794 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO
*infoPtr
)
1798 iterator_frameditems(&i
, infoPtr
, &infoPtr
->rcList
);
1799 while(iterator_next(&i
))
1801 if (LISTVIEW_GetItemState(infoPtr
, i
.nItem
, LVIS_SELECTED
))
1802 LISTVIEW_InvalidateItem(infoPtr
, i
.nItem
);
1804 iterator_destroy(&i
);
1809 * DESCRIPTION: [INTERNAL]
1810 * Computes an item's (left,top) corner, relative to rcView.
1811 * That is, the position has NOT been made relative to the Origin.
1812 * This is deliberate, to avoid computing the Origin over, and
1813 * over again, when this function is call in a loop. Instead,
1814 * one ca factor the computation of the Origin before the loop,
1815 * and offset the value retured by this function, on every iteration.
1818 * [I] infoPtr : valid pointer to the listview structure
1819 * [I] nItem : item number
1820 * [O] lpptOrig : item top, left corner
1825 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
1827 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1829 assert(nItem
>= 0 && nItem
< infoPtr
->nItemCount
);
1831 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1833 lpptPosition
->x
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1834 lpptPosition
->y
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1836 else if (uView
== LVS_LIST
)
1838 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
1839 lpptPosition
->x
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
1840 lpptPosition
->y
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
1842 else /* LVS_REPORT */
1844 lpptPosition
->x
= 0;
1845 lpptPosition
->y
= nItem
* infoPtr
->nItemHeight
;
1850 * DESCRIPTION: [INTERNAL]
1851 * Compute the rectangles of an item. This is to localize all
1852 * the computations in one place. If you are not interested in some
1853 * of these values, simply pass in a NULL -- the fucntion is smart
1854 * enough to compute only what's necessary. The function computes
1855 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1856 * one, the BOX rectangle. This rectangle is very cheap to compute,
1857 * and is guaranteed to contain all the other rectangles. Computing
1858 * the ICON rect is also cheap, but all the others are potentaily
1859 * expensive. This gives an easy and effective optimization when
1860 * searching (like point inclusion, or rectangle intersection):
1861 * first test against the BOX, and if TRUE, test agains the desired
1863 * If the function does not have all the necessary information
1864 * to computed the requested rectangles, will crash with a
1865 * failed assertion. This is done so we catch all programming
1866 * errors, given that the function is called only from our code.
1868 * We have the following 'special' meanings for a few fields:
1869 * * If LVIS_FOCUSED is set, we assume the item has the focus
1870 * This is important in ICON mode, where it might get a larger
1871 * then usual rectange
1873 * Please note that subitem support works only in REPORT mode.
1876 * [I] infoPtr : valid pointer to the listview structure
1877 * [I] lpLVItem : item to compute the measures for
1878 * [O] lprcBox : ptr to Box rectangle
1879 * The internal LVIR_BOX rectangle
1880 * [0] lprcState : ptr to State icon rectangle
1881 * The internal LVIR_STATE rectangle
1882 * [O] lprcIcon : ptr to Icon rectangle
1883 * Same as LVM_GETITEMRECT with LVIR_ICON
1884 * [O] lprcLabel : ptr to Label rectangle
1885 * Same as LVM_GETITEMRECT with LVIR_LABEL
1890 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
,
1891 LPRECT lprcBox
, LPRECT lprcState
,
1892 LPRECT lprcIcon
, LPRECT lprcLabel
)
1894 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1895 BOOL doState
= FALSE
, doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
1896 RECT Box
, State
, Icon
, Label
;
1897 COLUMN_INFO
*lpColumnInfo
= NULL
;
1899 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem
, TRUE
));
1901 /* Be smart and try to figure out the minimum we have to do */
1902 if (lpLVItem
->iSubItem
) assert(uView
== LVS_REPORT
);
1903 if (uView
== LVS_ICON
&& (lprcBox
|| lprcLabel
))
1905 assert((lpLVItem
->mask
& LVIF_STATE
) && (lpLVItem
->stateMask
& LVIS_FOCUSED
));
1906 if (lpLVItem
->state
& LVIS_FOCUSED
) oversizedBox
= doLabel
= TRUE
;
1908 if (lprcLabel
) doLabel
= TRUE
;
1909 if (doLabel
|| lprcIcon
) doIcon
= TRUE
;
1910 if (doIcon
|| lprcState
) doState
= TRUE
;
1912 /************************************************************/
1913 /* compute the box rectangle (it should be cheap to do) */
1914 /************************************************************/
1915 if (lpLVItem
->iSubItem
|| uView
== LVS_REPORT
)
1916 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpLVItem
->iSubItem
);
1918 if (lpLVItem
->iSubItem
)
1920 Box
= lpColumnInfo
->rcHeader
;
1925 Box
.right
= infoPtr
->nItemWidth
;
1928 Box
.bottom
= infoPtr
->nItemHeight
;
1930 /************************************************************/
1931 /* compute STATEICON bounding box */
1932 /************************************************************/
1935 if (uView
== LVS_ICON
)
1937 State
.left
= Box
.left
- infoPtr
->iconStateSize
.cx
- 2;
1938 if (infoPtr
->himlNormal
)
1939 State
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
1940 State
.top
= Box
.top
+ infoPtr
->iconSize
.cy
- infoPtr
->iconStateSize
.cy
+ 4;
1944 /* we need the ident in report mode, if we don't have it, we fail */
1945 State
.left
= Box
.left
;
1946 if (uView
== LVS_REPORT
)
1948 if (lpLVItem
->iSubItem
== 0)
1950 State
.left
+= REPORT_MARGINX
;
1951 assert(lpLVItem
->mask
& LVIF_INDENT
);
1952 State
.left
+= infoPtr
->iconSize
.cx
* lpLVItem
->iIndent
;
1955 State
.top
= Box
.top
;
1957 State
.right
= State
.left
;
1958 State
.bottom
= State
.top
;
1959 if (infoPtr
->himlState
&& lpLVItem
->iSubItem
== 0)
1961 State
.right
+= infoPtr
->iconStateSize
.cx
;
1962 State
.bottom
+= infoPtr
->iconStateSize
.cy
;
1964 if (lprcState
) *lprcState
= State
;
1965 TRACE(" - state=%s\n", debugrect(&State
));
1968 /************************************************************/
1969 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1970 /************************************************************/
1973 if (uView
== LVS_ICON
)
1975 Icon
.left
= Box
.left
;
1976 if (infoPtr
->himlNormal
)
1977 Icon
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
1978 Icon
.top
= Box
.top
+ ICON_TOP_PADDING
;
1979 Icon
.right
= Icon
.left
;
1980 Icon
.bottom
= Icon
.top
;
1981 if (infoPtr
->himlNormal
)
1983 Icon
.right
+= infoPtr
->iconSize
.cx
;
1984 Icon
.bottom
+= infoPtr
->iconSize
.cy
;
1987 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1989 Icon
.left
= State
.right
;
1991 Icon
.right
= Icon
.left
;
1992 if (infoPtr
->himlSmall
&&
1993 (!lpColumnInfo
|| lpLVItem
->iSubItem
== 0 || (lpColumnInfo
->fmt
& LVCFMT_IMAGE
) ||
1994 ((infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
) && lpLVItem
->iImage
!= I_IMAGECALLBACK
)))
1995 Icon
.right
+= infoPtr
->iconSize
.cx
;
1996 Icon
.bottom
= Icon
.top
+ infoPtr
->nItemHeight
;
1998 if(lprcIcon
) *lprcIcon
= Icon
;
1999 TRACE(" - icon=%s\n", debugrect(&Icon
));
2002 /************************************************************/
2003 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2004 /************************************************************/
2007 SIZE labelSize
= { 0, 0 };
2009 /* calculate how far to the right can the label strech */
2010 Label
.right
= Box
.right
;
2011 if (uView
== LVS_REPORT
)
2013 if (lpLVItem
->iSubItem
== 0) Label
= lpColumnInfo
->rcHeader
;
2016 if (lpLVItem
->iSubItem
|| ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && uView
== LVS_REPORT
))
2018 labelSize
.cx
= infoPtr
->nItemWidth
;
2019 labelSize
.cy
= infoPtr
->nItemHeight
;
2023 /* we need the text in non owner draw mode */
2024 assert(lpLVItem
->mask
& LVIF_TEXT
);
2025 if (is_textT(lpLVItem
->pszText
, TRUE
))
2027 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2028 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2029 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2033 /* compute rough rectangle where the label will go */
2034 SetRectEmpty(&rcText
);
2035 rcText
.right
= infoPtr
->nItemWidth
- TRAILING_LABEL_PADDING
;
2036 rcText
.bottom
= infoPtr
->nItemHeight
;
2037 if (uView
== LVS_ICON
)
2038 rcText
.bottom
-= ICON_TOP_PADDING
+ infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2040 /* now figure out the flags */
2041 if (uView
== LVS_ICON
)
2042 uFormat
= oversizedBox
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
;
2044 uFormat
= LV_SL_DT_FLAGS
;
2046 DrawTextW (hdc
, lpLVItem
->pszText
, -1, &rcText
, uFormat
| DT_CALCRECT
);
2048 labelSize
.cx
= min(rcText
.right
- rcText
.left
+ TRAILING_LABEL_PADDING
, infoPtr
->nItemWidth
);
2049 labelSize
.cy
= rcText
.bottom
- rcText
.top
;
2051 SelectObject(hdc
, hOldFont
);
2052 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2056 if (uView
== LVS_ICON
)
2058 Label
.left
= Box
.left
+ (infoPtr
->nItemWidth
- labelSize
.cx
) / 2;
2059 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
2060 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2061 Label
.right
= Label
.left
+ labelSize
.cx
;
2062 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2063 if (!oversizedBox
&& labelSize
.cy
> infoPtr
->ntmHeight
)
2065 labelSize
.cy
= min(Box
.bottom
- Label
.top
, labelSize
.cy
);
2066 labelSize
.cy
/= infoPtr
->ntmHeight
;
2067 labelSize
.cy
= max(labelSize
.cy
, 1);
2068 labelSize
.cy
*= infoPtr
->ntmHeight
;
2070 Label
.bottom
= Label
.top
+ labelSize
.cy
+ HEIGHT_PADDING
;
2072 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2074 Label
.left
= Icon
.right
;
2075 Label
.top
= Box
.top
;
2076 Label
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2077 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2080 if (lprcLabel
) *lprcLabel
= Label
;
2081 TRACE(" - label=%s\n", debugrect(&Label
));
2084 /* Fix the Box if necessary */
2087 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
2088 else *lprcBox
= Box
;
2090 TRACE(" - box=%s\n", debugrect(&Box
));
2094 * DESCRIPTION: [INTERNAL]
2097 * [I] infoPtr : valid pointer to the listview structure
2098 * [I] nItem : item number
2099 * [O] lprcBox : ptr to Box rectangle
2104 static void LISTVIEW_GetItemBox(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprcBox
)
2106 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2107 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2108 POINT Position
, Origin
;
2111 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
2112 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
2114 /* Be smart and try to figure out the minimum we have to do */
2116 if (uView
== LVS_ICON
&& infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
2117 lvItem
.mask
|= LVIF_TEXT
;
2118 lvItem
.iItem
= nItem
;
2119 lvItem
.iSubItem
= 0;
2120 lvItem
.pszText
= szDispText
;
2121 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2122 if (lvItem
.mask
) LISTVIEW_GetItemW(infoPtr
, &lvItem
);
2123 if (uView
== LVS_ICON
)
2125 lvItem
.mask
|= LVIF_STATE
;
2126 lvItem
.stateMask
= LVIS_FOCUSED
;
2127 lvItem
.state
= (lvItem
.mask
& LVIF_TEXT
? LVIS_FOCUSED
: 0);
2129 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprcBox
, 0, 0, 0);
2131 OffsetRect(lprcBox
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
2137 * Returns the current icon position, and advances it along the top.
2138 * The returned position is not offset by Origin.
2141 * [I] infoPtr : valid pointer to the listview structure
2142 * [O] lpPos : will get the current icon position
2147 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2149 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2151 *lpPos
= infoPtr
->currIconPos
;
2153 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2154 if (infoPtr
->currIconPos
.x
+ infoPtr
->nItemWidth
<= nListWidth
) return;
2156 infoPtr
->currIconPos
.x
= 0;
2157 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2163 * Returns the current icon position, and advances it down the left edge.
2164 * The returned position is not offset by Origin.
2167 * [I] infoPtr : valid pointer to the listview structure
2168 * [O] lpPos : will get the current icon position
2173 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2175 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2177 *lpPos
= infoPtr
->currIconPos
;
2179 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2180 if (infoPtr
->currIconPos
.y
+ infoPtr
->nItemHeight
<= nListHeight
) return;
2182 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2183 infoPtr
->currIconPos
.y
= 0;
2189 * Moves an icon to the specified position.
2190 * It takes care of invalidating the item, etc.
2193 * [I] infoPtr : valid pointer to the listview structure
2194 * [I] nItem : the item to move
2195 * [I] lpPos : the new icon position
2196 * [I] isNew : flags the item as being new
2202 static BOOL
LISTVIEW_MoveIconTo(LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*lppt
, BOOL isNew
)
2208 old
.x
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2209 old
.y
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2211 if (lppt
->x
== old
.x
&& lppt
->y
== old
.y
) return TRUE
;
2212 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2215 /* Allocating a POINTER for every item is too resource intensive,
2216 * so we'll keep the (x,y) in different arrays */
2217 if (!DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)lppt
->x
)) return FALSE
;
2218 if (!DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)lppt
->y
)) return FALSE
;
2220 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2227 * Arranges listview items in icon display mode.
2230 * [I] infoPtr : valid pointer to the listview structure
2231 * [I] nAlignCode : alignment code
2237 static BOOL
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
2239 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2240 void (*next_pos
)(LISTVIEW_INFO
*, LPPOINT
);
2244 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
) return FALSE
;
2246 TRACE("nAlignCode=%d\n", nAlignCode
);
2248 if (nAlignCode
== LVA_DEFAULT
)
2250 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
) nAlignCode
= LVA_ALIGNLEFT
;
2251 else nAlignCode
= LVA_ALIGNTOP
;
2256 case LVA_ALIGNLEFT
: next_pos
= LISTVIEW_NextIconPosLeft
; break;
2257 case LVA_ALIGNTOP
: next_pos
= LISTVIEW_NextIconPosTop
; break;
2258 case LVA_SNAPTOGRID
: next_pos
= LISTVIEW_NextIconPosTop
; break; /* FIXME */
2259 default: return FALSE
;
2262 infoPtr
->bAutoarrange
= TRUE
;
2263 infoPtr
->currIconPos
.x
= infoPtr
->currIconPos
.y
= 0;
2264 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2266 next_pos(infoPtr
, &pos
);
2267 LISTVIEW_MoveIconTo(infoPtr
, i
, &pos
, FALSE
);
2275 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2278 * [I] infoPtr : valid pointer to the listview structure
2279 * [O] lprcView : bounding rectangle
2285 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2289 SetRectEmpty(lprcView
);
2291 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
2295 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2297 x
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosX
, i
);
2298 y
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosY
, i
);
2299 lprcView
->right
= max(lprcView
->right
, x
);
2300 lprcView
->bottom
= max(lprcView
->bottom
, y
);
2302 if (infoPtr
->nItemCount
> 0)
2304 lprcView
->right
+= infoPtr
->nItemWidth
;
2305 lprcView
->bottom
+= infoPtr
->nItemHeight
;
2310 y
= LISTVIEW_GetCountPerColumn(infoPtr
);
2311 x
= infoPtr
->nItemCount
/ y
;
2312 if (infoPtr
->nItemCount
% y
) x
++;
2313 lprcView
->right
= x
* infoPtr
->nItemWidth
;
2314 lprcView
->bottom
= y
* infoPtr
->nItemHeight
;
2318 lprcView
->right
= infoPtr
->nItemWidth
;
2319 lprcView
->bottom
= infoPtr
->nItemCount
* infoPtr
->nItemHeight
;
2326 * Retrieves the bounding rectangle of all the items.
2329 * [I] infoPtr : valid pointer to the listview structure
2330 * [O] lprcView : bounding rectangle
2336 static BOOL
LISTVIEW_GetViewRect(LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2340 TRACE("(lprcView=%p)\n", lprcView
);
2342 if (!lprcView
) return FALSE
;
2344 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
2345 LISTVIEW_GetAreaRect(infoPtr
, lprcView
);
2346 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
2348 TRACE("lprcView=%s\n", debugrect(lprcView
));
2355 * Retrieves the subitem pointer associated with the subitem index.
2358 * [I] hdpaSubItems : DPA handle for a specific item
2359 * [I] nSubItem : index of subitem
2362 * SUCCESS : subitem pointer
2365 static SUBITEM_INFO
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
, INT nSubItem
)
2367 SUBITEM_INFO
*lpSubItem
;
2370 /* we should binary search here if need be */
2371 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
2373 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
2374 if (lpSubItem
->iSubItem
== nSubItem
)
2384 * Caclulates the desired item width.
2387 * [I] infoPtr : valid pointer to the listview structure
2390 * The desired item width.
2392 static INT
LISTVIEW_CalculateItemWidth(LISTVIEW_INFO
*infoPtr
)
2394 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2397 TRACE("uView=%d\n", uView
);
2399 if (uView
== LVS_ICON
)
2400 nItemWidth
= infoPtr
->iconSpacing
.cx
;
2401 else if (uView
== LVS_REPORT
)
2405 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2407 LISTVIEW_GetHeaderRect(infoPtr
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, &rcHeader
);
2408 nItemWidth
= rcHeader
.right
;
2411 else /* LVS_SMALLICON, or LVS_LIST */
2415 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2416 nItemWidth
= max(LISTVIEW_GetLabelWidth(infoPtr
, i
), nItemWidth
);
2418 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
2419 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
2421 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
+ WIDTH_PADDING
);
2424 return max(nItemWidth
, 1);
2429 * Caclulates the desired item height.
2432 * [I] infoPtr : valid pointer to the listview structure
2435 * The desired item height.
2437 static INT
LISTVIEW_CalculateItemHeight(LISTVIEW_INFO
*infoPtr
)
2439 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2442 TRACE("uView=%d\n", uView
);
2444 if (uView
== LVS_ICON
)
2445 nItemHeight
= infoPtr
->iconSpacing
.cy
;
2448 nItemHeight
= infoPtr
->ntmHeight
;
2449 if (infoPtr
->himlState
)
2450 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
2451 if (infoPtr
->himlSmall
)
2452 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
2453 if (infoPtr
->himlState
|| infoPtr
->himlSmall
)
2454 nItemHeight
+= HEIGHT_PADDING
;
2457 return max(nItemHeight
, 1);
2462 * Updates the width, and height of an item.
2465 * [I] infoPtr : valid pointer to the listview structure
2470 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO
*infoPtr
)
2472 infoPtr
->nItemWidth
= LISTVIEW_CalculateItemWidth(infoPtr
);
2473 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
2479 * Retrieves and saves important text metrics info for the current
2483 * [I] infoPtr : valid pointer to the listview structure
2486 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
2488 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2489 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2490 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2493 if (GetTextMetricsW(hdc
, &tm
))
2495 infoPtr
->ntmHeight
= tm
.tmHeight
;
2496 infoPtr
->ntmAveCharWidth
= tm
.tmAveCharWidth
;
2498 SelectObject(hdc
, hOldFont
);
2499 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2501 TRACE("tmHeight=%d\n", infoPtr
->ntmHeight
);
2506 * A compare function for ranges
2509 * [I] range1 : pointer to range 1;
2510 * [I] range2 : pointer to range 2;
2514 * > 0 : if range 1 > range 2
2515 * < 0 : if range 2 > range 1
2516 * = 0 : if range intersects range 2
2518 static INT CALLBACK
ranges_cmp(LPVOID range1
, LPVOID range2
, LPARAM flags
)
2522 if (((RANGE
*)range1
)->upper
<= ((RANGE
*)range2
)->lower
)
2524 else if (((RANGE
*)range2
)->upper
<= ((RANGE
*)range1
)->lower
)
2529 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE
*)range1
), debugrange((RANGE
*)range2
), cmp
);
2535 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2537 #define ranges_check(ranges, desc) do { } while(0)
2540 static void ranges_assert(RANGES ranges
, LPCSTR desc
, const char *func
, int line
)
2545 TRACE("*** Checking %s:%d:%s ***\n", func
, line
, desc
);
2547 assert (DPA_GetPtrCount(ranges
->hdpa
) >= 0);
2548 ranges_dump(ranges
);
2549 prev
= (RANGE
*)DPA_GetPtr(ranges
->hdpa
, 0);
2550 if (DPA_GetPtrCount(ranges
->hdpa
) > 0)
2551 assert (prev
->lower
>= 0 && prev
->lower
< prev
->upper
);
2552 for (i
= 1; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2554 curr
= (RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
);
2555 assert (prev
->upper
<= curr
->lower
);
2556 assert (curr
->lower
< curr
->upper
);
2559 TRACE("--- Done checking---\n");
2562 static RANGES
ranges_create(int count
)
2564 RANGES ranges
= (RANGES
)Alloc(sizeof(struct tagRANGES
));
2565 if (!ranges
) return NULL
;
2566 ranges
->hdpa
= DPA_Create(count
);
2567 if (ranges
->hdpa
) return ranges
;
2572 static void ranges_clear(RANGES ranges
)
2576 for(i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2577 Free(DPA_GetPtr(ranges
->hdpa
, i
));
2578 DPA_DeleteAllPtrs(ranges
->hdpa
);
2582 static void ranges_destroy(RANGES ranges
)
2584 if (!ranges
) return;
2585 ranges_clear(ranges
);
2586 DPA_Destroy(ranges
->hdpa
);
2590 static RANGES
ranges_clone(RANGES ranges
)
2595 if (!(clone
= ranges_create(DPA_GetPtrCount(ranges
->hdpa
)))) goto fail
;
2597 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2599 RANGE
*newrng
= (RANGE
*)Alloc(sizeof(RANGE
));
2600 if (!newrng
) goto fail
;
2601 *newrng
= *((RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
));
2602 DPA_SetPtr(clone
->hdpa
, i
, newrng
);
2607 TRACE ("clone failed\n");
2608 ranges_destroy(clone
);
2612 static RANGES
ranges_diff(RANGES ranges
, RANGES sub
)
2616 for (i
= 0; i
< DPA_GetPtrCount(sub
->hdpa
); i
++)
2617 ranges_del(ranges
, *((RANGE
*)DPA_GetPtr(sub
->hdpa
, i
)));
2622 static void ranges_dump(RANGES ranges
)
2626 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2627 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges
->hdpa
, i
)));
2630 static inline BOOL
ranges_contain(RANGES ranges
, INT nItem
)
2632 RANGE srchrng
= { nItem
, nItem
+ 1 };
2634 TRACE("(nItem=%d)\n", nItem
);
2635 ranges_check(ranges
, "before contain");
2636 return DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
) != -1;
2639 static INT
ranges_itemcount(RANGES ranges
)
2643 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2645 RANGE
*sel
= DPA_GetPtr(ranges
->hdpa
, i
);
2646 count
+= sel
->upper
- sel
->lower
;
2652 static BOOL
ranges_shift(RANGES ranges
, INT nItem
, INT delta
, INT nUpper
)
2654 RANGE srchrng
= { nItem
, nItem
+ 1 }, *chkrng
;
2657 index
= DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2658 if (index
== -1) return TRUE
;
2660 for (; index
< DPA_GetPtrCount(ranges
->hdpa
); index
++)
2662 chkrng
= DPA_GetPtr(ranges
->hdpa
, index
);
2663 if (chkrng
->lower
>= nItem
)
2664 chkrng
->lower
= max(min(chkrng
->lower
+ delta
, nUpper
- 1), 0);
2665 if (chkrng
->upper
> nItem
)
2666 chkrng
->upper
= max(min(chkrng
->upper
+ delta
, nUpper
), 0);
2671 static BOOL
ranges_add(RANGES ranges
, RANGE range
)
2676 TRACE("(%s)\n", debugrange(&range
));
2677 ranges_check(ranges
, "before add");
2679 /* try find overlapping regions first */
2680 srchrgn
.lower
= range
.lower
- 1;
2681 srchrgn
.upper
= range
.upper
+ 1;
2682 index
= DPA_Search(ranges
->hdpa
, &srchrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
);
2688 TRACE("Adding new range\n");
2690 /* create the brand new range to insert */
2691 newrgn
= (RANGE
*)Alloc(sizeof(RANGE
));
2692 if(!newrgn
) goto fail
;
2695 /* figure out where to insert it */
2696 index
= DPA_Search(ranges
->hdpa
, newrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2697 TRACE("index=%d\n", index
);
2698 if (index
== -1) index
= 0;
2700 /* and get it over with */
2701 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2709 RANGE
*chkrgn
, *mrgrgn
;
2710 INT fromindex
, mergeindex
;
2712 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2713 TRACE("Merge with %s @%d\n", debugrange(chkrgn
), index
);
2715 chkrgn
->lower
= min(range
.lower
, chkrgn
->lower
);
2716 chkrgn
->upper
= max(range
.upper
, chkrgn
->upper
);
2718 TRACE("New range %s @%d\n", debugrange(chkrgn
), index
);
2720 /* merge now common anges */
2722 srchrgn
.lower
= chkrgn
->lower
- 1;
2723 srchrgn
.upper
= chkrgn
->upper
+ 1;
2727 mergeindex
= DPA_Search(ranges
->hdpa
, &srchrgn
, fromindex
, ranges_cmp
, 0, 0);
2728 if (mergeindex
== -1) break;
2729 if (mergeindex
== index
)
2731 fromindex
= index
+ 1;
2735 TRACE("Merge with index %i\n", mergeindex
);
2737 mrgrgn
= DPA_GetPtr(ranges
->hdpa
, mergeindex
);
2738 chkrgn
->lower
= min(chkrgn
->lower
, mrgrgn
->lower
);
2739 chkrgn
->upper
= max(chkrgn
->upper
, mrgrgn
->upper
);
2741 DPA_DeletePtr(ranges
->hdpa
, mergeindex
);
2742 if (mergeindex
< index
) index
--;
2746 ranges_check(ranges
, "after add");
2750 ranges_check(ranges
, "failed add");
2754 static BOOL
ranges_del(RANGES ranges
, RANGE range
)
2759 TRACE("(%s)\n", debugrange(&range
));
2760 ranges_check(ranges
, "before del");
2762 /* we don't use DPAS_SORTED here, since we need *
2763 * to find the first overlapping range */
2764 index
= DPA_Search(ranges
->hdpa
, &range
, 0, ranges_cmp
, 0, 0);
2767 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2769 TRACE("Matches range %s @%d\n", debugrange(chkrgn
), index
);
2771 /* case 1: Same range */
2772 if ( (chkrgn
->upper
== range
.upper
) &&
2773 (chkrgn
->lower
== range
.lower
) )
2775 DPA_DeletePtr(ranges
->hdpa
, index
);
2778 /* case 2: engulf */
2779 else if ( (chkrgn
->upper
<= range
.upper
) &&
2780 (chkrgn
->lower
>= range
.lower
) )
2782 DPA_DeletePtr(ranges
->hdpa
, index
);
2784 /* case 3: overlap upper */
2785 else if ( (chkrgn
->upper
<= range
.upper
) &&
2786 (chkrgn
->lower
< range
.lower
) )
2788 chkrgn
->upper
= range
.lower
;
2790 /* case 4: overlap lower */
2791 else if ( (chkrgn
->upper
> range
.upper
) &&
2792 (chkrgn
->lower
>= range
.lower
) )
2794 chkrgn
->lower
= range
.upper
;
2797 /* case 5: fully internal */
2800 RANGE tmprgn
= *chkrgn
, *newrgn
;
2802 if (!(newrgn
= (RANGE
*)Alloc(sizeof(RANGE
)))) goto fail
;
2803 newrgn
->lower
= chkrgn
->lower
;
2804 newrgn
->upper
= range
.lower
;
2805 chkrgn
->lower
= range
.upper
;
2806 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2815 index
= DPA_Search(ranges
->hdpa
, &range
, index
, ranges_cmp
, 0, 0);
2818 ranges_check(ranges
, "after del");
2822 ranges_check(ranges
, "failed del");
2828 * Removes all selection ranges
2831 * [I] infoPtr : valid pointer to the listview structure
2832 * [I] toSkip : item range to skip removing the selection
2838 static BOOL
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO
*infoPtr
, RANGES toSkip
)
2847 lvItem
.stateMask
= LVIS_SELECTED
;
2849 /* need to clone the DPA because callbacks can change it */
2850 if (!(clone
= ranges_clone(infoPtr
->selectionRanges
))) return FALSE
;
2851 iterator_rangesitems(&i
, ranges_diff(clone
, toSkip
));
2852 while(iterator_next(&i
))
2853 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &lvItem
);
2854 /* note that the iterator destructor will free the cloned range */
2855 iterator_destroy(&i
);
2860 static inline BOOL
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2864 if (!(toSkip
= ranges_create(1))) return FALSE
;
2865 if (nItem
!= -1) ranges_additem(toSkip
, nItem
);
2866 LISTVIEW_DeselectAllSkipItems(infoPtr
, toSkip
);
2867 ranges_destroy(toSkip
);
2871 static inline BOOL
LISTVIEW_DeselectAll(LISTVIEW_INFO
*infoPtr
)
2873 return LISTVIEW_DeselectAllSkipItem(infoPtr
, -1);
2878 * Retrieves the number of items that are marked as selected.
2881 * [I] infoPtr : valid pointer to the listview structure
2884 * Number of items selected.
2886 static INT
LISTVIEW_GetSelectedCount(LISTVIEW_INFO
*infoPtr
)
2888 INT nSelectedCount
= 0;
2890 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
2893 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2895 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
2900 nSelectedCount
= ranges_itemcount(infoPtr
->selectionRanges
);
2902 TRACE("nSelectedCount=%d\n", nSelectedCount
);
2903 return nSelectedCount
;
2908 * Manages the item focus.
2911 * [I] infoPtr : valid pointer to the listview structure
2912 * [I] nItem : item index
2915 * TRUE : focused item changed
2916 * FALSE : focused item has NOT changed
2918 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2920 INT oldFocus
= infoPtr
->nFocusedItem
;
2923 if (nItem
== infoPtr
->nFocusedItem
) return FALSE
;
2925 lvItem
.state
= nItem
== -1 ? 0 : LVIS_FOCUSED
;
2926 lvItem
.stateMask
= LVIS_FOCUSED
;
2927 LISTVIEW_SetItemState(infoPtr
, nItem
== -1 ? infoPtr
->nFocusedItem
: nItem
, &lvItem
);
2929 return oldFocus
!= infoPtr
->nFocusedItem
;
2932 /* Helper function for LISTVIEW_ShiftIndices *only* */
2933 static INT
shift_item(LISTVIEW_INFO
*infoPtr
, INT nShiftItem
, INT nItem
, INT direction
)
2935 if (nShiftItem
< nItem
) return nShiftItem
;
2937 if (nShiftItem
> nItem
) return nShiftItem
+ direction
;
2939 if (direction
> 0) return nShiftItem
+ direction
;
2941 return min(nShiftItem
, infoPtr
->nItemCount
- 1);
2946 * Updates the various indices after an item has been inserted or deleted.
2949 * [I] infoPtr : valid pointer to the listview structure
2950 * [I] nItem : item index
2951 * [I] direction : Direction of shift, +1 or -1.
2956 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
2961 /* temporarily disable change notification while shifting items */
2962 bOldChange
= infoPtr
->bDoChangeNotify
;
2963 infoPtr
->bDoChangeNotify
= FALSE
;
2965 TRACE("Shifting %iu, %i steps\n", nItem
, direction
);
2967 ranges_shift(infoPtr
->selectionRanges
, nItem
, direction
, infoPtr
->nItemCount
);
2969 assert(abs(direction
) == 1);
2971 infoPtr
->nSelectionMark
= shift_item(infoPtr
, infoPtr
->nSelectionMark
, nItem
, direction
);
2973 nNewFocus
= shift_item(infoPtr
, infoPtr
->nFocusedItem
, nItem
, direction
);
2974 if (nNewFocus
!= infoPtr
->nFocusedItem
)
2975 LISTVIEW_SetItemFocus(infoPtr
, nNewFocus
);
2977 /* But we are not supposed to modify nHotItem! */
2979 infoPtr
->bDoChangeNotify
= bOldChange
;
2985 * Adds a block of selections.
2988 * [I] infoPtr : valid pointer to the listview structure
2989 * [I] nItem : item index
2994 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2996 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
2997 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
2998 NMLVODSTATECHANGE nmlv
;
3003 /* Temporarily disable change notification
3004 * If the control is LVS_OWNERDATA, we need to send
3005 * only one LVN_ODSTATECHANGED notification.
3006 * See MSDN documentation for LVN_ITEMCHANGED.
3008 bOldChange
= infoPtr
->bDoChangeNotify
;
3009 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3011 if (nFirst
== -1) nFirst
= nItem
;
3013 item
.state
= LVIS_SELECTED
;
3014 item
.stateMask
= LVIS_SELECTED
;
3016 for (i
= nFirst
; i
<= nLast
; i
++)
3017 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
3019 ZeroMemory(&nmlv
, sizeof(nmlv
));
3020 nmlv
.iFrom
= nFirst
;
3023 nmlv
.uOldState
= item
.state
;
3025 notify_hdr(infoPtr
, LVN_ODSTATECHANGED
, (LPNMHDR
)&nmlv
);
3026 infoPtr
->bDoChangeNotify
= bOldChange
;
3032 * Sets a single group selection.
3035 * [I] infoPtr : valid pointer to the listview structure
3036 * [I] nItem : item index
3041 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3043 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3048 if (!(selection
= ranges_create(100))) return;
3050 item
.state
= LVIS_SELECTED
;
3051 item
.stateMask
= LVIS_SELECTED
;
3053 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
3055 if (infoPtr
->nSelectionMark
== -1)
3057 infoPtr
->nSelectionMark
= nItem
;
3058 ranges_additem(selection
, nItem
);
3064 sel
.lower
= min(infoPtr
->nSelectionMark
, nItem
);
3065 sel
.upper
= max(infoPtr
->nSelectionMark
, nItem
) + 1;
3066 ranges_add(selection
, sel
);
3071 RECT rcItem
, rcSel
, rcSelMark
;
3074 rcItem
.left
= LVIR_BOUNDS
;
3075 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return;
3076 rcSelMark
.left
= LVIR_BOUNDS
;
3077 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) return;
3078 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
3079 iterator_frameditems(&i
, infoPtr
, &rcSel
);
3080 while(iterator_next(&i
))
3082 LISTVIEW_GetItemPosition(infoPtr
, i
.nItem
, &ptItem
);
3083 if (PtInRect(&rcSel
, ptItem
)) ranges_additem(selection
, i
.nItem
);
3085 iterator_destroy(&i
);
3088 LISTVIEW_DeselectAllSkipItems(infoPtr
, selection
);
3089 iterator_rangesitems(&i
, selection
);
3090 while(iterator_next(&i
))
3091 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &item
);
3092 /* this will also destroy the selection */
3093 iterator_destroy(&i
);
3095 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3100 * Sets a single selection.
3103 * [I] infoPtr : valid pointer to the listview structure
3104 * [I] nItem : item index
3109 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3113 TRACE("nItem=%d\n", nItem
);
3115 LISTVIEW_DeselectAllSkipItem(infoPtr
, nItem
);
3117 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3118 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3119 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3121 infoPtr
->nSelectionMark
= nItem
;
3126 * Set selection(s) with keyboard.
3129 * [I] infoPtr : valid pointer to the listview structure
3130 * [I] nItem : item index
3133 * SUCCESS : TRUE (needs to be repainted)
3134 * FAILURE : FALSE (nothing has changed)
3136 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3138 /* FIXME: pass in the state */
3139 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
3140 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
3141 BOOL bResult
= FALSE
;
3143 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3145 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
3148 LISTVIEW_SetSelection(infoPtr
, nItem
);
3155 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3159 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3164 LISTVIEW_SetSelection(infoPtr
, nItem
);
3167 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3170 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3174 static BOOL
LISTVIEW_GetItemAtPt(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, POINT pt
)
3176 LVHITTESTINFO lvHitTestInfo
;
3178 ZeroMemory(&lvHitTestInfo
, sizeof(lvHitTestInfo
));
3179 lvHitTestInfo
.pt
.x
= pt
.x
;
3180 lvHitTestInfo
.pt
.y
= pt
.y
;
3182 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
3184 lpLVItem
->mask
= LVIF_PARAM
;
3185 lpLVItem
->iItem
= lvHitTestInfo
.iItem
;
3186 lpLVItem
->iSubItem
= 0;
3188 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
3193 * Called when the mouse is being actively tracked and has hovered for a specified
3197 * [I] infoPtr : valid pointer to the listview structure
3198 * [I] fwKeys : key indicator
3199 * [I] x,y : mouse position
3202 * 0 if the message was processed, non-zero if there was an error
3205 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3206 * over the item for a certain period of time.
3209 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, WORD fwKyes
, INT x
, INT y
)
3211 if (infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)
3219 if (LISTVIEW_GetItemAtPt(infoPtr
, &item
, pt
))
3220 LISTVIEW_SetSelection(infoPtr
, item
.iItem
);
3228 * Called whenever WM_MOUSEMOVE is received.
3231 * [I] infoPtr : valid pointer to the listview structure
3232 * [I] fwKeys : key indicator
3233 * [I] x,y : mouse position
3236 * 0 if the message is processed, non-zero if there was an error
3238 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3240 TRACKMOUSEEVENT trackinfo
;
3242 if (infoPtr
->bLButtonDown
&& DragDetect(infoPtr
->hwndSelf
, infoPtr
->ptClickPos
))
3244 LVHITTESTINFO lvHitTestInfo
;
3247 lvHitTestInfo
.pt
= infoPtr
->ptClickPos
;
3248 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
3250 ZeroMemory(&nmlv
, sizeof(nmlv
));
3251 nmlv
.iItem
= lvHitTestInfo
.iItem
;
3252 nmlv
.ptAction
= infoPtr
->ptClickPos
;
3254 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
3259 /* see if we are supposed to be tracking mouse hovering */
3260 if(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) {
3261 /* fill in the trackinfo struct */
3262 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3263 trackinfo
.dwFlags
= TME_QUERY
;
3264 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
3265 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
3267 /* see if we are already tracking this hwnd */
3268 _TrackMouseEvent(&trackinfo
);
3270 if(!(trackinfo
.dwFlags
& TME_HOVER
)) {
3271 trackinfo
.dwFlags
= TME_HOVER
;
3273 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3274 _TrackMouseEvent(&trackinfo
);
3283 * Tests wheather the item is assignable to a list with style lStyle
3285 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
3287 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
3288 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
3289 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
3297 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3300 * [I] infoPtr : valid pointer to the listview structure
3301 * [I] lpLVItem : valid pointer to new item atttributes
3302 * [I] isNew : the item being set is being inserted
3303 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3304 * [O] bChanged : will be set to TRUE if the item really changed
3310 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
3312 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3320 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
3322 if (lpLVItem
->mask
== 0) return TRUE
;
3324 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
3326 /* a virtual listview we stores only selection and focus */
3327 if (lpLVItem
->mask
& ~LVIF_STATE
)
3333 HDPA hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3334 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
3338 /* we need to get the lParam and state of the item */
3339 item
.iItem
= lpLVItem
->iItem
;
3340 item
.iSubItem
= lpLVItem
->iSubItem
;
3341 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
3342 item
.stateMask
= ~0;
3345 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
3347 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
3348 /* determine what fields will change */
3349 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
))
3350 uChanged
|= LVIF_STATE
;
3352 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
3353 uChanged
|= LVIF_IMAGE
;
3355 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
3356 uChanged
|= LVIF_PARAM
;
3358 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
3359 uChanged
|= LVIF_INDENT
;
3361 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
3362 uChanged
|= LVIF_TEXT
;
3364 TRACE("uChanged=0x%x\n", uChanged
);
3365 if (!uChanged
) return TRUE
;
3368 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
3369 nmlv
.iItem
= lpLVItem
->iItem
;
3370 nmlv
.uNewState
= (item
.state
& ~lpLVItem
->stateMask
) | (lpLVItem
->state
& lpLVItem
->stateMask
);
3371 nmlv
.uOldState
= item
.state
;
3372 nmlv
.uChanged
= uChanged
;
3373 nmlv
.lParam
= item
.lParam
;
3375 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3376 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3378 if(lpItem
&& !isNew
&& infoPtr
->bDoChangeNotify
&&
3379 notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
3382 /* copy information */
3383 if (lpLVItem
->mask
& LVIF_TEXT
)
3384 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3386 if (lpLVItem
->mask
& LVIF_IMAGE
)
3387 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
3389 if (lpLVItem
->mask
& LVIF_PARAM
)
3390 lpItem
->lParam
= lpLVItem
->lParam
;
3392 if (lpLVItem
->mask
& LVIF_INDENT
)
3393 lpItem
->iIndent
= lpLVItem
->iIndent
;
3395 if (uChanged
& LVIF_STATE
)
3397 if (lpItem
&& (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& ~(LVIS_FOCUSED
| LVIS_SELECTED
)))
3399 lpItem
->state
&= ~lpLVItem
->stateMask
;
3400 lpItem
->state
|= (lpLVItem
->state
& lpLVItem
->stateMask
);
3402 if (lpLVItem
->state
& lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3404 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
3405 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3407 else if (lpLVItem
->stateMask
& LVIS_SELECTED
)
3408 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3410 /* if we are asked to change focus, and we manage it, do it */
3411 if (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
3413 if (lpLVItem
->state
& LVIS_FOCUSED
)
3415 LISTVIEW_SetItemFocus(infoPtr
, -1);
3416 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
3417 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, uView
== LVS_LIST
);
3419 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
3420 infoPtr
->nFocusedItem
= -1;
3424 /* if we're inserting the item, we're done */
3425 if (isNew
) return TRUE
;
3427 /* send LVN_ITEMCHANGED notification */
3428 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
3429 if (infoPtr
->bDoChangeNotify
) notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
3436 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3439 * [I] infoPtr : valid pointer to the listview structure
3440 * [I] lpLVItem : valid pointer to new subitem atttributes
3441 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3442 * [O] bChanged : will be set to TRUE if the item really changed
3448 static BOOL
set_sub_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
3451 SUBITEM_INFO
*lpSubItem
;
3453 /* we do not support subitems for virtual listviews */
3454 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
3456 /* set subitem only if column is present */
3457 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
3459 /* First do some sanity checks */
3460 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
)) return FALSE
;
3461 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
))) return TRUE
;
3463 /* get the subitem structure, and create it if not there */
3464 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3465 assert (hdpaSubItems
);
3467 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
3470 SUBITEM_INFO
*tmpSubItem
;
3473 lpSubItem
= (SUBITEM_INFO
*)Alloc(sizeof(SUBITEM_INFO
));
3474 if (!lpSubItem
) return FALSE
;
3475 /* we could binary search here, if need be...*/
3476 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
3478 tmpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
3479 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
3481 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
3486 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
3487 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
3491 if (lpLVItem
->mask
& LVIF_IMAGE
)
3492 if (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
)
3494 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
3498 if (lpLVItem
->mask
& LVIF_TEXT
)
3499 if (lpSubItem
->hdr
.pszText
!= lpLVItem
->pszText
)
3501 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3510 * Sets item attributes.
3513 * [I] infoPtr : valid pointer to the listview structure
3514 * [I] lpLVItem : new item atttributes
3515 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3521 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
3523 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3524 LPWSTR pszText
= NULL
;
3525 BOOL bResult
, bChanged
= FALSE
;
3527 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
3529 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
3532 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3533 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_textW(lpLVItem
->pszText
))
3535 pszText
= lpLVItem
->pszText
;
3536 ((LVITEMW
*)lpLVItem
)->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
3539 /* actually set the fields */
3540 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
3542 if (lpLVItem
->iSubItem
)
3543 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
3545 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
3547 /* redraw item, if necessary */
3548 if (bChanged
&& !infoPtr
->bIsDrawing
)
3550 /* this little optimization eliminates some nasty flicker */
3551 if ( uView
== LVS_REPORT
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
3552 (!(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) || lpLVItem
->iSubItem
) )
3553 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
3555 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
3560 textfreeT(lpLVItem
->pszText
, isW
);
3561 ((LVITEMW
*)lpLVItem
)->pszText
= pszText
;
3569 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3572 * [I] infoPtr : valid pointer to the listview structure
3577 static INT
LISTVIEW_GetTopIndex(LISTVIEW_INFO
*infoPtr
)
3579 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3581 SCROLLINFO scrollInfo
;
3583 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3584 scrollInfo
.fMask
= SIF_POS
;
3586 if (uView
== LVS_LIST
)
3588 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
3589 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
3591 else if (uView
== LVS_REPORT
)
3593 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3594 nItem
= scrollInfo
.nPos
;
3598 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3599 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
3602 TRACE("nItem=%d\n", nItem
);
3610 * Erases the background of the given rectangle
3613 * [I] infoPtr : valid pointer to the listview structure
3614 * [I] hdc : device context handle
3615 * [I] lprcBox : clipping rectangle
3621 static inline BOOL
LISTVIEW_FillBkgnd(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
3623 if (!infoPtr
->hBkBrush
) return FALSE
;
3625 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, debugrect(lprcBox
), infoPtr
->hBkBrush
);
3627 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
3635 * [I] infoPtr : valid pointer to the listview structure
3636 * [I] hdc : device context handle
3637 * [I] nItem : item index
3638 * [I] nSubItem : subitem index
3639 * [I] pos : item position in client coordinates
3640 * [I] cdmode : custom draw mode
3646 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, INT nSubItem
, POINT pos
, DWORD cdmode
)
3648 UINT uFormat
, uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3649 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
3650 static const WCHAR szCallback
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3651 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
3652 RECT
* lprcFocus
, rcSelect
, rcBox
, rcState
, rcIcon
, rcLabel
;
3653 NMLVCUSTOMDRAW nmlvcd
;
3657 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc
, nItem
, nSubItem
, debugpoint(&pos
));
3659 /* get information needed for drawing the item */
3660 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
;
3661 if (nSubItem
== 0) lvItem
.mask
|= LVIF_STATE
| LVIF_PARAM
;
3662 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
3663 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
;
3664 lvItem
.iItem
= nItem
;
3665 lvItem
.iSubItem
= nSubItem
;
3668 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3669 lvItem
.pszText
= szDispText
;
3670 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
3671 if (nSubItem
> 0 && (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3672 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3673 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= (LPWSTR
)szCallback
;
3674 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3676 /* now check if we need to update the focus rectangle */
3677 lprcFocus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
3679 if (!lprcFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
3680 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcState
, &rcIcon
, &rcLabel
);
3681 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
3682 OffsetRect(&rcState
, pos
.x
, pos
.y
);
3683 OffsetRect(&rcIcon
, pos
.x
, pos
.y
);
3684 OffsetRect(&rcLabel
, pos
.x
, pos
.y
);
3685 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3686 debugrect(&rcBox
), debugrect(&rcState
), debugrect(&rcIcon
), debugrect(&rcLabel
));
3688 /* fill in the custom draw structure */
3689 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
3691 if (nSubItem
> 0) cdmode
= infoPtr
->cditemmode
;
3692 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3693 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3694 if (nSubItem
== 0) infoPtr
->cditemmode
= cdsubitemmode
;
3695 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3696 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3697 if (nSubItem
== 0 && cdsubitemmode
== CDRF_NOTIFYITEMDRAW
)
3699 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
3700 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3702 if (nSubItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
3703 prepaint_setup(infoPtr
, hdc
, &nmlvcd
);
3705 /* in full row select, subitems, will just use main item's colors */
3706 if (nSubItem
&& uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3707 nmlvcd
.clrTextBk
= CLR_NONE
;
3710 if (infoPtr
->himlState
&& !IsRectEmpty(&rcState
))
3712 UINT uStateImage
= (lvItem
.state
& LVIS_STATEIMAGEMASK
) >> 12;
3715 TRACE("uStateImage=%d\n", uStateImage
);
3716 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
, rcState
.left
, rcState
.top
, ILD_NORMAL
);
3721 himl
= (uView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
3722 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
))
3724 TRACE("iImage=%d\n", lvItem
.iImage
);
3725 ImageList_Draw(himl
, lvItem
.iImage
, hdc
, rcIcon
.left
, rcIcon
.top
,
3726 (lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
) ? ILD_SELECTED
: ILD_NORMAL
);
3729 /* Don't bother painting item being edited */
3730 if (infoPtr
->hwndEdit
&& nItem
== infoPtr
->nEditLabelItem
&& nSubItem
== 0) goto postpaint
;
3732 /* draw the selection background, if we're drawing the main item */
3736 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3737 rcSelect
.right
= rcBox
.right
;
3739 if (nmlvcd
.clrTextBk
!= CLR_NONE
)
3740 ExtTextOutW(hdc
, rcSelect
.left
, rcSelect
.top
, ETO_OPAQUE
, &rcSelect
, 0, 0, 0);
3741 if(lprcFocus
) *lprcFocus
= rcSelect
;
3744 /* figure out the text drawing flags */
3745 uFormat
= (uView
== LVS_ICON
? (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
3746 if (uView
== LVS_ICON
)
3747 uFormat
= (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
3750 switch (LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
3752 case LVCFMT_RIGHT
: uFormat
|= DT_RIGHT
; break;
3753 case LVCFMT_CENTER
: uFormat
|= DT_CENTER
; break;
3754 default: uFormat
|= DT_LEFT
;
3757 if (!(uFormat
& (DT_RIGHT
| DT_CENTER
)))
3759 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
3760 else rcLabel
.left
+= LABEL_HOR_PADDING
;
3762 else if (uFormat
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
3763 DrawTextW(hdc
, lvItem
.pszText
, -1, &rcLabel
, uFormat
);
3766 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
3767 notify_postpaint(infoPtr
, &nmlvcd
);
3773 * Draws listview items when in owner draw mode.
3776 * [I] infoPtr : valid pointer to the listview structure
3777 * [I] hdc : device context handle
3782 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3784 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
3785 DWORD cditemmode
= CDRF_DODEFAULT
;
3786 NMLVCUSTOMDRAW nmlvcd
;
3787 POINT Origin
, Position
;
3793 ZeroMemory(&dis
, sizeof(dis
));
3795 /* Get scroll info once before loop */
3796 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3798 /* iterate through the invalidated rows */
3799 while(iterator_next(i
))
3801 item
.iItem
= i
->nItem
;
3803 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
3804 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
3805 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
3807 dis
.CtlType
= ODT_LISTVIEW
;
3809 dis
.itemID
= item
.iItem
;
3810 dis
.itemAction
= ODA_DRAWENTIRE
;
3812 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
3813 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
3814 dis
.hwndItem
= infoPtr
->hwndSelf
;
3816 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
3817 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
3818 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
3819 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
3820 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
3821 dis
.itemData
= item
.lParam
;
3823 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), debugrect(&dis
.rcItem
));
3826 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3827 * structure for the rest. of the paint cycle
3829 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
3830 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3831 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3833 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
3835 prepaint_setup (infoPtr
, hdc
, &nmlvcd
);
3836 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
3839 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3840 notify_postpaint(infoPtr
, &nmlvcd
);
3846 * Draws listview items when in report display mode.
3849 * [I] infoPtr : valid pointer to the listview structure
3850 * [I] hdc : device context handle
3851 * [I] cdmode : custom draw mode
3856 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3859 RECT rcClip
, rcItem
;
3860 POINT Origin
, Position
;
3866 /* figure out what to draw */
3867 rgntype
= GetClipBox(hdc
, &rcClip
);
3868 if (rgntype
== NULLREGION
) return;
3870 /* Get scroll info once before loop */
3871 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3873 /* narrow down the columns we need to paint */
3874 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
3876 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
3877 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
3879 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
3881 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
3882 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
3884 iterator_rangeitems(&j
, colRange
);
3886 /* in full row select, we _have_ to draw the main item */
3887 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
3890 /* iterate through the invalidated rows */
3891 while(iterator_next(i
))
3893 /* iterate through the invalidated columns */
3894 while(iterator_next(&j
))
3896 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
3897 Position
.x
+= Origin
.x
;
3898 Position
.y
+= Origin
.y
;
3900 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
3902 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
3904 rcItem
.bottom
= infoPtr
->nItemHeight
;
3905 OffsetRect(&rcItem
, Position
.x
, Position
.y
);
3906 if (!RectVisible(hdc
, &rcItem
)) continue;
3909 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, j
.nItem
, Position
, cdmode
);
3912 iterator_destroy(&j
);
3917 * Draws listview items when in list display mode.
3920 * [I] infoPtr : valid pointer to the listview structure
3921 * [I] hdc : device context handle
3922 * [I] cdmode : custom draw mode
3927 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3929 POINT Origin
, Position
;
3931 /* Get scroll info once before loop */
3932 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3934 while(iterator_prev(i
))
3936 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
3937 Position
.x
+= Origin
.x
;
3938 Position
.y
+= Origin
.y
;
3940 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, 0, Position
, cdmode
);
3947 * Draws listview items.
3950 * [I] infoPtr : valid pointer to the listview structure
3951 * [I] hdc : device context handle
3956 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
3958 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3959 COLORREF oldTextColor
, oldClrTextBk
, oldClrText
;
3960 NMLVCUSTOMDRAW nmlvcd
;
3967 LISTVIEW_DUMP(infoPtr
);
3969 infoPtr
->bIsDrawing
= TRUE
;
3971 /* save dc values we're gonna trash while drawing */
3972 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
3973 oldBkMode
= GetBkMode(hdc
);
3974 infoPtr
->clrTextBkDefault
= GetBkColor(hdc
);
3975 oldTextColor
= GetTextColor(hdc
);
3977 oldClrTextBk
= infoPtr
->clrTextBk
;
3978 oldClrText
= infoPtr
->clrText
;
3980 infoPtr
->cditemmode
= CDRF_DODEFAULT
;
3982 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
3983 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
3984 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3985 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
3986 prepaint_setup(infoPtr
, hdc
, &nmlvcd
);
3988 /* Use these colors to draw the items */
3989 infoPtr
->clrTextBk
= nmlvcd
.clrTextBk
;
3990 infoPtr
->clrText
= nmlvcd
.clrText
;
3992 /* nothing to draw */
3993 if(infoPtr
->nItemCount
== 0) goto enddraw
;
3995 /* figure out what we need to draw */
3996 iterator_visibleitems(&i
, infoPtr
, hdc
);
3998 /* send cache hint notification */
3999 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4001 RANGE range
= iterator_range(&i
);
4004 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
4005 nmlv
.iFrom
= range
.lower
;
4006 nmlv
.iTo
= range
.upper
- 1;
4007 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
4010 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
4011 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
4014 if (uView
== LVS_REPORT
)
4015 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
4016 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4017 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
4019 /* if we have a focus rect, draw it */
4020 if (infoPtr
->bFocus
)
4021 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
4023 iterator_destroy(&i
);
4026 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
4027 notify_postpaint(infoPtr
, &nmlvcd
);
4029 infoPtr
->clrTextBk
= oldClrTextBk
;
4030 infoPtr
->clrText
= oldClrText
;
4032 SelectObject(hdc
, hOldFont
);
4033 SetBkMode(hdc
, oldBkMode
);
4034 SetBkColor(hdc
, infoPtr
->clrTextBkDefault
);
4035 SetTextColor(hdc
, oldTextColor
);
4036 infoPtr
->bIsDrawing
= FALSE
;
4042 * Calculates the approximate width and height of a given number of items.
4045 * [I] infoPtr : valid pointer to the listview structure
4046 * [I] nItemCount : number of items
4047 * [I] wWidth : width
4048 * [I] wHeight : height
4051 * Returns a DWORD. The width in the low word and the height in high word.
4053 static DWORD
LISTVIEW_ApproximateViewRect(LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
4054 WORD wWidth
, WORD wHeight
)
4056 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4057 INT nItemCountPerColumn
= 1;
4058 INT nColumnCount
= 0;
4059 DWORD dwViewRect
= 0;
4061 if (nItemCount
== -1)
4062 nItemCount
= infoPtr
->nItemCount
;
4064 if (uView
== LVS_LIST
)
4066 if (wHeight
== 0xFFFF)
4068 /* use current height */
4069 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4072 if (wHeight
< infoPtr
->nItemHeight
)
4073 wHeight
= infoPtr
->nItemHeight
;
4077 if (infoPtr
->nItemHeight
> 0)
4079 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
4080 if (nItemCountPerColumn
== 0)
4081 nItemCountPerColumn
= 1;
4083 if (nItemCount
% nItemCountPerColumn
!= 0)
4084 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
4086 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
4090 /* Microsoft padding magic */
4091 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
4092 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
4094 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4096 else if (uView
== LVS_REPORT
)
4100 if (infoPtr
->nItemCount
> 0)
4102 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
4103 wWidth
= rcBox
.right
- rcBox
.left
;
4104 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
4108 /* use current height and width */
4109 if (wHeight
== 0xffff)
4110 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4111 if (wWidth
== 0xffff)
4112 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
4115 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4117 else if (uView
== LVS_SMALLICON
)
4118 FIXME("uView == LVS_SMALLICON: not implemented\n");
4119 else if (uView
== LVS_ICON
)
4120 FIXME("uView == LVS_ICON: not implemented\n");
4128 * Create a drag image list for the specified item.
4131 * [I] infoPtr : valid pointer to the listview structure
4132 * [I] iItem : index of item
4133 * [O] lppt : Upperr-left corner of the image
4136 * Returns a handle to the image list if successful, NULL otherwise.
4138 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
4144 HBITMAP hbmp
, hOldbmp
;
4145 HIMAGELIST dragList
= 0;
4146 TRACE("iItem=%d Count=%d \n", iItem
, infoPtr
->nItemCount
);
4148 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
)
4151 rcItem
.left
= LVIR_BOUNDS
;
4152 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
4155 lppt
->x
= rcItem
.left
;
4156 lppt
->y
= rcItem
.top
;
4158 size
.cx
= rcItem
.right
- rcItem
.left
;
4159 size
.cy
= rcItem
.bottom
- rcItem
.top
;
4161 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
4162 hdc
= CreateCompatibleDC(hdcOrig
);
4163 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
4164 hOldbmp
= SelectObject(hdc
, hbmp
);
4166 rcItem
.left
= rcItem
.top
= 0;
4167 rcItem
.right
= size
.cx
;
4168 rcItem
.bottom
= size
.cy
;
4169 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
4172 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, 0, pos
, infoPtr
->cditemmode
))
4174 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
4175 SelectObject(hdc
, hOldbmp
);
4176 ImageList_Add(dragList
, hbmp
, 0);
4179 SelectObject(hdc
, hOldbmp
);
4183 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
4185 TRACE("ret=%p\n", dragList
);
4193 * Removes all listview items and subitems.
4196 * [I] infoPtr : valid pointer to the listview structure
4202 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
)
4205 HDPA hdpaSubItems
= NULL
;
4212 /* we do it directly, to avoid notifications */
4213 ranges_clear(infoPtr
->selectionRanges
);
4214 infoPtr
->nSelectionMark
= -1;
4215 infoPtr
->nFocusedItem
= -1;
4216 SetRectEmpty(&infoPtr
->rcFocus
);
4217 /* But we are supposed to leave nHotItem as is! */
4220 /* send LVN_DELETEALLITEMS notification */
4221 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4223 bSuppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
4225 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
4227 /* send LVN_DELETEITEM notification, if not suppressed */
4228 if (!bSuppress
) notify_deleteitem(infoPtr
, i
);
4229 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4231 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
4232 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
4234 hdrItem
= (ITEMHDR
*)DPA_GetPtr(hdpaSubItems
, j
);
4235 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4238 DPA_Destroy(hdpaSubItems
);
4239 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
4241 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
4242 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
4243 infoPtr
->nItemCount
--;
4246 LISTVIEW_UpdateScroll(infoPtr
);
4248 LISTVIEW_InvalidateList(infoPtr
);
4255 * Scrolls, and updates the columns, when a column is changing width.
4258 * [I] infoPtr : valid pointer to the listview structure
4259 * [I] nColumn : column to scroll
4260 * [I] dx : amount of scroll, in pixels
4265 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
4267 COLUMN_INFO
*lpColumnInfo
;
4271 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
4272 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
4273 rcCol
= lpColumnInfo
->rcHeader
;
4274 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
4275 rcCol
.left
= rcCol
.right
;
4277 /* ajust the other columns */
4278 for (nCol
= nColumn
; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
4280 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
4281 lpColumnInfo
->rcHeader
.left
+= dx
;
4282 lpColumnInfo
->rcHeader
.right
+= dx
;
4285 /* do not update screen if not in report mode */
4286 if (!is_redrawing(infoPtr
) || (infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return;
4288 /* if we have a focus, must first erase the focus rect */
4289 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
4291 /* Need to reset the item width when inserting a new column */
4292 infoPtr
->nItemWidth
+= dx
;
4294 LISTVIEW_UpdateScroll(infoPtr
);
4296 /* scroll to cover the deleted column, and invalidate for redraw */
4297 rcOld
= infoPtr
->rcList
;
4298 rcOld
.left
= rcCol
.left
;
4299 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4301 /* we can restore focus now */
4302 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
4307 * Removes a column from the listview control.
4310 * [I] infoPtr : valid pointer to the listview structure
4311 * [I] nColumn : column index
4317 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4321 TRACE("nColumn=%d\n", nColumn
);
4323 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0
4324 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4326 /* While the MSDN specifically says that column zero should not be deleted,
4327 what actually happens is that the column itself is deleted but no items or subitems
4331 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
4333 if (!Header_DeleteItem(infoPtr
->hwndHeader
, nColumn
))
4336 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
4337 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
4339 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
4341 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
4343 INT nItem
, nSubItem
, i
;
4345 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
4347 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
4350 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4352 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
4353 if (lpSubItem
->iSubItem
== nColumn
)
4356 lpDelItem
= lpSubItem
;
4358 else if (lpSubItem
->iSubItem
> nColumn
)
4360 lpSubItem
->iSubItem
--;
4364 /* if we found our subitem, zapp it */
4368 if (is_textW(lpDelItem
->hdr
.pszText
))
4369 Free(lpDelItem
->hdr
.pszText
);
4374 /* free dpa memory */
4375 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
4380 /* update the other column info */
4381 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
4382 LISTVIEW_InvalidateList(infoPtr
);
4384 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
4391 * Invalidates the listview after an item's insertion or deletion.
4394 * [I] infoPtr : valid pointer to the listview structure
4395 * [I] nItem : item index
4396 * [I] dir : -1 if deleting, 1 if inserting
4401 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
4403 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4404 INT nPerCol
, nItemCol
, nItemRow
;
4408 /* if we don't refresh, what's the point of scrolling? */
4409 if (!is_redrawing(infoPtr
)) return;
4411 assert (abs(dir
) == 1);
4413 /* arrange icons if autoarrange is on */
4414 if (is_autoarrange(infoPtr
))
4416 BOOL arrange
= TRUE
;
4417 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
4418 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
4419 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4422 /* scrollbars need updating */
4423 LISTVIEW_UpdateScroll(infoPtr
);
4425 /* figure out the item's position */
4426 if (uView
== LVS_REPORT
)
4427 nPerCol
= infoPtr
->nItemCount
+ 1;
4428 else if (uView
== LVS_LIST
)
4429 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
4430 else /* LVS_ICON, or LVS_SMALLICON */
4433 nItemCol
= nItem
/ nPerCol
;
4434 nItemRow
= nItem
% nPerCol
;
4435 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4437 /* move the items below up a slot */
4438 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
4439 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
4440 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
4441 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4442 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4443 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
4444 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4446 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll
), debugrect(&infoPtr
->rcList
));
4447 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4448 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4451 /* report has only that column, so we're done */
4452 if (uView
== LVS_REPORT
) return;
4454 /* now for LISTs, we have to deal with the columns to the right */
4455 rcScroll
.left
= (nItemCol
+ 1) * infoPtr
->nItemWidth
;
4457 rcScroll
.right
= (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
4458 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4459 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4460 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4461 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4462 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4467 * Removes an item from the listview control.
4470 * [I] infoPtr : valid pointer to the listview structure
4471 * [I] nItem : item index
4477 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
4479 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4482 TRACE("(nItem=%d)\n", nItem
);
4484 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
4486 /* remove selection, and focus */
4488 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4489 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
4491 /* send LVN_DELETEITEM notification. */
4492 notify_deleteitem(infoPtr
, nItem
);
4494 /* we need to do this here, because we'll be deleting stuff */
4495 if (uView
== LVS_SMALLICON
|| uView
== LVS_ICON
)
4496 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4498 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4504 hdpaSubItems
= (HDPA
)DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
4505 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4507 hdrItem
= (ITEMHDR
*)DPA_GetPtr(hdpaSubItems
, i
);
4508 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4511 DPA_Destroy(hdpaSubItems
);
4514 if (uView
== LVS_SMALLICON
|| uView
== LVS_ICON
)
4516 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
4517 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
4520 infoPtr
->nItemCount
--;
4521 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
4523 /* now is the invalidation fun */
4524 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
4531 * Callback implementation for editlabel control
4534 * [I] infoPtr : valid pointer to the listview structure
4535 * [I] pszText : modified text
4536 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4542 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, LPWSTR pszText
, BOOL isW
)
4544 NMLVDISPINFOW dispInfo
;
4546 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
4548 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4549 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4550 dispInfo
.item
.iItem
= infoPtr
->nEditLabelItem
;
4551 dispInfo
.item
.iSubItem
= 0;
4552 dispInfo
.item
.stateMask
= ~0;
4553 if (!LISTVIEW_GetItemW(infoPtr
, &dispInfo
.item
)) return FALSE
;
4554 /* add the text from the edit in */
4555 dispInfo
.item
.mask
|= LVIF_TEXT
;
4556 dispInfo
.item
.pszText
= pszText
;
4557 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4559 /* Do we need to update the Item Text */
4560 if (!notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
)) return FALSE
;
4561 if (!pszText
) return TRUE
;
4563 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4564 dispInfo
.item
.mask
= LVIF_TEXT
;
4565 dispInfo
.item
.iItem
= infoPtr
->nEditLabelItem
;
4566 dispInfo
.item
.iSubItem
= 0;
4567 dispInfo
.item
.pszText
= pszText
;
4568 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4569 return LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
4574 * Begin in place editing of specified list view item
4577 * [I] infoPtr : valid pointer to the listview structure
4578 * [I] nItem : item index
4579 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4585 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
4587 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
4588 NMLVDISPINFOW dispInfo
;
4591 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
4593 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
4594 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
4596 infoPtr
->nEditLabelItem
= nItem
;
4598 /* Is the EditBox still there, if so remove it */
4599 if(infoPtr
->hwndEdit
!= 0)
4601 SetFocus(infoPtr
->hwndSelf
);
4602 infoPtr
->hwndEdit
= 0;
4605 LISTVIEW_SetSelection(infoPtr
, nItem
);
4606 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
4607 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4609 rect
.left
= LVIR_LABEL
;
4610 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
4612 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4613 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4614 dispInfo
.item
.iItem
= nItem
;
4615 dispInfo
.item
.iSubItem
= 0;
4616 dispInfo
.item
.stateMask
= ~0;
4617 dispInfo
.item
.pszText
= szDispText
;
4618 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
4619 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
4621 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, WS_VISIBLE
,
4622 rect
.left
-2, rect
.top
-1, 0, rect
.bottom
- rect
.top
+2, isW
);
4623 if (!infoPtr
->hwndEdit
) return 0;
4625 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
4627 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
4628 infoPtr
->hwndEdit
= 0;
4632 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
4633 SetFocus(infoPtr
->hwndEdit
);
4634 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
4635 return infoPtr
->hwndEdit
;
4641 * Ensures the specified item is visible, scrolling into view if necessary.
4644 * [I] infoPtr : valid pointer to the listview structure
4645 * [I] nItem : item index
4646 * [I] bPartial : partially or entirely visible
4652 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
4654 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4655 INT nScrollPosHeight
= 0;
4656 INT nScrollPosWidth
= 0;
4657 INT nHorzAdjust
= 0;
4658 INT nVertAdjust
= 0;
4661 RECT rcItem
, rcTemp
;
4663 rcItem
.left
= LVIR_BOUNDS
;
4664 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
4666 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
4668 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
4670 /* scroll left/right, but in LVS_REPORT mode */
4671 if (uView
== LVS_LIST
)
4672 nScrollPosWidth
= infoPtr
->nItemWidth
;
4673 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
4674 nScrollPosWidth
= 1;
4676 if (rcItem
.left
< infoPtr
->rcList
.left
)
4679 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
4684 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
4688 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
4690 /* scroll up/down, but not in LVS_LIST mode */
4691 if (uView
== LVS_REPORT
)
4692 nScrollPosHeight
= infoPtr
->nItemHeight
;
4693 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
4694 nScrollPosHeight
= 1;
4696 if (rcItem
.top
< infoPtr
->rcList
.top
)
4699 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
4704 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
4708 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
4710 if (nScrollPosWidth
)
4712 INT diff
= nHorzDiff
/ nScrollPosWidth
;
4713 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
4714 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
4717 if (nScrollPosHeight
)
4719 INT diff
= nVertDiff
/ nScrollPosHeight
;
4720 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
4721 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
4729 * Searches for an item with specific characteristics.
4732 * [I] hwnd : window handle
4733 * [I] nStart : base item index
4734 * [I] lpFindInfo : item information to look for
4737 * SUCCESS : index of item
4740 static INT
LISTVIEW_FindItemW(LISTVIEW_INFO
*infoPtr
, INT nStart
,
4741 const LVFINDINFOW
*lpFindInfo
)
4743 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4744 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
4745 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
4746 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
4747 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
4748 POINT Position
, Destination
;
4751 if (!lpFindInfo
|| nItem
< 0) return -1;
4754 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
))
4756 lvItem
.mask
|= LVIF_TEXT
;
4757 lvItem
.pszText
= szDispText
;
4758 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4761 if (lpFindInfo
->flags
& LVFI_WRAP
)
4764 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
4765 (uView
== LVS_ICON
|| uView
==LVS_SMALLICON
))
4770 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4771 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
4772 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
4773 switch(lpFindInfo
->vkDirection
)
4775 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
4776 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
4777 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
4778 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
4779 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
4780 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
4781 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
4783 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
4784 Destination
.x
= rcArea
.right
;
4785 Destination
.y
= rcArea
.bottom
;
4787 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
4792 /* if LVFI_PARAM is specified, all other flags are ignored */
4793 if (lpFindInfo
->flags
& LVFI_PARAM
)
4795 lvItem
.mask
|= LVIF_PARAM
;
4797 lvItem
.mask
&= ~LVIF_TEXT
;
4801 for (; nItem
< nLast
; nItem
++)
4803 lvItem
.iItem
= nItem
;
4804 lvItem
.iSubItem
= 0;
4805 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
4807 if (lvItem
.mask
& LVIF_PARAM
)
4809 if (lpFindInfo
->lParam
== lvItem
.lParam
)
4815 if (lvItem
.mask
& LVIF_TEXT
)
4817 if (lpFindInfo
->flags
& LVFI_PARTIAL
)
4819 if (strstrW(lvItem
.pszText
, lpFindInfo
->psz
) == NULL
) continue;
4823 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
4827 if (!bNearest
) return nItem
;
4829 /* This is very inefficient. To do a good job here,
4830 * we need a sorted array of (x,y) item positions */
4831 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
4833 /* compute the distance^2 to the destination */
4834 xdist
= Destination
.x
- Position
.x
;
4835 ydist
= Destination
.y
- Position
.y
;
4836 dist
= xdist
* xdist
+ ydist
* ydist
;
4838 /* remember the distance, and item if it's closer */
4842 nNearestItem
= nItem
;
4849 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
4854 return nNearestItem
;
4859 * Searches for an item with specific characteristics.
4862 * [I] hwnd : window handle
4863 * [I] nStart : base item index
4864 * [I] lpFindInfo : item information to look for
4867 * SUCCESS : index of item
4870 static INT
LISTVIEW_FindItemA(LISTVIEW_INFO
*infoPtr
, INT nStart
,
4871 const LVFINDINFOA
*lpFindInfo
)
4873 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
);
4877 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
4878 if (hasText
) fiw
.psz
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
4879 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
4880 if (hasText
) textfreeT((LPWSTR
)fiw
.psz
, FALSE
);
4886 * Retrieves the background image of the listview control.
4889 * [I] infoPtr : valid pointer to the listview structure
4890 * [O] lpBkImage : background image attributes
4896 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4898 /* FIXME (listview, "empty stub!\n"); */
4904 * Retrieves column attributes.
4907 * [I] infoPtr : valid pointer to the listview structure
4908 * [I] nColumn : column index
4909 * [IO] lpColumn : column information
4910 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4911 * otherwise it is in fact a LPLVCOLUMNA
4917 static BOOL
LISTVIEW_GetColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
4919 COLUMN_INFO
*lpColumnInfo
;
4922 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4923 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
4925 /* initialize memory */
4926 ZeroMemory(&hdi
, sizeof(hdi
));
4928 if (lpColumn
->mask
& LVCF_TEXT
)
4930 hdi
.mask
|= HDI_TEXT
;
4931 hdi
.pszText
= lpColumn
->pszText
;
4932 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
4935 if (lpColumn
->mask
& LVCF_IMAGE
)
4936 hdi
.mask
|= HDI_IMAGE
;
4938 if (lpColumn
->mask
& LVCF_ORDER
)
4939 hdi
.mask
|= HDI_ORDER
;
4941 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
4943 if (lpColumn
->mask
& LVCF_FMT
)
4944 lpColumn
->fmt
= lpColumnInfo
->fmt
;
4946 if (lpColumn
->mask
& LVCF_WIDTH
)
4947 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
4949 if (lpColumn
->mask
& LVCF_IMAGE
)
4950 lpColumn
->iImage
= hdi
.iImage
;
4952 if (lpColumn
->mask
& LVCF_ORDER
)
4953 lpColumn
->iOrder
= hdi
.iOrder
;
4959 static BOOL
LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
4966 /* FIXME: little hack */
4967 for (i
= 0; i
< iCount
; i
++)
4975 * Retrieves the column width.
4978 * [I] infoPtr : valid pointer to the listview structure
4979 * [I] int : column index
4982 * SUCCESS : column width
4985 static INT
LISTVIEW_GetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4987 INT nColumnWidth
= 0;
4990 TRACE("nColumn=%d\n", nColumn
);
4992 /* we have a 'column' in LIST and REPORT mode only */
4993 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
)
4996 nColumnWidth
= infoPtr
->nItemWidth
;
4999 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
5000 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
5001 nColumnWidth
= rcHeader
.right
- rcHeader
.left
;
5005 TRACE("nColumnWidth=%d\n", nColumnWidth
);
5006 return nColumnWidth
;
5011 * In list or report display mode, retrieves the number of items that can fit
5012 * vertically in the visible area. In icon or small icon display mode,
5013 * retrieves the total number of visible items.
5016 * [I] infoPtr : valid pointer to the listview structure
5019 * Number of fully visible items.
5021 static INT
LISTVIEW_GetCountPerPage(LISTVIEW_INFO
*infoPtr
)
5023 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
5027 return infoPtr
->nItemCount
;
5029 return LISTVIEW_GetCountPerColumn(infoPtr
);
5031 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
5039 * Retrieves an image list handle.
5042 * [I] infoPtr : valid pointer to the listview structure
5043 * [I] nImageList : image list identifier
5046 * SUCCESS : image list handle
5049 static HIMAGELIST
LISTVIEW_GetImageList(LISTVIEW_INFO
*infoPtr
, INT nImageList
)
5053 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
5054 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
5055 case LVSIL_STATE
: return infoPtr
->himlState
;
5060 /* LISTVIEW_GetISearchString */
5064 * Retrieves item attributes.
5067 * [I] hwnd : window handle
5068 * [IO] lpLVItem : item info
5069 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5070 * if FALSE, the lpLVItem is a LPLVITEMA.
5073 * This is the internal 'GetItem' interface -- it tries to
5074 * be smart, and avoids text copies, if possible, by modifing
5075 * lpLVItem->pszText to point to the text string. Please note
5076 * that this is not always possible (e.g. OWNERDATA), so on
5077 * entry you *must* supply valid values for pszText, and cchTextMax.
5078 * The only difference to the documented interface is that upon
5079 * return, you should use *only* the lpLVItem->pszText, rather than
5080 * the buffer pointer you provided on input. Most code already does
5081 * that, so it's not a problem.
5082 * For the two cases when the text must be copied (that is,
5083 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5089 static BOOL
LISTVIEW_GetItemT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5091 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
5092 NMLVDISPINFOW dispInfo
;
5098 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
5100 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5103 if (lpLVItem
->mask
== 0) return TRUE
;
5105 /* make a local copy */
5106 isubitem
= lpLVItem
->iSubItem
;
5108 /* a quick optimization if all we're asked is the focus state
5109 * these queries are worth optimising since they are common,
5110 * and can be answered in constant time, without the heavy accesses */
5111 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
5112 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
5114 lpLVItem
->state
= 0;
5115 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5116 lpLVItem
->state
|= LVIS_FOCUSED
;
5120 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5122 /* if the app stores all the data, handle it separately */
5123 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5125 dispInfo
.item
.state
= 0;
5127 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5128 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) || infoPtr
->uCallbackMask
)
5130 /* NOTE: copy only fields which we _know_ are initialized, some apps
5131 * depend on the uninitialized fields being 0 */
5132 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
5133 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5134 dispInfo
.item
.iSubItem
= isubitem
;
5135 if (lpLVItem
->mask
& LVIF_TEXT
)
5137 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5138 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5140 if (lpLVItem
->mask
& LVIF_STATE
)
5141 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
5142 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5143 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
5144 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
5146 /* full size structure expected - _WIN32IE >= 0x560 */
5147 *lpLVItem
= dispInfo
.item
;
5149 else if (lpLVItem
->mask
& LVIF_INDENT
)
5151 /* indent member expected - _WIN32IE >= 0x300 */
5152 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
5156 /* minimal structure expected */
5157 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
5159 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
5162 /* make sure lParam is zeroed out */
5163 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
5165 /* we store only a little state, so if we're not asked, we're done */
5166 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
5168 /* if focus is handled by us, report it */
5169 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5171 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5172 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5173 lpLVItem
->state
|= LVIS_FOCUSED
;
5176 /* and do the same for selection, if we handle it */
5177 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5179 lpLVItem
->state
&= ~LVIS_SELECTED
;
5180 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5181 lpLVItem
->state
|= LVIS_SELECTED
;
5187 /* find the item and subitem structures before we proceed */
5188 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
5189 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
5194 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
5195 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
5198 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
5203 pItemHdr
= &lpItem
->hdr
;
5205 /* Do we need to query the state from the app? */
5206 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
5208 dispInfo
.item
.mask
|= LVIF_STATE
;
5209 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
5212 /* Do we need to enquire about the image? */
5213 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
5214 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
5216 dispInfo
.item
.mask
|= LVIF_IMAGE
;
5217 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
5220 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5221 if ((lpLVItem
->mask
& LVIF_TEXT
) && !is_textW(pItemHdr
->pszText
))
5223 dispInfo
.item
.mask
|= LVIF_TEXT
;
5224 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5225 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5226 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
5227 *dispInfo
.item
.pszText
= '\0';
5230 /* If we don't have all the requested info, query the application */
5231 if (dispInfo
.item
.mask
!= 0)
5233 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5234 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
5235 dispInfo
.item
.lParam
= lpItem
->lParam
;
5236 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5237 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
5240 /* we should not store values for subitems */
5241 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
5243 /* Now, handle the iImage field */
5244 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
5246 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
5247 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
5248 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
5250 else if (lpLVItem
->mask
& LVIF_IMAGE
)
5252 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
5253 lpLVItem
->iImage
= pItemHdr
->iImage
;
5255 lpLVItem
->iImage
= 0;
5258 /* The pszText field */
5259 if (dispInfo
.item
.mask
& LVIF_TEXT
)
5261 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
5262 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
5264 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
5266 else if (lpLVItem
->mask
& LVIF_TEXT
)
5268 if (isW
) lpLVItem
->pszText
= pItemHdr
->pszText
;
5269 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
5272 /* if this is a subitem, we're done */
5273 if (isubitem
) return TRUE
;
5275 /* Next is the lParam field */
5276 if (dispInfo
.item
.mask
& LVIF_PARAM
)
5278 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
5279 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
5280 lpItem
->lParam
= dispInfo
.item
.lParam
;
5282 else if (lpLVItem
->mask
& LVIF_PARAM
)
5283 lpLVItem
->lParam
= lpItem
->lParam
;
5285 /* ... the state field (this one is different due to uCallbackmask) */
5286 if (lpLVItem
->mask
& LVIF_STATE
)
5288 lpLVItem
->state
= lpItem
->state
;
5289 if (dispInfo
.item
.mask
& LVIF_STATE
)
5291 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
5292 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
5294 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5296 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5297 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5298 lpLVItem
->state
|= LVIS_FOCUSED
;
5300 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5302 lpLVItem
->state
&= ~LVIS_SELECTED
;
5303 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5304 lpLVItem
->state
|= LVIS_SELECTED
;
5308 /* and last, but not least, the indent field */
5309 if (lpLVItem
->mask
& LVIF_INDENT
)
5310 lpLVItem
->iIndent
= lpItem
->iIndent
;
5317 * Retrieves item attributes.
5320 * [I] hwnd : window handle
5321 * [IO] lpLVItem : item info
5322 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5323 * if FALSE, the lpLVItem is a LPLVITEMA.
5326 * This is the external 'GetItem' interface -- it properly copies
5327 * the text in the provided buffer.
5333 static BOOL
LISTVIEW_GetItemExtT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5338 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5341 pszText
= lpLVItem
->pszText
;
5342 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
5343 if (bResult
&& lpLVItem
->pszText
!= pszText
)
5344 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
5345 lpLVItem
->pszText
= pszText
;
5353 * Retrieves the position (upper-left) of the listview control item.
5354 * Note that for LVS_ICON style, the upper-left is that of the icon
5355 * and not the bounding box.
5358 * [I] infoPtr : valid pointer to the listview structure
5359 * [I] nItem : item index
5360 * [O] lpptPosition : coordinate information
5366 static BOOL
LISTVIEW_GetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
5368 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5371 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
5373 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5375 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5376 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
5378 if (uView
== LVS_ICON
)
5380 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
5381 lpptPosition
->y
+= ICON_TOP_PADDING
;
5383 lpptPosition
->x
+= Origin
.x
;
5384 lpptPosition
->y
+= Origin
.y
;
5386 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition
));
5393 * Retrieves the bounding rectangle for a listview control item.
5396 * [I] infoPtr : valid pointer to the listview structure
5397 * [I] nItem : item index
5398 * [IO] lprc : bounding rectangle coordinates
5399 * lprc->left specifies the portion of the item for which the bounding
5400 * rectangle will be retrieved.
5402 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5403 * including the icon and label.
5406 * * Experiment shows that native control returns:
5407 * * width = min (48, length of text line)
5408 * * .left = position.x - (width - iconsize.cx)/2
5409 * * .right = .left + width
5410 * * height = #lines of text * ntmHeight + icon height + 8
5411 * * .top = position.y - 2
5412 * * .bottom = .top + height
5413 * * separation between items .y = itemSpacing.cy - height
5414 * * .x = itemSpacing.cx - width
5415 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5418 * * Experiment shows that native control returns:
5419 * * width = iconSize.cx + 16
5420 * * .left = position.x - (width - iconsize.cx)/2
5421 * * .right = .left + width
5422 * * height = iconSize.cy + 4
5423 * * .top = position.y - 2
5424 * * .bottom = .top + height
5425 * * separation between items .y = itemSpacing.cy - height
5426 * * .x = itemSpacing.cx - width
5427 * LVIR_LABEL Returns the bounding rectangle of the item text.
5430 * * Experiment shows that native control returns:
5431 * * width = text length
5432 * * .left = position.x - width/2
5433 * * .right = .left + width
5434 * * height = ntmH * linecount + 2
5435 * * .top = position.y + iconSize.cy + 6
5436 * * .bottom = .top + height
5437 * * separation between items .y = itemSpacing.cy - height
5438 * * .x = itemSpacing.cx - width
5439 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5440 * rectangles, but excludes columns in report view.
5447 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5448 * upon whether the window has the focus currently and on whether the item
5449 * is the one with the focus. Ensure that the control's record of which
5450 * item has the focus agrees with the items' records.
5452 static BOOL
LISTVIEW_GetItemRect(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5454 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5455 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5456 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
5457 POINT Position
, Origin
;
5461 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
5463 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5465 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5466 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5468 /* Be smart and try to figure out the minimum we have to do */
5469 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
5470 if (uView
== LVS_REPORT
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
5471 if (uView
== LVS_ICON
&& lprc
->left
!= LVIR_ICON
&&
5472 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
5473 oversizedBox
= TRUE
;
5475 /* get what we need from the item before hand, so we make
5476 * only one request. This can speed up things, if data
5477 * is stored on the app side */
5479 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
5480 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
5481 lvItem
.iItem
= nItem
;
5482 lvItem
.iSubItem
= 0;
5483 lvItem
.pszText
= szDispText
;
5484 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5485 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5486 /* we got the state already up, simulate it here, to avoid a reget */
5487 if (uView
== LVS_ICON
&& (lprc
->left
!= LVIR_ICON
))
5489 lvItem
.mask
|= LVIF_STATE
;
5490 lvItem
.stateMask
= LVIS_FOCUSED
;
5491 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
5494 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
5495 lprc
->left
= LVIR_BOUNDS
;
5499 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
);
5503 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, lprc
);
5507 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
);
5510 case LVIR_SELECTBOUNDS
:
5511 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, &label_rect
);
5512 UnionRect(lprc
, lprc
, &label_rect
);
5516 WARN("Unknown value: %ld\n", lprc
->left
);
5520 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
5522 TRACE(" rect=%s\n", debugrect(lprc
));
5529 * Retrieves the spacing between listview control items.
5532 * [I] infoPtr : valid pointer to the listview structure
5533 * [IO] lprc : rectangle to receive the output
5534 * on input, lprc->top = nSubItem
5535 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5537 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5538 * not only those of the first column.
5539 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5545 static BOOL
LISTVIEW_GetSubItemRect(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5550 if (!lprc
) return FALSE
;
5552 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem
, lprc
->top
);
5553 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5555 return LISTVIEW_GetItemRect(infoPtr
, nItem
, lprc
);
5557 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return FALSE
;
5559 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &Position
)) return FALSE
;
5562 lvItem
.iItem
= nItem
;
5563 lvItem
.iSubItem
= lprc
->top
;
5565 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5569 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
);
5574 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
);
5578 ERR("Unknown bounds=%ld\n", lprc
->left
);
5582 OffsetRect(lprc
, Position
.x
, Position
.y
);
5589 * Retrieves the width of a label.
5592 * [I] infoPtr : valid pointer to the listview structure
5595 * SUCCESS : string width (in pixels)
5598 static INT
LISTVIEW_GetLabelWidth(LISTVIEW_INFO
*infoPtr
, INT nItem
)
5600 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5603 TRACE("(nItem=%d)\n", nItem
);
5605 lvItem
.mask
= LVIF_TEXT
;
5606 lvItem
.iItem
= nItem
;
5607 lvItem
.iSubItem
= 0;
5608 lvItem
.pszText
= szDispText
;
5609 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5610 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5612 return LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
5617 * Retrieves the spacing between listview control items.
5620 * [I] infoPtr : valid pointer to the listview structure
5621 * [I] bSmall : flag for small or large icon
5624 * Horizontal + vertical spacing
5626 static LONG
LISTVIEW_GetItemSpacing(LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
5632 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
5636 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
5637 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
5639 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
5646 * Retrieves the state of a listview control item.
5649 * [I] infoPtr : valid pointer to the listview structure
5650 * [I] nItem : item index
5651 * [I] uMask : state mask
5654 * State specified by the mask.
5656 static UINT
LISTVIEW_GetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
5660 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
5662 lvItem
.iItem
= nItem
;
5663 lvItem
.iSubItem
= 0;
5664 lvItem
.mask
= LVIF_STATE
;
5665 lvItem
.stateMask
= uMask
;
5666 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5668 return lvItem
.state
& uMask
;
5673 * Retrieves the text of a listview control item or subitem.
5676 * [I] hwnd : window handle
5677 * [I] nItem : item index
5678 * [IO] lpLVItem : item information
5679 * [I] isW : TRUE if lpLVItem is Unicode
5682 * SUCCESS : string length
5685 static INT
LISTVIEW_GetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
5687 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
5689 lpLVItem
->mask
= LVIF_TEXT
;
5690 lpLVItem
->iItem
= nItem
;
5691 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
5693 return textlenT(lpLVItem
->pszText
, isW
);
5698 * Searches for an item based on properties + relationships.
5701 * [I] infoPtr : valid pointer to the listview structure
5702 * [I] nItem : item index
5703 * [I] uFlags : relationship flag
5706 * SUCCESS : item index
5709 static INT
LISTVIEW_GetNextItem(LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
5711 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5713 LVFINDINFOW lvFindInfo
;
5714 INT nCountPerColumn
;
5718 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
5719 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
5721 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
5723 if (uFlags
& LVNI_CUT
)
5726 if (uFlags
& LVNI_DROPHILITED
)
5727 uMask
|= LVIS_DROPHILITED
;
5729 if (uFlags
& LVNI_FOCUSED
)
5730 uMask
|= LVIS_FOCUSED
;
5732 if (uFlags
& LVNI_SELECTED
)
5733 uMask
|= LVIS_SELECTED
;
5735 /* if we're asked for the focused item, that's only one,
5736 * so it's worth optimizing */
5737 if (uFlags
& LVNI_FOCUSED
)
5739 if (!(LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) == uMask
) return -1;
5740 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
5743 if (uFlags
& LVNI_ABOVE
)
5745 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
5750 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5756 /* Special case for autoarrange - move 'til the top of a list */
5757 if (is_autoarrange(infoPtr
))
5759 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5760 while (nItem
- nCountPerRow
>= 0)
5762 nItem
-= nCountPerRow
;
5763 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5768 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5769 lvFindInfo
.vkDirection
= VK_UP
;
5770 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5771 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5773 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5778 else if (uFlags
& LVNI_BELOW
)
5780 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
5782 while (nItem
< infoPtr
->nItemCount
)
5785 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5791 /* Special case for autoarrange - move 'til the bottom of a list */
5792 if (is_autoarrange(infoPtr
))
5794 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5795 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
5797 nItem
+= nCountPerRow
;
5798 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5803 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5804 lvFindInfo
.vkDirection
= VK_DOWN
;
5805 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5806 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5808 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5813 else if (uFlags
& LVNI_TOLEFT
)
5815 if (uView
== LVS_LIST
)
5817 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
5818 while (nItem
- nCountPerColumn
>= 0)
5820 nItem
-= nCountPerColumn
;
5821 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5825 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5827 /* Special case for autoarrange - move 'ti the beginning of a row */
5828 if (is_autoarrange(infoPtr
))
5830 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5831 while (nItem
% nCountPerRow
> 0)
5834 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5839 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5840 lvFindInfo
.vkDirection
= VK_LEFT
;
5841 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5842 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5844 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5849 else if (uFlags
& LVNI_TORIGHT
)
5851 if (uView
== LVS_LIST
)
5853 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
5854 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
5856 nItem
+= nCountPerColumn
;
5857 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5861 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5863 /* Special case for autoarrange - move 'til the end of a row */
5864 if (is_autoarrange(infoPtr
))
5866 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5867 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
5870 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5875 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5876 lvFindInfo
.vkDirection
= VK_RIGHT
;
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
)
5889 /* search by index */
5890 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
5892 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
5900 /* LISTVIEW_GetNumberOfWorkAreas */
5904 * Retrieves the origin coordinates when in icon or small icon display mode.
5907 * [I] infoPtr : valid pointer to the listview structure
5908 * [O] lpptOrigin : coordinate information
5913 static void LISTVIEW_GetOrigin(LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
5915 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5916 INT nHorzPos
= 0, nVertPos
= 0;
5917 SCROLLINFO scrollInfo
;
5919 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
5920 scrollInfo
.fMask
= SIF_POS
;
5922 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
5923 nHorzPos
= scrollInfo
.nPos
;
5924 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
5925 nVertPos
= scrollInfo
.nPos
;
5927 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
5929 lpptOrigin
->x
= infoPtr
->rcList
.left
;
5930 lpptOrigin
->y
= infoPtr
->rcList
.top
;
5931 if (uView
== LVS_LIST
)
5932 nHorzPos
*= infoPtr
->nItemWidth
;
5933 else if (uView
== LVS_REPORT
)
5934 nVertPos
*= infoPtr
->nItemHeight
;
5936 lpptOrigin
->x
-= nHorzPos
;
5937 lpptOrigin
->y
-= nVertPos
;
5939 TRACE(" origin=%s\n", debugpoint(lpptOrigin
));
5944 * Retrieves the width of a string.
5947 * [I] hwnd : window handle
5948 * [I] lpszText : text string to process
5949 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5952 * SUCCESS : string width (in pixels)
5955 static INT
LISTVIEW_GetStringWidthT(LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
5960 if (is_textT(lpszText
, isW
))
5962 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
5963 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
5964 HFONT hOldFont
= SelectObject(hdc
, hFont
);
5967 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
5969 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
5970 SelectObject(hdc
, hOldFont
);
5971 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
5973 return stringSize
.cx
;
5978 * Determines which listview item is located at the specified position.
5981 * [I] infoPtr : valid pointer to the listview structure
5982 * [IO] lpht : hit test information
5983 * [I] subitem : fill out iSubItem.
5984 * [I] select : return the index only if the hit selects the item
5987 * (mm 20001022): We must not allow iSubItem to be touched, for
5988 * an app might pass only a structure with space up to iItem!
5989 * (MS Office 97 does that for instance in the file open dialog)
5992 * SUCCESS : item index
5995 static INT
LISTVIEW_HitTest(LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
5997 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5998 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5999 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
6000 POINT Origin
, Position
, opt
;
6005 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht
->pt
), subitem
, select
);
6009 if (subitem
) lpht
->iSubItem
= 0;
6011 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
6012 lpht
->flags
|= LVHT_TOLEFT
;
6013 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
6014 lpht
->flags
|= LVHT_TORIGHT
;
6016 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
6017 lpht
->flags
|= LVHT_ABOVE
;
6018 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
6019 lpht
->flags
|= LVHT_BELOW
;
6021 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6022 if (lpht
->flags
) return -1;
6024 lpht
->flags
|= LVHT_NOWHERE
;
6026 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6028 /* first deal with the large items */
6029 rcSearch
.left
= lpht
->pt
.x
;
6030 rcSearch
.top
= lpht
->pt
.y
;
6031 rcSearch
.right
= rcSearch
.left
+ 1;
6032 rcSearch
.bottom
= rcSearch
.top
+ 1;
6034 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
6035 iterator_next(&i
); /* go to first item in the sequence */
6037 iterator_destroy(&i
);
6039 TRACE("lpht->iItem=%d\n", iItem
);
6040 if (iItem
== -1) return -1;
6042 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
6043 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
6044 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
6045 if (uView
== LVS_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
6046 lvItem
.iItem
= iItem
;
6047 lvItem
.iSubItem
= 0;
6048 lvItem
.pszText
= szDispText
;
6049 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6050 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
6051 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
6053 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcState
, &rcIcon
, &rcLabel
);
6054 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
6055 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
6056 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
6058 if (uView
== LVS_REPORT
)
6061 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6062 TRACE("rcBounds=%s\n", debugrect(&rcBounds
));
6063 if (!PtInRect(&rcBounds
, opt
)) return -1;
6065 if (PtInRect(&rcIcon
, opt
))
6066 lpht
->flags
|= LVHT_ONITEMICON
;
6067 else if (PtInRect(&rcLabel
, opt
))
6068 lpht
->flags
|= LVHT_ONITEMLABEL
;
6069 else if (infoPtr
->himlState
&& ((lvItem
.state
& LVIS_STATEIMAGEMASK
) >> 12) && PtInRect(&rcState
, opt
))
6070 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
6071 if (lpht
->flags
& LVHT_ONITEM
)
6072 lpht
->flags
&= ~LVHT_NOWHERE
;
6074 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6075 if (uView
== LVS_REPORT
&& subitem
)
6079 rcBounds
.right
= rcBounds
.left
;
6080 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
6082 rcBounds
.left
= rcBounds
.right
;
6083 rcBounds
.right
+= LISTVIEW_GetColumnWidth(infoPtr
, j
);
6084 if (PtInRect(&rcBounds
, opt
))
6092 if (select
&& !(uView
== LVS_REPORT
&&
6093 ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) ||
6094 (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))))
6096 if (uView
== LVS_REPORT
)
6098 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6099 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6101 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
6103 return lpht
->iItem
= iItem
;
6107 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6108 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6109 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6110 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6111 their own sort proc. when sending LVM_SORTITEMS.
6114 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6116 LVS_SORTXXX must be specified,
6117 LVS_OWNERDRAW is not set,
6118 <item>.pszText is not LPSTR_TEXTCALLBACK.
6120 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6121 are sorted based on item text..."
6123 static INT WINAPI
LISTVIEW_InsertCompare( LPVOID first
, LPVOID second
, LPARAM lParam
)
6125 ITEM_INFO
* lv_first
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)first
, 0 );
6126 ITEM_INFO
* lv_second
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)second
, 0 );
6127 INT cmpv
= textcmpWT(lv_first
->hdr
.pszText
, lv_second
->hdr
.pszText
, TRUE
);
6129 /* if we're sorting descending, negate the return value */
6130 return (((LISTVIEW_INFO
*)lParam
)->dwStyle
& LVS_SORTDESCENDING
) ? -cmpv
: cmpv
;
6135 * Inserts a new item in the listview control.
6138 * [I] infoPtr : valid pointer to the listview structure
6139 * [I] lpLVItem : item information
6140 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6143 * SUCCESS : new item index
6146 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
6148 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6153 BOOL is_sorted
, has_changed
;
6156 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6158 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
6160 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6161 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iSubItem
) return -1;
6163 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
6165 if (!(lpItem
= (ITEM_INFO
*)Alloc(sizeof(ITEM_INFO
)))) return -1;
6167 /* insert item in listview control data structure */
6168 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
6169 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
6171 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
6172 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
6174 nItem
= is_sorted
? infoPtr
->nItemCount
: min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
6175 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
6176 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
6177 if (nItem
== -1) goto fail
;
6178 infoPtr
->nItemCount
++;
6180 /* shift indices first so they don't get tangled */
6181 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
6183 /* set the item attributes */
6184 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6186 /* full size structure expected - _WIN32IE >= 0x560 */
6189 else if (lpLVItem
->mask
& LVIF_INDENT
)
6191 /* indent member expected - _WIN32IE >= 0x300 */
6192 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
6196 /* minimal structure expected */
6197 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
6200 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) item
.state
&= ~LVIS_STATEIMAGEMASK
;
6201 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
6203 /* if we're sorted, sort the list, and update the index */
6206 DPA_Sort( infoPtr
->hdpaItems
, LISTVIEW_InsertCompare
, (LPARAM
)infoPtr
);
6207 nItem
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, hdpaSubItems
);
6208 assert(nItem
!= -1);
6211 /* make room for the position, if we are in the right mode */
6212 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6214 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
6216 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
6218 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
6223 /* send LVN_INSERTITEM notification */
6224 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
6226 nmlv
.lParam
= lpItem
->lParam
;
6227 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
6229 /* align items (set position of each item) */
6230 if ((uView
== LVS_SMALLICON
|| uView
== LVS_ICON
))
6234 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
6235 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
6237 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
6239 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
6242 /* now is the invalidation fun */
6243 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
6247 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
6248 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
6249 infoPtr
->nItemCount
--;
6251 DPA_DeletePtr(hdpaSubItems
, 0);
6252 DPA_Destroy (hdpaSubItems
);
6259 * Redraws a range of items.
6262 * [I] infoPtr : valid pointer to the listview structure
6263 * [I] nFirst : first item
6264 * [I] nLast : last item
6270 static BOOL
LISTVIEW_RedrawItems(LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
6274 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
6275 max(nFirst
, nLast
) >= infoPtr
->nItemCount
)
6278 for (i
= nFirst
; i
<= nLast
; i
++)
6279 LISTVIEW_InvalidateItem(infoPtr
, i
);
6286 * Scroll the content of a listview.
6289 * [I] infoPtr : valid pointer to the listview structure
6290 * [I] dx : horizontal scroll amount in pixels
6291 * [I] dy : vertical scroll amount in pixels
6298 * If the control is in report mode (LVS_REPORT) the control can
6299 * be scrolled only in line increments. "dy" will be rounded to the
6300 * nearest number of pixels that are a whole line. Ex: if line height
6301 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6302 * is passed the the scroll will be 0. (per MSDN 7/2002)
6304 * For: (per experimentaion with native control and CSpy ListView)
6305 * LVS_ICON dy=1 = 1 pixel (vertical only)
6307 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6309 * LVS_LIST dx=1 = 1 column (horizontal only)
6310 * but will only scroll 1 column per message
6311 * no matter what the value.
6312 * dy must be 0 or FALSE returned.
6313 * LVS_REPORT dx=1 = 1 pixel
6317 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
6319 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
) {
6321 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
6322 dy
/= infoPtr
->nItemHeight
;
6325 if (dy
!= 0) return FALSE
;
6332 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
, 0);
6333 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
, 0);
6340 * Sets the background color.
6343 * [I] infoPtr : valid pointer to the listview structure
6344 * [I] clrBk : background color
6350 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrBk
)
6352 TRACE("(clrBk=%lx)\n", clrBk
);
6354 if(infoPtr
->clrBk
!= clrBk
) {
6355 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
6356 infoPtr
->clrBk
= clrBk
;
6357 if (clrBk
== CLR_NONE
)
6358 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
6360 infoPtr
->hBkBrush
= CreateSolidBrush(clrBk
);
6361 LISTVIEW_InvalidateList(infoPtr
);
6367 /* LISTVIEW_SetBkImage */
6369 /*** Helper for {Insert,Set}ColumnT *only* */
6370 static void column_fill_hditem(LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
, const LVCOLUMNW
*lpColumn
, BOOL isW
)
6372 if (lpColumn
->mask
& LVCF_FMT
)
6374 /* format member is valid */
6375 lphdi
->mask
|= HDI_FORMAT
;
6377 /* set text alignment (leftmost column must be left-aligned) */
6378 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
6379 lphdi
->fmt
|= HDF_LEFT
;
6380 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
6381 lphdi
->fmt
|= HDF_RIGHT
;
6382 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
6383 lphdi
->fmt
|= HDF_CENTER
;
6385 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6386 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
6388 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6390 lphdi
->fmt
|= HDF_IMAGE
;
6391 lphdi
->iImage
= I_IMAGECALLBACK
;
6395 if (lpColumn
->mask
& LVCF_WIDTH
)
6397 lphdi
->mask
|= HDI_WIDTH
;
6398 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
6400 /* make it fill the remainder of the controls width */
6404 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
6406 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
6407 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
6410 /* retrieve the layout of the header */
6411 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
6412 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, debugrect(&rcHeader
));
6414 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
6417 lphdi
->cxy
= lpColumn
->cx
;
6420 if (lpColumn
->mask
& LVCF_TEXT
)
6422 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
6423 lphdi
->fmt
|= HDF_STRING
;
6424 lphdi
->pszText
= lpColumn
->pszText
;
6425 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
6428 if (lpColumn
->mask
& LVCF_IMAGE
)
6430 lphdi
->mask
|= HDI_IMAGE
;
6431 lphdi
->iImage
= lpColumn
->iImage
;
6434 if (lpColumn
->mask
& LVCF_ORDER
)
6436 lphdi
->mask
|= HDI_ORDER
;
6437 lphdi
->iOrder
= lpColumn
->iOrder
;
6444 * Inserts a new column.
6447 * [I] infoPtr : valid pointer to the listview structure
6448 * [I] nColumn : column index
6449 * [I] lpColumn : column information
6450 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6453 * SUCCESS : new column index
6456 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6457 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6459 COLUMN_INFO
*lpColumnInfo
;
6463 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6465 if (!lpColumn
|| nColumn
< 0) return -1;
6466 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
6468 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6469 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6471 /* insert item in header control */
6472 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
6473 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
6474 (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6475 if (nNewColumn
== -1) return -1;
6476 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
6478 /* create our own column info */
6479 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
6480 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
6482 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
6483 if (!Header_GetItemRect(infoPtr
->hwndHeader
, nNewColumn
, &lpColumnInfo
->rcHeader
)) goto fail
;
6485 /* now we have to actually adjust the data */
6486 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
6488 SUBITEM_INFO
*lpSubItem
;
6492 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
6494 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
6495 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
6497 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
6498 if (lpSubItem
->iSubItem
>= nNewColumn
)
6499 lpSubItem
->iSubItem
++;
6504 /* make space for the new column */
6505 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
6510 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
6513 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
6521 * Sets the attributes of a header item.
6524 * [I] infoPtr : valid pointer to the listview structure
6525 * [I] nColumn : column index
6526 * [I] lpColumn : column attributes
6527 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6533 static BOOL
LISTVIEW_SetColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6534 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6536 HDITEMW hdi
, hdiget
;
6539 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6541 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6543 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6544 if (lpColumn
->mask
& LVCF_FMT
)
6546 hdi
.mask
|= HDI_FORMAT
;
6547 hdiget
.mask
= HDI_FORMAT
;
6548 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdiget
))
6549 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
6551 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6553 /* set header item attributes */
6554 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6555 if (!bResult
) return FALSE
;
6557 if (lpColumn
->mask
& LVCF_FMT
)
6559 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
6560 int oldFmt
= lpColumnInfo
->fmt
;
6562 lpColumnInfo
->fmt
= lpColumn
->fmt
;
6563 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
6565 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6566 if (uView
== LVS_REPORT
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
6575 * Sets the column order array
6578 * [I] infoPtr : valid pointer to the listview structure
6579 * [I] iCount : number of elements in column order array
6580 * [I] lpiArray : pointer to column order array
6586 static BOOL
LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
6588 FIXME("iCount %d lpiArray %p\n", iCount
, lpiArray
);
6599 * Sets the width of a column
6602 * [I] infoPtr : valid pointer to the listview structure
6603 * [I] nColumn : column index
6604 * [I] cx : column width
6610 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
6612 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6613 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
6617 TRACE("(nColumn=%d, cx=%d\n", nColumn
, cx
);
6619 /* set column width only if in report or list mode */
6620 if (uView
!= LVS_REPORT
&& uView
!= LVS_LIST
) return FALSE
;
6622 /* take care of invalid cx values */
6623 if(uView
== LVS_REPORT
&& cx
< -2) cx
= LVSCW_AUTOSIZE
;
6624 else if (uView
== LVS_LIST
&& cx
< 1) return FALSE
;
6626 /* resize all columns if in LVS_LIST mode */
6627 if(uView
== LVS_LIST
)
6629 infoPtr
->nItemWidth
= cx
;
6630 LISTVIEW_InvalidateList(infoPtr
);
6634 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6636 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
6641 lvItem
.mask
= LVIF_TEXT
;
6643 lvItem
.iSubItem
= nColumn
;
6644 lvItem
.pszText
= szDispText
;
6645 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6646 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
6648 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
6649 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
6650 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
6652 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
6653 max_cx
+= infoPtr
->iconSize
.cx
;
6654 max_cx
+= TRAILING_LABEL_PADDING
;
6657 /* autosize based on listview items width */
6658 if(cx
== LVSCW_AUTOSIZE
)
6660 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
6662 /* if iCol is the last column make it fill the remainder of the controls width */
6663 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
6668 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6669 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
6671 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
6675 /* Despite what the MS docs say, if this is not the last
6676 column, then MS resizes the column to the width of the
6677 largest text string in the column, including headers
6678 and items. This is different from LVSCW_AUTOSIZE in that
6679 LVSCW_AUTOSIZE ignores the header string length. */
6682 /* retrieve header text */
6683 hdi
.mask
= HDI_TEXT
;
6684 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
6685 hdi
.pszText
= szDispText
;
6686 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, (LPARAM
)&hdi
))
6688 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
6689 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
6692 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
6693 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
6694 /* FIXME: Take into account the header image, if one is present */
6695 SelectObject(hdc
, old_font
);
6696 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6698 cx
= max (cx
, max_cx
);
6702 if (cx
< 0) return FALSE
;
6704 /* call header to update the column change */
6705 hdi
.mask
= HDI_WIDTH
;
6707 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
6708 return Header_SetItemW(infoPtr
->hwndHeader
, nColumn
, (LPARAM
)&hdi
);
6712 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6715 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO
*infoPtr
)
6718 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
6720 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
6721 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
6724 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
6725 ILC_COLOR
| ILC_MASK
, 2, 2);
6726 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
6727 hdc
= CreateCompatibleDC(hdc_wnd
);
6728 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
6729 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
6730 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
6732 rc
.left
= rc
.top
= 0;
6733 rc
.right
= GetSystemMetrics(SM_CXSMICON
);
6734 rc
.bottom
= GetSystemMetrics(SM_CYSMICON
);
6736 hbm_orig
= SelectObject(hdc
, hbm_mask
);
6737 FillRect(hdc
, &rc
, hbr_white
);
6738 InflateRect(&rc
, -3, -3);
6739 FillRect(hdc
, &rc
, hbr_black
);
6741 SelectObject(hdc
, hbm_im
);
6742 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
6743 SelectObject(hdc
, hbm_orig
);
6744 ImageList_Add(himl
, hbm_im
, hbm_mask
);
6746 SelectObject(hdc
, hbm_im
);
6747 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
6748 SelectObject(hdc
, hbm_orig
);
6749 ImageList_Add(himl
, hbm_im
, hbm_mask
);
6751 DeleteObject(hbm_mask
);
6752 DeleteObject(hbm_im
);
6760 * Sets the extended listview style.
6763 * [I] infoPtr : valid pointer to the listview structure
6765 * [I] dwStyle : style
6768 * SUCCESS : previous style
6771 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD dwMask
, DWORD dwStyle
)
6773 DWORD dwOldStyle
= infoPtr
->dwLvExStyle
;
6777 infoPtr
->dwLvExStyle
= (dwOldStyle
& ~dwMask
) | (dwStyle
& dwMask
);
6779 infoPtr
->dwLvExStyle
= dwStyle
;
6781 if((infoPtr
->dwLvExStyle
^ dwOldStyle
) & LVS_EX_CHECKBOXES
)
6783 HIMAGELIST himl
= 0;
6784 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
6785 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
6786 LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
6794 * Sets the new hot cursor used during hot tracking and hover selection.
6797 * [I] infoPtr : valid pointer to the listview structure
6798 * [I} hCurosr : the new hot cursor handle
6801 * Returns the previous hot cursor
6803 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
6805 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
6807 infoPtr
->hHotCursor
= hCursor
;
6815 * Sets the hot item index.
6818 * [I] infoPtr : valid pointer to the listview structure
6819 * [I] iIndex : index
6822 * SUCCESS : previous hot item index
6823 * FAILURE : -1 (no hot item)
6825 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
6827 INT iOldIndex
= infoPtr
->nHotItem
;
6829 infoPtr
->nHotItem
= iIndex
;
6837 * Sets the amount of time the cursor must hover over an item before it is selected.
6840 * [I] infoPtr : valid pointer to the listview structure
6841 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6844 * Returns the previous hover time
6846 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
6848 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
6850 infoPtr
->dwHoverTime
= dwHoverTime
;
6852 return oldHoverTime
;
6857 * Sets spacing for icons of LVS_ICON style.
6860 * [I] infoPtr : valid pointer to the listview structure
6861 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6862 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6865 * MAKELONG(oldcx, oldcy)
6867 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
6869 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
6870 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6872 TRACE("requested=(%d,%d)\n", cx
, cy
);
6874 /* this is supported only for LVS_ICON style */
6875 if (uView
!= LVS_ICON
) return oldspacing
;
6877 /* set to defaults, if instructed to */
6878 if (cx
== -1) cx
= GetSystemMetrics(SM_CXICONSPACING
);
6879 if (cy
== -1) cy
= GetSystemMetrics(SM_CYICONSPACING
);
6881 /* if 0 then compute width
6882 * FIXME: Should scan each item and determine max width of
6883 * icon or label, then make that the width */
6885 cx
= infoPtr
->iconSpacing
.cx
;
6887 /* if 0 then compute height */
6889 cy
= infoPtr
->iconSize
.cy
+ 2 * infoPtr
->ntmHeight
+
6890 ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_PADDING
;
6893 infoPtr
->iconSpacing
.cx
= cx
;
6894 infoPtr
->iconSpacing
.cy
= cy
;
6896 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6897 LOWORD(oldspacing
), HIWORD(oldspacing
), cx
, cy
,
6898 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
6899 infoPtr
->ntmHeight
);
6901 /* these depend on the iconSpacing */
6902 LISTVIEW_UpdateItemSize(infoPtr
);
6907 inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL small
)
6911 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
6918 size
->cx
= GetSystemMetrics(small
? SM_CXSMICON
: SM_CXICON
);
6919 size
->cy
= GetSystemMetrics(small
? SM_CYSMICON
: SM_CYICON
);
6928 * [I] infoPtr : valid pointer to the listview structure
6929 * [I] nType : image list type
6930 * [I] himl : image list handle
6933 * SUCCESS : old image list
6936 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
6938 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6939 INT oldHeight
= infoPtr
->nItemHeight
;
6940 HIMAGELIST himlOld
= 0;
6942 TRACE("(nType=%d, himl=%p\n", nType
, himl
);
6947 himlOld
= infoPtr
->himlNormal
;
6948 infoPtr
->himlNormal
= himl
;
6949 if (uView
== LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
6950 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
6954 himlOld
= infoPtr
->himlSmall
;
6955 infoPtr
->himlSmall
= himl
;
6956 if (uView
!= LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
6960 himlOld
= infoPtr
->himlState
;
6961 infoPtr
->himlState
= himl
;
6962 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
6963 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
6967 ERR("Unknown icon type=%d\n", nType
);
6971 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
6972 if (infoPtr
->nItemHeight
!= oldHeight
)
6973 LISTVIEW_UpdateScroll(infoPtr
);
6980 * Preallocates memory (does *not* set the actual count of items !)
6983 * [I] infoPtr : valid pointer to the listview structure
6984 * [I] nItems : item count (projected number of items to allocate)
6985 * [I] dwFlags : update flags
6991 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
6993 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems
, dwFlags
);
6995 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
6997 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6998 INT nOldCount
= infoPtr
->nItemCount
;
7000 if (nItems
< nOldCount
)
7002 RANGE range
= { nItems
, nOldCount
};
7003 ranges_del(infoPtr
->selectionRanges
, range
);
7004 if (infoPtr
->nFocusedItem
>= nItems
)
7006 infoPtr
->nFocusedItem
= -1;
7007 SetRectEmpty(&infoPtr
->rcFocus
);
7011 infoPtr
->nItemCount
= nItems
;
7012 LISTVIEW_UpdateScroll(infoPtr
);
7014 /* the flags are valid only in ownerdata report and list modes */
7015 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
) dwFlags
= 0;
7017 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
7018 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
7020 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
7021 LISTVIEW_InvalidateList(infoPtr
);
7028 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7029 nFrom
= min(nOldCount
, nItems
);
7030 nTo
= max(nOldCount
, nItems
);
7032 if (uView
== LVS_REPORT
)
7035 rcErase
.top
= nFrom
* infoPtr
->nItemHeight
;
7036 rcErase
.right
= infoPtr
->nItemWidth
;
7037 rcErase
.bottom
= nTo
* infoPtr
->nItemHeight
;
7038 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7039 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7040 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7044 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
7046 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
7047 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
7048 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
7049 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7050 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7051 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7052 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7054 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7056 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7057 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7058 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7059 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7060 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7066 /* According to MSDN for non-LVS_OWNERDATA this is just
7067 * a performance issue. The control allocates its internal
7068 * data structures for the number of items specified. It
7069 * cuts down on the number of memory allocations. Therefore
7070 * we will just issue a WARN here
7072 WARN("for non-ownerdata performance option not implemented.\n");
7080 * Sets the position of an item.
7083 * [I] infoPtr : valid pointer to the listview structure
7084 * [I] nItem : item index
7085 * [I] pt : coordinate
7091 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, POINT pt
)
7093 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7096 TRACE("(nItem=%d, &pt=%s\n", nItem
, debugpoint(&pt
));
7098 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
7099 !(uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)) return FALSE
;
7101 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7103 /* This point value seems to be an undocumented feature.
7104 * The best guess is that it means either at the origin,
7105 * or at true beginning of the list. I will assume the origin. */
7106 if ((pt
.x
== -1) && (pt
.y
== -1))
7109 if (uView
== LVS_ICON
)
7111 pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
7112 pt
.y
-= ICON_TOP_PADDING
;
7117 infoPtr
->bAutoarrange
= FALSE
;
7119 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, FALSE
);
7124 * Sets the state of one or many items.
7127 * [I] infoPtr : valid pointer to the listview structure
7128 * [I] nItem : item index
7129 * [I] lpLVItem : item or subitem info
7135 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
)
7137 BOOL bResult
= TRUE
;
7140 lvItem
.iItem
= nItem
;
7141 lvItem
.iSubItem
= 0;
7142 lvItem
.mask
= LVIF_STATE
;
7143 lvItem
.state
= lpLVItem
->state
;
7144 lvItem
.stateMask
= lpLVItem
->stateMask
;
7145 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
7149 /* apply to all items */
7150 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7151 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) bResult
= FALSE
;
7154 bResult
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
7157 * Update selection mark
7159 * Investigation on windows 2k showed that selection mark was updated
7160 * whenever a new selection was made, but if the selected item was
7161 * unselected it was not updated.
7163 * we are probably still not 100% accurate, but this at least sets the
7164 * proper selection mark when it is needed
7167 if (bResult
&& (lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) &&
7168 ((infoPtr
->nSelectionMark
== -1) || (lvItem
.iItem
<= infoPtr
->nSelectionMark
)))
7171 infoPtr
->nSelectionMark
= -1;
7172 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
7174 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
7176 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
7178 infoPtr
->nSelectionMark
= i
;
7182 else if (ranges_contain(infoPtr
->selectionRanges
, i
))
7184 infoPtr
->nSelectionMark
= i
;
7195 * Sets the text of an item or subitem.
7198 * [I] hwnd : window handle
7199 * [I] nItem : item index
7200 * [I] lpLVItem : item or subitem info
7201 * [I] isW : TRUE if input is Unicode
7207 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
7211 if (nItem
< 0 && nItem
>= infoPtr
->nItemCount
) return FALSE
;
7213 lvItem
.iItem
= nItem
;
7214 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
7215 lvItem
.mask
= LVIF_TEXT
;
7216 lvItem
.pszText
= lpLVItem
->pszText
;
7217 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
7219 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
7221 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
7226 * Set item index that marks the start of a multiple selection.
7229 * [I] infoPtr : valid pointer to the listview structure
7230 * [I] nIndex : index
7233 * Index number or -1 if there is no selection mark.
7235 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
7237 INT nOldIndex
= infoPtr
->nSelectionMark
;
7239 TRACE("(nIndex=%d)\n", nIndex
);
7241 infoPtr
->nSelectionMark
= nIndex
;
7248 * Sets the text background color.
7251 * [I] infoPtr : valid pointer to the listview structure
7252 * [I] clrTextBk : text background color
7258 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrTextBk
)
7260 TRACE("(clrTextBk=%lx)\n", clrTextBk
);
7262 if (infoPtr
->clrTextBk
!= clrTextBk
)
7264 infoPtr
->clrTextBk
= clrTextBk
;
7265 LISTVIEW_InvalidateList(infoPtr
);
7273 * Sets the text foreground color.
7276 * [I] infoPtr : valid pointer to the listview structure
7277 * [I] clrText : text color
7283 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF clrText
)
7285 TRACE("(clrText=%lx)\n", clrText
);
7287 if (infoPtr
->clrText
!= clrText
)
7289 infoPtr
->clrText
= clrText
;
7290 LISTVIEW_InvalidateList(infoPtr
);
7298 * Determines which listview item is located at the specified position.
7301 * [I] infoPtr : valid pointer to the listview structure
7302 * [I] hwndNewToolTip : handle to new ToolTip
7307 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
7309 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
7310 infoPtr
->hwndToolTip
= hwndNewToolTip
;
7311 return hwndOldToolTip
;
7314 /* LISTVIEW_SetUnicodeFormat */
7315 /* LISTVIEW_SetWorkAreas */
7319 * Callback internally used by LISTVIEW_SortItems()
7322 * [I] first : pointer to first ITEM_INFO to compare
7323 * [I] second : pointer to second ITEM_INFO to compare
7324 * [I] lParam : HWND of control
7327 * if first comes before second : negative
7328 * if first comes after second : positive
7329 * if first and second are equivalent : zero
7331 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
7333 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
7334 ITEM_INFO
* lv_first
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)first
, 0 );
7335 ITEM_INFO
* lv_second
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)second
, 0 );
7337 /* Forward the call to the client defined callback */
7338 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
7343 * Sorts the listview items.
7346 * [I] infoPtr : valid pointer to the listview structure
7347 * [I] pfnCompare : application-defined value
7348 * [I] lParamSort : pointer to comparision callback
7354 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
, LPARAM lParamSort
)
7356 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7359 LPVOID selectionMarkItem
;
7363 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
7365 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
7367 if (!pfnCompare
) return FALSE
;
7368 if (!infoPtr
->hdpaItems
) return FALSE
;
7370 /* if there are 0 or 1 items, there is no need to sort */
7371 if (infoPtr
->nItemCount
< 2) return TRUE
;
7373 if (infoPtr
->nFocusedItem
>= 0)
7375 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
7376 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
7377 if (lpItem
) lpItem
->state
|= LVIS_FOCUSED
;
7379 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7380 /* clear the lpItem->state for non-selected ones */
7381 /* remove the selection ranges */
7383 infoPtr
->pfnCompare
= pfnCompare
;
7384 infoPtr
->lParamSort
= lParamSort
;
7385 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
7387 /* Adjust selections and indices so that they are the way they should
7388 * be after the sort (otherwise, the list items move around, but
7389 * whatever is at the item's previous original position will be
7392 selectionMarkItem
=(infoPtr
->nSelectionMark
>=0)?DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
):NULL
;
7393 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
7395 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
7396 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
7398 if (lpItem
->state
& LVIS_SELECTED
)
7400 item
.state
= LVIS_SELECTED
;
7401 item
.stateMask
= LVIS_SELECTED
;
7402 LISTVIEW_SetItemState(infoPtr
, i
, &item
);
7404 if (lpItem
->state
& LVIS_FOCUSED
)
7406 infoPtr
->nFocusedItem
= i
;
7407 lpItem
->state
&= ~LVIS_FOCUSED
;
7410 if (selectionMarkItem
!= NULL
)
7411 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
7412 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7414 /* refresh the display */
7415 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
)
7416 LISTVIEW_InvalidateList(infoPtr
);
7423 * Updates an items or rearranges the listview control.
7426 * [I] infoPtr : valid pointer to the listview structure
7427 * [I] nItem : item index
7433 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
7435 TRACE("(nItem=%d)\n", nItem
);
7437 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
7439 /* rearrange with default alignment style */
7440 if (is_autoarrange(infoPtr
))
7441 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
7443 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
7451 * Creates the listview control.
7454 * [I] hwnd : window handle
7455 * [I] lpcs : the create parameters
7461 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
7463 LISTVIEW_INFO
*infoPtr
;
7464 UINT uView
= lpcs
->style
& LVS_TYPEMASK
;
7467 TRACE("(lpcs=%p)\n", lpcs
);
7469 /* initialize info pointer */
7470 infoPtr
= (LISTVIEW_INFO
*)Alloc(sizeof(LISTVIEW_INFO
));
7471 if (!infoPtr
) return -1;
7473 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
7475 infoPtr
->hwndSelf
= hwnd
;
7476 infoPtr
->dwStyle
= lpcs
->style
;
7477 /* determine the type of structures to use */
7478 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
7479 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
7480 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
7482 /* initialize color information */
7483 infoPtr
->clrBk
= CLR_NONE
;
7484 infoPtr
->clrText
= comctl32_color
.clrWindowText
;
7485 infoPtr
->clrTextBk
= CLR_DEFAULT
;
7486 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
7488 /* set default values */
7489 infoPtr
->nFocusedItem
= -1;
7490 infoPtr
->nSelectionMark
= -1;
7491 infoPtr
->nHotItem
= -1;
7492 infoPtr
->bRedraw
= TRUE
;
7493 infoPtr
->bNoItemMetrics
= TRUE
;
7494 infoPtr
->bDoChangeNotify
= TRUE
;
7495 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
7496 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
7497 infoPtr
->nEditLabelItem
= -1;
7498 infoPtr
->dwHoverTime
= -1; /* default system hover time */
7500 /* get default font (icon title) */
7501 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
7502 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
7503 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
7504 LISTVIEW_SaveTextMetrics(infoPtr
);
7507 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
,
7508 WS_CHILD
| HDS_HORZ
| (DWORD
)((LVS_NOSORTHEADER
& lpcs
->style
)?0:HDS_BUTTONS
),
7509 0, 0, 0, 0, hwnd
, NULL
,
7510 lpcs
->hInstance
, NULL
);
7511 if (!infoPtr
->hwndHeader
) goto fail
;
7513 /* set header unicode format */
7514 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, (WPARAM
)TRUE
, (LPARAM
)NULL
);
7516 /* set header font */
7517 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, (LPARAM
)TRUE
);
7519 /* allocate memory for the data structure */
7520 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
7521 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
7522 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
7523 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
7524 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
7526 /* initialize the icon sizes */
7527 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, uView
!= LVS_ICON
);
7528 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
7530 /* init item size to avoid division by 0 */
7531 LISTVIEW_UpdateItemSize (infoPtr
);
7533 if (uView
== LVS_REPORT
)
7535 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
))
7537 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
7541 /* set HDS_HIDDEN flag to hide the header bar */
7542 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
,
7543 GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
) | HDS_HIDDEN
);
7550 DestroyWindow(infoPtr
->hwndHeader
);
7551 ranges_destroy(infoPtr
->selectionRanges
);
7552 DPA_Destroy(infoPtr
->hdpaItems
);
7553 DPA_Destroy(infoPtr
->hdpaPosX
);
7554 DPA_Destroy(infoPtr
->hdpaPosY
);
7555 DPA_Destroy(infoPtr
->hdpaColumns
);
7562 * Enables the listview control.
7565 * [I] infoPtr : valid pointer to the listview structure
7566 * [I] bEnable : specifies whether to enable or disable the window
7572 static BOOL
LISTVIEW_Enable(LISTVIEW_INFO
*infoPtr
, BOOL bEnable
)
7574 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
7575 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
7581 * Erases the background of the listview control.
7584 * [I] infoPtr : valid pointer to the listview structure
7585 * [I] hdc : device context handle
7591 static inline BOOL
LISTVIEW_EraseBkgnd(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
7595 TRACE("(hdc=%p)\n", hdc
);
7597 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
7599 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
7605 * Helper function for LISTVIEW_[HV]Scroll *only*.
7606 * Performs vertical/horizontal scrolling by a give amount.
7609 * [I] infoPtr : valid pointer to the listview structure
7610 * [I] dx : amount of horizontal scroll
7611 * [I] dy : amount of vertical scroll
7613 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
7615 /* now we can scroll the list */
7616 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
7617 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
7618 /* if we have focus, adjust rect */
7619 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
7620 UpdateWindow(infoPtr
->hwndSelf
);
7625 * Performs vertical scrolling.
7628 * [I] infoPtr : valid pointer to the listview structure
7629 * [I] nScrollCode : scroll code
7630 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7631 * [I] hScrollWnd : scrollbar control window handle
7637 * SB_LINEUP/SB_LINEDOWN:
7638 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7639 * for LVS_REPORT is 1 line
7640 * for LVS_LIST cannot occur
7643 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
7644 INT nScrollDiff
, HWND hScrollWnd
)
7646 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7647 INT nOldScrollPos
, nNewScrollPos
;
7648 SCROLLINFO scrollInfo
;
7651 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
7652 debugscrollcode(nScrollCode
), nScrollDiff
);
7654 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
7656 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7657 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
7659 is_an_icon
= ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
));
7661 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
7663 nOldScrollPos
= scrollInfo
.nPos
;
7664 switch (nScrollCode
)
7670 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
7674 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
7678 nScrollDiff
= -scrollInfo
.nPage
;
7682 nScrollDiff
= scrollInfo
.nPage
;
7685 case SB_THUMBPOSITION
:
7687 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
7694 /* quit right away if pos isn't changing */
7695 if (nScrollDiff
== 0) return 0;
7697 /* calculate new position, and handle overflows */
7698 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
7699 if (nScrollDiff
> 0) {
7700 if (nNewScrollPos
< nOldScrollPos
||
7701 nNewScrollPos
> scrollInfo
.nMax
)
7702 nNewScrollPos
= scrollInfo
.nMax
;
7704 if (nNewScrollPos
> nOldScrollPos
||
7705 nNewScrollPos
< scrollInfo
.nMin
)
7706 nNewScrollPos
= scrollInfo
.nMin
;
7709 /* set the new position, and reread in case it changed */
7710 scrollInfo
.fMask
= SIF_POS
;
7711 scrollInfo
.nPos
= nNewScrollPos
;
7712 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
7714 /* carry on only if it really changed */
7715 if (nNewScrollPos
== nOldScrollPos
) return 0;
7717 /* now adjust to client coordinates */
7718 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
7719 if (uView
== LVS_REPORT
) nScrollDiff
*= infoPtr
->nItemHeight
;
7721 /* and scroll the window */
7722 scroll_list(infoPtr
, 0, nScrollDiff
);
7729 * Performs horizontal scrolling.
7732 * [I] infoPtr : valid pointer to the listview structure
7733 * [I] nScrollCode : scroll code
7734 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7735 * [I] hScrollWnd : scrollbar control window handle
7741 * SB_LINELEFT/SB_LINERIGHT:
7742 * for LVS_ICON, LVS_SMALLICON 1 pixel
7743 * for LVS_REPORT is 1 pixel
7744 * for LVS_LIST is 1 column --> which is a 1 because the
7745 * scroll is based on columns not pixels
7748 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
7749 INT nScrollDiff
, HWND hScrollWnd
)
7751 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7752 INT nOldScrollPos
, nNewScrollPos
;
7753 SCROLLINFO scrollInfo
;
7755 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
7756 debugscrollcode(nScrollCode
), nScrollDiff
);
7758 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
7760 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7761 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
7763 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
7765 nOldScrollPos
= scrollInfo
.nPos
;
7767 switch (nScrollCode
)
7781 nScrollDiff
= -scrollInfo
.nPage
;
7785 nScrollDiff
= scrollInfo
.nPage
;
7788 case SB_THUMBPOSITION
:
7790 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
7797 /* quit right away if pos isn't changing */
7798 if (nScrollDiff
== 0) return 0;
7800 /* calculate new position, and handle overflows */
7801 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
7802 if (nScrollDiff
> 0) {
7803 if (nNewScrollPos
< nOldScrollPos
||
7804 nNewScrollPos
> scrollInfo
.nMax
)
7805 nNewScrollPos
= scrollInfo
.nMax
;
7807 if (nNewScrollPos
> nOldScrollPos
||
7808 nNewScrollPos
< scrollInfo
.nMin
)
7809 nNewScrollPos
= scrollInfo
.nMin
;
7812 /* set the new position, and reread in case it changed */
7813 scrollInfo
.fMask
= SIF_POS
;
7814 scrollInfo
.nPos
= nNewScrollPos
;
7815 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
7817 /* carry on only if it really changed */
7818 if (nNewScrollPos
== nOldScrollPos
) return 0;
7820 if(uView
== LVS_REPORT
)
7821 LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
7823 /* now adjust to client coordinates */
7824 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
7825 if (uView
== LVS_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
7827 /* and scroll the window */
7828 scroll_list(infoPtr
, nScrollDiff
, 0);
7833 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
7835 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7836 INT gcWheelDelta
= 0;
7837 INT pulScrollLines
= 3;
7838 SCROLLINFO scrollInfo
;
7840 TRACE("(wheelDelta=%d)\n", wheelDelta
);
7842 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
7843 gcWheelDelta
-= wheelDelta
;
7845 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7846 scrollInfo
.fMask
= SIF_POS
;
7853 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7854 * should be fixed in the future.
7856 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (gcWheelDelta
< 0) ?
7857 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
, 0);
7861 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
7863 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
7864 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
7865 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, cLineScroll
, 0);
7870 LISTVIEW_HScroll(infoPtr
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0, 0);
7881 * [I] infoPtr : valid pointer to the listview structure
7882 * [I] nVirtualKey : virtual key
7883 * [I] lKeyData : key data
7888 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
7890 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7892 NMLVKEYDOWN nmKeyDown
;
7894 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey
, lKeyData
);
7896 /* send LVN_KEYDOWN notification */
7897 nmKeyDown
.wVKey
= nVirtualKey
;
7898 nmKeyDown
.flags
= 0;
7899 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
7901 switch (nVirtualKey
)
7904 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
7906 notify(infoPtr
, NM_RETURN
);
7907 notify(infoPtr
, LVN_ITEMACTIVATE
);
7912 if (infoPtr
->nItemCount
> 0)
7917 if (infoPtr
->nItemCount
> 0)
7918 nItem
= infoPtr
->nItemCount
- 1;
7922 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
7926 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
7930 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
7934 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
7938 if (uView
== LVS_REPORT
)
7939 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
);
7941 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
7942 * LISTVIEW_GetCountPerRow(infoPtr
);
7943 if(nItem
< 0) nItem
= 0;
7947 if (uView
== LVS_REPORT
)
7948 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
);
7950 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
7951 * LISTVIEW_GetCountPerRow(infoPtr
);
7952 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
7956 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
))
7957 LISTVIEW_KeySelection(infoPtr
, nItem
);
7967 * [I] infoPtr : valid pointer to the listview structure
7972 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
7976 /* if we did not have the focus, there's nothing to do */
7977 if (!infoPtr
->bFocus
) return 0;
7979 /* send NM_KILLFOCUS notification */
7980 notify(infoPtr
, NM_KILLFOCUS
);
7982 /* if we have a focus rectagle, get rid of it */
7983 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
7985 /* set window focus flag */
7986 infoPtr
->bFocus
= FALSE
;
7988 /* invalidate the selected items before reseting focus flag */
7989 LISTVIEW_InvalidateSelectedItems(infoPtr
);
7996 * Processes double click messages (left mouse button).
7999 * [I] infoPtr : valid pointer to the listview structure
8000 * [I] wKey : key flag
8001 * [I] x,y : mouse coordinate
8006 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8008 LVHITTESTINFO htInfo
;
8010 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8012 /* send NM_RELEASEDCAPTURE notification */
8013 notify(infoPtr
, NM_RELEASEDCAPTURE
);
8018 /* send NM_DBLCLK notification */
8019 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
8020 notify_click(infoPtr
, NM_DBLCLK
, &htInfo
);
8022 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8023 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
8030 * Processes mouse down messages (left mouse button).
8033 * [I] infoPtr : valid pointer to the listview structure
8034 * [I] wKey : key flag
8035 * [I] x,y : mouse coordinate
8040 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8042 LVHITTESTINFO lvHitTestInfo
;
8043 static BOOL bGroupSelect
= TRUE
;
8044 POINT pt
= { x
, y
};
8047 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8049 /* send NM_RELEASEDCAPTURE notification */
8050 notify(infoPtr
, NM_RELEASEDCAPTURE
);
8052 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
8054 /* set left button down flag and record the click position */
8055 infoPtr
->bLButtonDown
= TRUE
;
8056 infoPtr
->ptClickPos
= pt
;
8058 lvHitTestInfo
.pt
.x
= x
;
8059 lvHitTestInfo
.pt
.y
= y
;
8061 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8062 TRACE("at %s, nItem=%d\n", debugpoint(&pt
), nItem
);
8063 infoPtr
->nEditLabelItem
= -1;
8064 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8066 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
8068 DWORD state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_STATEIMAGEMASK
) >> 12;
8069 if(state
== 1 || state
== 2)
8073 lvitem
.state
= state
<< 12;
8074 lvitem
.stateMask
= LVIS_STATEIMAGEMASK
;
8075 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvitem
);
8080 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
8082 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8083 infoPtr
->nEditLabelItem
= nItem
;
8085 LISTVIEW_SetSelection(infoPtr
, nItem
);
8089 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
8093 LISTVIEW_AddGroupSelection(infoPtr
, nItem
);
8094 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8095 infoPtr
->nSelectionMark
= nItem
;
8101 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
8102 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8104 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
8105 infoPtr
->nSelectionMark
= nItem
;
8108 else if (wKey
& MK_CONTROL
)
8112 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
8114 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
8115 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8116 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
8117 infoPtr
->nSelectionMark
= nItem
;
8119 else if (wKey
& MK_SHIFT
)
8121 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
8125 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8126 infoPtr
->nEditLabelItem
= nItem
;
8128 /* set selection (clears other pre-existing selections) */
8129 LISTVIEW_SetSelection(infoPtr
, nItem
);
8135 /* remove all selections */
8136 LISTVIEW_DeselectAll(infoPtr
);
8145 * Processes mouse up messages (left mouse button).
8148 * [I] infoPtr : valid pointer to the listview structure
8149 * [I] wKey : key flag
8150 * [I] x,y : mouse coordinate
8155 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8157 LVHITTESTINFO lvHitTestInfo
;
8159 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8161 if (!infoPtr
->bLButtonDown
) return 0;
8163 lvHitTestInfo
.pt
.x
= x
;
8164 lvHitTestInfo
.pt
.y
= y
;
8166 /* send NM_CLICK notification */
8167 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8168 notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
);
8170 /* set left button flag */
8171 infoPtr
->bLButtonDown
= FALSE
;
8173 /* if we clicked on a selected item, edit the label */
8174 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
8175 LISTVIEW_EditLabelT(infoPtr
, lvHitTestInfo
.iItem
, TRUE
);
8182 * Destroys the listview control (called after WM_DESTROY).
8185 * [I] infoPtr : valid pointer to the listview structure
8190 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
8194 /* delete all items */
8195 LISTVIEW_DeleteAllItems(infoPtr
);
8197 /* destroy data structure */
8198 DPA_Destroy(infoPtr
->hdpaItems
);
8199 DPA_Destroy(infoPtr
->hdpaPosX
);
8200 DPA_Destroy(infoPtr
->hdpaPosY
);
8201 DPA_Destroy(infoPtr
->hdpaColumns
);
8202 ranges_destroy(infoPtr
->selectionRanges
);
8204 /* destroy image lists */
8205 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
8207 if (infoPtr
->himlNormal
)
8208 ImageList_Destroy(infoPtr
->himlNormal
);
8209 if (infoPtr
->himlSmall
)
8210 ImageList_Destroy(infoPtr
->himlSmall
);
8211 if (infoPtr
->himlState
)
8212 ImageList_Destroy(infoPtr
->himlState
);
8215 /* destroy font, bkgnd brush */
8217 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
8218 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
8220 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
8222 /* free listview info pointer*/
8230 * Handles notifications from header.
8233 * [I] infoPtr : valid pointer to the listview structure
8234 * [I] nCtrlId : control identifier
8235 * [I] lpnmh : notification information
8240 static LRESULT
LISTVIEW_HeaderNotification(LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
8242 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8244 TRACE("(lpnmh=%p)\n", lpnmh
);
8246 if (!lpnmh
|| lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
8248 switch (lpnmh
->hdr
.code
)
8252 case HDN_ITEMCHANGEDW
:
8253 case HDN_ITEMCHANGEDA
:
8255 COLUMN_INFO
*lpColumnInfo
;
8258 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8262 hdi
.mask
= HDI_WIDTH
;
8263 if (!Header_GetItemW(infoPtr
->hwndHeader
, lpnmh
->iItem
, (LPARAM
)&hdi
)) return 0;
8267 cxy
= lpnmh
->pitem
->cxy
;
8269 /* determine how much we change since the last know position */
8270 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8271 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
8274 RECT rcCol
= lpColumnInfo
->rcHeader
;
8276 lpColumnInfo
->rcHeader
.right
+= dx
;
8277 LISTVIEW_ScrollColumns(infoPtr
, lpnmh
->iItem
+ 1, dx
);
8278 LISTVIEW_UpdateItemSize(infoPtr
);
8279 if (uView
== LVS_REPORT
&& is_redrawing(infoPtr
))
8281 /* this trick works for left aligned columns only */
8282 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
8284 rcCol
.right
= min (rcCol
.right
, lpColumnInfo
->rcHeader
.right
);
8285 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- 3 * infoPtr
->ntmAveCharWidth
);
8287 rcCol
.top
= infoPtr
->rcList
.top
;
8288 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
8289 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
8295 case HDN_ITEMCLICKW
:
8296 case HDN_ITEMCLICKA
:
8298 /* Handle sorting by Header Column */
8301 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
8303 nmlv
.iSubItem
= lpnmh
->iItem
;
8304 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
8308 case HDN_DIVIDERDBLCLICKW
:
8309 case HDN_DIVIDERDBLCLICKA
:
8310 LISTVIEW_SetColumnWidth(infoPtr
, lpnmh
->iItem
, LVSCW_AUTOSIZE
);
8319 * Determines the type of structure to use.
8322 * [I] infoPtr : valid pointer to the listview structureof the sender
8323 * [I] hwndFrom : listview window handle
8324 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8329 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
8331 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
8333 if (nCommand
!= NF_REQUERY
) return 0;
8335 infoPtr
->notifyFormat
= SendMessageW(hwndFrom
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
8342 * Paints/Repaints the listview control.
8345 * [I] infoPtr : valid pointer to the listview structure
8346 * [I] hdc : device context handle
8351 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
8353 TRACE("(hdc=%p)\n", hdc
);
8355 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
8357 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8359 infoPtr
->bNoItemMetrics
= FALSE
;
8360 LISTVIEW_UpdateItemSize(infoPtr
);
8361 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
8362 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8363 LISTVIEW_UpdateScroll(infoPtr
);
8366 LISTVIEW_Refresh(infoPtr
, hdc
);
8371 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
8373 if (ps
.fErase
) LISTVIEW_FillBkgnd(infoPtr
, hdc
, &ps
.rcPaint
);
8374 LISTVIEW_Refresh(infoPtr
, hdc
);
8375 EndPaint(infoPtr
->hwndSelf
, &ps
);
8384 * Paints/Repaints the listview control.
8387 * [I] infoPtr : valid pointer to the listview structure
8388 * [I] hdc : device context handle
8389 * [I] options : drawing options
8394 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
8396 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc
, options
);
8398 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
8401 if (options
& PRF_ERASEBKGND
)
8402 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
8404 if (options
& PRF_CLIENT
)
8405 LISTVIEW_Paint(infoPtr
, hdc
);
8413 * Processes double click messages (right mouse button).
8416 * [I] infoPtr : valid pointer to the listview structure
8417 * [I] wKey : key flag
8418 * [I] x,y : mouse coordinate
8423 static LRESULT
LISTVIEW_RButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8425 LVHITTESTINFO lvHitTestInfo
;
8427 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
8429 /* send NM_RELEASEDCAPTURE notification */
8430 notify(infoPtr
, NM_RELEASEDCAPTURE
);
8432 /* send NM_RDBLCLK notification */
8433 lvHitTestInfo
.pt
.x
= x
;
8434 lvHitTestInfo
.pt
.y
= y
;
8435 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8436 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
8443 * Processes mouse down messages (right mouse button).
8446 * [I] infoPtr : valid pointer to the listview structure
8447 * [I] wKey : key flag
8448 * [I] x,y : mouse coordinate
8453 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8455 LVHITTESTINFO lvHitTestInfo
;
8458 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
8460 /* send NM_RELEASEDCAPTURE notification */
8461 notify(infoPtr
, NM_RELEASEDCAPTURE
);
8463 /* make sure the listview control window has the focus */
8464 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
8466 /* set right button down flag */
8467 infoPtr
->bRButtonDown
= TRUE
;
8469 /* determine the index of the selected item */
8470 lvHitTestInfo
.pt
.x
= x
;
8471 lvHitTestInfo
.pt
.y
= y
;
8472 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8474 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8476 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8477 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
8478 !LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8479 LISTVIEW_SetSelection(infoPtr
, nItem
);
8483 LISTVIEW_DeselectAll(infoPtr
);
8491 * Processes mouse up messages (right mouse button).
8494 * [I] infoPtr : valid pointer to the listview structure
8495 * [I] wKey : key flag
8496 * [I] x,y : mouse coordinate
8501 static LRESULT
LISTVIEW_RButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8503 LVHITTESTINFO lvHitTestInfo
;
8506 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
8508 if (!infoPtr
->bRButtonDown
) return 0;
8510 /* set button flag */
8511 infoPtr
->bRButtonDown
= FALSE
;
8513 /* Send NM_RClICK notification */
8514 lvHitTestInfo
.pt
.x
= x
;
8515 lvHitTestInfo
.pt
.y
= y
;
8516 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8517 notify_click(infoPtr
, NM_RCLICK
, &lvHitTestInfo
);
8519 /* Change to screen coordinate for WM_CONTEXTMENU */
8520 pt
= lvHitTestInfo
.pt
;
8521 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
8523 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8524 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
8525 (WPARAM
)infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
8536 * [I] infoPtr : valid pointer to the listview structure
8537 * [I] hwnd : window handle of window containing the cursor
8538 * [I] nHittest : hit-test code
8539 * [I] wMouseMsg : ideintifier of the mouse message
8542 * TRUE if cursor is set
8545 static BOOL
LISTVIEW_SetCursor(LISTVIEW_INFO
*infoPtr
, HWND hwnd
, UINT nHittest
, UINT wMouseMsg
)
8547 LVHITTESTINFO lvHitTestInfo
;
8549 if(!(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)) return FALSE
;
8551 if(!infoPtr
->hHotCursor
) return FALSE
;
8553 GetCursorPos(&lvHitTestInfo
.pt
);
8554 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) return FALSE
;
8556 SetCursor(infoPtr
->hHotCursor
);
8566 * [I] infoPtr : valid pointer to the listview structure
8567 * [I] hwndLoseFocus : handle of previously focused window
8572 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
8574 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
8576 /* if we have the focus already, there's nothing to do */
8577 if (infoPtr
->bFocus
) return 0;
8579 /* send NM_SETFOCUS notification */
8580 notify(infoPtr
, NM_SETFOCUS
);
8582 /* set window focus flag */
8583 infoPtr
->bFocus
= TRUE
;
8585 /* put the focus rect back on */
8586 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
8588 /* redraw all visible selected items */
8589 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8599 * [I] infoPtr : valid pointer to the listview structure
8600 * [I] fRedraw : font handle
8601 * [I] fRedraw : redraw flag
8606 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
8608 HFONT oldFont
= infoPtr
->hFont
;
8610 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
8612 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
8613 if (infoPtr
->hFont
== oldFont
) return 0;
8615 LISTVIEW_SaveTextMetrics(infoPtr
);
8617 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
)
8618 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
8620 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
8627 * Message handling for WM_SETREDRAW.
8628 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8631 * [I] infoPtr : valid pointer to the listview structure
8632 * [I] bRedraw: state of redraw flag
8635 * DefWinProc return value
8637 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
8639 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr
->bRedraw
, bRedraw
);
8641 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
8642 if ((infoPtr
->bRedraw
&& bRedraw
) || (!infoPtr
->bRedraw
&& !bRedraw
)) return 0;
8644 infoPtr
->bRedraw
= bRedraw
;
8646 if(!bRedraw
) return 0;
8648 if (is_autoarrange(infoPtr
))
8649 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8650 LISTVIEW_UpdateScroll(infoPtr
);
8652 /* despite what the WM_SETREDRAW docs says, apps expect us
8653 * to invalidate the listview here... stupid! */
8654 LISTVIEW_InvalidateList(infoPtr
);
8661 * Resizes the listview control. This function processes WM_SIZE
8662 * messages. At this time, the width and height are not used.
8665 * [I] infoPtr : valid pointer to the listview structure
8666 * [I] Width : new width
8667 * [I] Height : new height
8672 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
8674 RECT rcOld
= infoPtr
->rcList
;
8676 TRACE("(width=%d, height=%d)\n", Width
, Height
);
8678 LISTVIEW_UpdateSize(infoPtr
);
8679 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
8681 /* do not bother with display related stuff if we're not redrawing */
8682 if (!is_redrawing(infoPtr
)) return 0;
8684 if (is_autoarrange(infoPtr
))
8685 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8687 LISTVIEW_UpdateScroll(infoPtr
);
8689 /* refresh all only for lists whose height changed significantly */
8690 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
&&
8691 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
8692 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
8693 LISTVIEW_InvalidateList(infoPtr
);
8700 * Sets the size information.
8703 * [I] infoPtr : valid pointer to the listview structure
8708 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
8710 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8712 TRACE("uView=%d, rcList(old)=%s\n", uView
, debugrect(&infoPtr
->rcList
));
8714 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
8716 if (uView
== LVS_LIST
)
8718 /* Apparently the "LIST" style is supposed to have the same
8719 * number of items in a column even if there is no scroll bar.
8720 * Since if a scroll bar already exists then the bottom is already
8721 * reduced, only reduce if the scroll bar does not currently exist.
8722 * The "2" is there to mimic the native control. I think it may be
8723 * related to either padding or edges. (GLA 7/2002)
8725 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_HSCROLL
))
8726 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
8727 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
8729 else if (uView
== LVS_REPORT
&& !(infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
))
8734 hl
.prc
= &infoPtr
->rcList
;
8736 Header_Layout(infoPtr
->hwndHeader
, &hl
);
8738 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
8740 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
8743 TRACE(" rcList=%s\n", debugrect(&infoPtr
->rcList
));
8748 * Processes WM_STYLECHANGED messages.
8751 * [I] infoPtr : valid pointer to the listview structure
8752 * [I] wStyleType : window style type (normal or extended)
8753 * [I] lpss : window style information
8758 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
8759 const STYLESTRUCT
*lpss
)
8761 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
8762 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
8764 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8765 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
8767 if (wStyleType
!= GWL_STYLE
) return 0;
8769 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8770 /* what if LVS_OWNERDATA changed? */
8771 /* or LVS_SINGLESEL */
8772 /* or LVS_SORT{AS,DES}CENDING */
8774 infoPtr
->dwStyle
= lpss
->styleNew
;
8776 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
8777 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
8778 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
8780 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
8781 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
8782 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
8784 if (uNewView
!= uOldView
)
8786 SIZE oldIconSize
= infoPtr
->iconSize
;
8789 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8790 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
8792 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
8793 SetRectEmpty(&infoPtr
->rcFocus
);
8795 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
8796 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
8798 if (uNewView
== LVS_ICON
)
8800 if ((infoPtr
->iconSize
.cx
!= oldIconSize
.cx
) || (infoPtr
->iconSize
.cy
!= oldIconSize
.cy
))
8802 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8803 oldIconSize
.cx
, oldIconSize
.cy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
8804 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
8807 else if (uNewView
== LVS_REPORT
)
8812 hl
.prc
= &infoPtr
->rcList
;
8814 Header_Layout(infoPtr
->hwndHeader
, &hl
);
8815 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
8818 LISTVIEW_UpdateItemSize(infoPtr
);
8821 if (uNewView
== LVS_REPORT
)
8822 ShowWindow(infoPtr
->hwndHeader
, (lpss
->styleNew
& LVS_NOCOLUMNHEADER
) ? SW_HIDE
: SW_SHOWNORMAL
);
8824 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
8825 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
8826 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8828 /* update the size of the client area */
8829 LISTVIEW_UpdateSize(infoPtr
);
8831 /* add scrollbars if needed */
8832 LISTVIEW_UpdateScroll(infoPtr
);
8834 /* invalidate client area + erase background */
8835 LISTVIEW_InvalidateList(infoPtr
);
8842 * Window procedure of the listview control.
8845 static LRESULT WINAPI
8846 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
8848 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
8850 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg
, wParam
, lParam
);
8852 if (!infoPtr
&& (uMsg
!= WM_CREATE
))
8853 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
8857 case LVM_APPROXIMATEVIEWRECT
:
8858 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
8859 LOWORD(lParam
), HIWORD(lParam
));
8861 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
8863 /* case LVM_CANCELEDITLABEL: */
8865 case LVM_CREATEDRAGIMAGE
:
8866 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
8868 case LVM_DELETEALLITEMS
:
8869 return LISTVIEW_DeleteAllItems(infoPtr
);
8871 case LVM_DELETECOLUMN
:
8872 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
8874 case LVM_DELETEITEM
:
8875 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
8877 case LVM_EDITLABELW
:
8878 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, TRUE
);
8880 case LVM_EDITLABELA
:
8881 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, FALSE
);
8883 /* case LVM_ENABLEGROUPVIEW: */
8885 case LVM_ENSUREVISIBLE
:
8886 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
8889 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
8892 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
8894 case LVM_GETBKCOLOR
:
8895 return infoPtr
->clrBk
;
8897 /* case LVM_GETBKIMAGE: */
8899 case LVM_GETCALLBACKMASK
:
8900 return infoPtr
->uCallbackMask
;
8902 case LVM_GETCOLUMNA
:
8903 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
8905 case LVM_GETCOLUMNW
:
8906 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
8908 case LVM_GETCOLUMNORDERARRAY
:
8909 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
8911 case LVM_GETCOLUMNWIDTH
:
8912 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
8914 case LVM_GETCOUNTPERPAGE
:
8915 return LISTVIEW_GetCountPerPage(infoPtr
);
8917 case LVM_GETEDITCONTROL
:
8918 return (LRESULT
)infoPtr
->hwndEdit
;
8920 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
8921 return infoPtr
->dwLvExStyle
;
8923 /* case LVM_GETGROUPINFO: */
8925 /* case LVM_GETGROUPMETRICS: */
8928 return (LRESULT
)infoPtr
->hwndHeader
;
8930 case LVM_GETHOTCURSOR
:
8931 return (LRESULT
)infoPtr
->hHotCursor
;
8933 case LVM_GETHOTITEM
:
8934 return infoPtr
->nHotItem
;
8936 case LVM_GETHOVERTIME
:
8937 return infoPtr
->dwHoverTime
;
8939 case LVM_GETIMAGELIST
:
8940 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
8942 /* case LVM_GETINSERTMARK: */
8944 /* case LVM_GETINSERTMARKCOLOR: */
8946 /* case LVM_GETINSERTMARKRECT: */
8948 case LVM_GETISEARCHSTRINGA
:
8949 case LVM_GETISEARCHSTRINGW
:
8950 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8954 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
8957 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
8959 case LVM_GETITEMCOUNT
:
8960 return infoPtr
->nItemCount
;
8962 case LVM_GETITEMPOSITION
:
8963 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
8965 case LVM_GETITEMRECT
:
8966 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
8968 case LVM_GETITEMSPACING
:
8969 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
8971 case LVM_GETITEMSTATE
:
8972 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
8974 case LVM_GETITEMTEXTA
:
8975 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
8977 case LVM_GETITEMTEXTW
:
8978 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
8980 case LVM_GETNEXTITEM
:
8981 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
8983 case LVM_GETNUMBEROFWORKAREAS
:
8984 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8988 if (!lParam
) return FALSE
;
8989 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
8992 /* case LVM_GETOUTLINECOLOR: */
8994 /* case LVM_GETSELECTEDCOLUMN: */
8996 case LVM_GETSELECTEDCOUNT
:
8997 return LISTVIEW_GetSelectedCount(infoPtr
);
8999 case LVM_GETSELECTIONMARK
:
9000 return infoPtr
->nSelectionMark
;
9002 case LVM_GETSTRINGWIDTHA
:
9003 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, FALSE
);
9005 case LVM_GETSTRINGWIDTHW
:
9006 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, TRUE
);
9008 case LVM_GETSUBITEMRECT
:
9009 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
9011 case LVM_GETTEXTBKCOLOR
:
9012 return infoPtr
->clrTextBk
;
9014 case LVM_GETTEXTCOLOR
:
9015 return infoPtr
->clrText
;
9017 /* case LVM_GETTILEINFO: */
9019 /* case LVM_GETTILEVIEWINFO: */
9021 case LVM_GETTOOLTIPS
:
9022 if( !infoPtr
->hwndToolTip
)
9023 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
9024 return (LRESULT
)infoPtr
->hwndToolTip
;
9026 case LVM_GETTOPINDEX
:
9027 return LISTVIEW_GetTopIndex(infoPtr
);
9029 /*case LVM_GETUNICODEFORMAT:
9030 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9033 /* case LVM_GETVIEW: */
9035 case LVM_GETVIEWRECT
:
9036 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
9038 case LVM_GETWORKAREAS
:
9039 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9042 /* case LVM_HASGROUP: */
9045 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, FALSE
);
9047 case LVM_INSERTCOLUMNA
:
9048 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9050 case LVM_INSERTCOLUMNW
:
9051 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9053 /* case LVM_INSERTGROUP: */
9055 /* case LVM_INSERTGROUPSORTED: */
9057 case LVM_INSERTITEMA
:
9058 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9060 case LVM_INSERTITEMW
:
9061 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9063 /* case LVM_INSERTMARKHITTEST: */
9065 /* case LVM_ISGROUPVIEWENABLED: */
9067 /* case LVM_MAPIDTOINDEX: */
9069 /* case LVM_MAPINDEXTOID: */
9071 /* case LVM_MOVEGROUP: */
9073 /* case LVM_MOVEITEMTOGROUP: */
9075 case LVM_REDRAWITEMS
:
9076 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9078 /* case LVM_REMOVEALLGROUPS: */
9080 /* case LVM_REMOVEGROUP: */
9083 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9085 case LVM_SETBKCOLOR
:
9086 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
9088 /* case LVM_SETBKIMAGE: */
9090 case LVM_SETCALLBACKMASK
:
9091 infoPtr
->uCallbackMask
= (UINT
)wParam
;
9094 case LVM_SETCOLUMNA
:
9095 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9097 case LVM_SETCOLUMNW
:
9098 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9100 case LVM_SETCOLUMNORDERARRAY
:
9101 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9103 case LVM_SETCOLUMNWIDTH
:
9104 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
9106 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
9107 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
9109 /* case LVM_SETGROUPINFO: */
9111 /* case LVM_SETGROUPMETRICS: */
9113 case LVM_SETHOTCURSOR
:
9114 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
9116 case LVM_SETHOTITEM
:
9117 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
9119 case LVM_SETHOVERTIME
:
9120 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)wParam
);
9122 case LVM_SETICONSPACING
:
9123 return LISTVIEW_SetIconSpacing(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
9125 case LVM_SETIMAGELIST
:
9126 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
9128 /* case LVM_SETINFOTIP: */
9130 /* case LVM_SETINSERTMARK: */
9132 /* case LVM_SETINSERTMARKCOLOR: */
9135 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9138 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9140 case LVM_SETITEMCOUNT
:
9141 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
9143 case LVM_SETITEMPOSITION
:
9146 pt
.x
= (short)LOWORD(lParam
);
9147 pt
.y
= (short)HIWORD(lParam
);
9148 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, pt
);
9151 case LVM_SETITEMPOSITION32
:
9152 if (lParam
== 0) return FALSE
;
9153 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, *((POINT
*)lParam
));
9155 case LVM_SETITEMSTATE
:
9156 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
9158 case LVM_SETITEMTEXTA
:
9159 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9161 case LVM_SETITEMTEXTW
:
9162 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9164 /* case LVM_SETOUTLINECOLOR: */
9166 /* case LVM_SETSELECTEDCOLUMN: */
9168 case LVM_SETSELECTIONMARK
:
9169 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
9171 case LVM_SETTEXTBKCOLOR
:
9172 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
9174 case LVM_SETTEXTCOLOR
:
9175 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
9177 /* case LVM_SETTILEINFO: */
9179 /* case LVM_SETTILEVIEWINFO: */
9181 /* case LVM_SETTILEWIDTH: */
9183 case LVM_SETTOOLTIPS
:
9184 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
9186 /* case LVM_SETUNICODEFORMAT: */
9188 /* case LVM_SETVIEW: */
9190 /* case LVM_SETWORKAREAS: */
9192 /* case LVM_SORTGROUPS: */
9195 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
);
9197 /* LVM_SORTITEMSEX: */
9199 case LVM_SUBITEMHITTEST
:
9200 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
9203 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
9206 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
9209 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
9212 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
9215 return LISTVIEW_Enable(infoPtr
, (BOOL
)wParam
);
9218 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
9221 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
9224 return (LRESULT
)infoPtr
->hFont
;
9227 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
9230 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
9233 return LISTVIEW_KillFocus(infoPtr
);
9235 case WM_LBUTTONDBLCLK
:
9236 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9238 case WM_LBUTTONDOWN
:
9239 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9242 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9245 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9248 return LISTVIEW_MouseHover(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9251 return LISTVIEW_NCDestroy(infoPtr
);
9254 if (lParam
&& ((LPNMHDR
)lParam
)->hwndFrom
== infoPtr
->hwndHeader
)
9255 return LISTVIEW_HeaderNotification(infoPtr
, (LPNMHEADERW
)lParam
);
9258 case WM_NOTIFYFORMAT
:
9259 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
9261 case WM_PRINTCLIENT
:
9262 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
9265 return LISTVIEW_Paint(infoPtr
, (HDC
)wParam
);
9267 case WM_RBUTTONDBLCLK
:
9268 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9270 case WM_RBUTTONDOWN
:
9271 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9274 return LISTVIEW_RButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9277 if(LISTVIEW_SetCursor(infoPtr
, (HWND
)wParam
, LOWORD(lParam
), HIWORD(lParam
)))
9282 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
9285 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
9288 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
9291 return LISTVIEW_Size(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
9293 case WM_STYLECHANGED
:
9294 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
9296 case WM_SYSCOLORCHANGE
:
9297 COMCTL32_RefreshSysColors();
9300 /* case WM_TIMER: */
9303 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
9306 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
9307 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9308 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
9310 case WM_WINDOWPOSCHANGED
:
9311 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
9313 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
9314 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
9315 LISTVIEW_UpdateSize(infoPtr
);
9316 LISTVIEW_UpdateScroll(infoPtr
);
9318 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9320 /* case WM_WININICHANGE: */
9323 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
))
9324 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg
, wParam
, lParam
);
9327 /* call default window procedure */
9328 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9335 * Registers the window class.
9343 void LISTVIEW_Register(void)
9347 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
9348 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
9349 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
9350 wndClass
.cbClsExtra
= 0;
9351 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
9352 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
9353 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
9354 wndClass
.lpszClassName
= WC_LISTVIEWW
;
9355 RegisterClassW(&wndClass
);
9360 * Unregisters the window class.
9368 void LISTVIEW_Unregister(void)
9370 UnregisterClassW(WC_LISTVIEWW
, NULL
);
9375 * Handle any WM_COMMAND messages
9378 * [I] infoPtr : valid pointer to the listview structure
9379 * [I] wParam : the first message parameter
9380 * [I] lParam : the second message parameter
9385 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
9387 switch (HIWORD(wParam
))
9392 * Adjust the edit window size
9395 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
9396 HFONT hFont
, hOldFont
= 0;
9401 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
9402 len
= GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
9403 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
9405 /* Select font to get the right dimension of the string */
9406 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
9409 hOldFont
= SelectObject(hdc
, hFont
);
9412 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
9414 TEXTMETRICW textMetric
;
9416 /* Add Extra spacing for the next character */
9417 GetTextMetricsW(hdc
, &textMetric
);
9418 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
9426 rect
.bottom
- rect
.top
,
9427 SWP_DRAWFRAME
|SWP_NOMOVE
);
9430 SelectObject(hdc
, hOldFont
);
9432 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
9438 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);
9447 * Subclassed edit control windproc function
9450 * [I] hwnd : the edit window handle
9451 * [I] uMsg : the message that is to be processed
9452 * [I] wParam : first message parameter
9453 * [I] lParam : second message parameter
9454 * [I] isW : TRUE if input is Unicode
9459 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
9461 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
9462 BOOL cancel
= FALSE
;
9464 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9465 hwnd
, uMsg
, wParam
, lParam
, isW
);
9470 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
9477 WNDPROC editProc
= infoPtr
->EditWndProc
;
9478 infoPtr
->EditWndProc
= 0;
9479 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
9480 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
9484 if (VK_ESCAPE
== (INT
)wParam
)
9489 else if (VK_RETURN
== (INT
)wParam
)
9493 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
9497 if (infoPtr
->hwndEdit
)
9499 LPWSTR buffer
= NULL
;
9501 infoPtr
->hwndEdit
= 0;
9504 DWORD len
= isW
? GetWindowTextLengthW(hwnd
) : GetWindowTextLengthA(hwnd
);
9508 if ( (buffer
= Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))) )
9510 if (isW
) GetWindowTextW(hwnd
, buffer
, len
+1);
9511 else GetWindowTextA(hwnd
, (CHAR
*)buffer
, len
+1);
9515 LISTVIEW_EndEditLabelT(infoPtr
, buffer
, isW
);
9517 if (buffer
) Free(buffer
);
9521 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
9527 * Subclassed edit control Unicode windproc function
9530 * [I] hwnd : the edit window handle
9531 * [I] uMsg : the message that is to be processed
9532 * [I] wParam : first message parameter
9533 * [I] lParam : second message parameter
9537 LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9539 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
9544 * Subclassed edit control ANSI windproc function
9547 * [I] hwnd : the edit window handle
9548 * [I] uMsg : the message that is to be processed
9549 * [I] wParam : first message parameter
9550 * [I] lParam : second message parameter
9554 LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9556 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
9561 * Creates a subclassed edit cotrol
9564 * [I] infoPtr : valid pointer to the listview structure
9565 * [I] text : initial text for the edit
9566 * [I] style : the window style
9567 * [I] isW : TRUE if input is Unicode
9571 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, DWORD style
,
9572 INT x
, INT y
, INT width
, INT height
, BOOL isW
)
9574 WCHAR editName
[5] = { 'E', 'd', 'i', 't', '\0' };
9579 TEXTMETRICW textMetric
;
9580 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
9582 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text
, isW
), isW
);
9584 style
|= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
;
9585 hdc
= GetDC(infoPtr
->hwndSelf
);
9587 /* Select the font to get appropriate metric dimensions */
9588 if(infoPtr
->hFont
!= 0)
9589 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
9591 /*Get String Length in pixels */
9592 GetTextExtentPoint32W(hdc
, text
, lstrlenW(text
), &sz
);
9594 /*Add Extra spacing for the next character */
9595 GetTextMetricsW(hdc
, &textMetric
);
9596 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
9598 if(infoPtr
->hFont
!= 0)
9599 SelectObject(hdc
, hOldFont
);
9601 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
9603 hedit
= CreateWindowW(editName
, text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
9605 hedit
= CreateWindowA("Edit", (LPCSTR
)text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
9607 if (!hedit
) return 0;
9609 infoPtr
->EditWndProc
= (WNDPROC
)
9610 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
9611 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
9613 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);