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 Oct. 21, 2002, by Dimitrie O. Paun.
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.
36 * -- Hot item handling, mouse hovering
37 * -- Workareas support
42 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
43 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
44 * -- LVA_SNAPTOGRID not implemented
45 * -- LISTVIEW_ApproximateViewRect partially implemented
46 * -- LISTVIEW_[GS]etColumnOrderArray stubs
47 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
48 * -- LISTVIEW_SetIconSpacing is incomplete
49 * -- LISTVIEW_SortItems is broken
50 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
53 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
54 * linear in the number of items in the list, and this is
55 * unacceptable for large lists.
56 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
57 * instead of inserting in the right spot
58 * -- we should keep an ordered array of coordinates in iconic mode
59 * this would allow to frame items (iterator_frameditems),
60 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
68 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
75 * -- LVS_NOSCROLL (see Q137520)
76 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
79 * -- LVS_EX_BORDERSELECT
82 * -- LVS_EX_HEADERDRAGDROP
85 * -- LVS_EX_MULTIWORKAREAS
86 * -- LVS_EX_ONECLICKACTIVATE
88 * -- LVS_EX_SIMPLESELECT
89 * -- LVS_EX_TRACKSELECT
90 * -- LVS_EX_TWOCLICKACTIVATE
91 * -- LVS_EX_UNDERLINECOLD
92 * -- LVS_EX_UNDERLINEHOT
95 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
104 * -- LVM_CANCELEDITLABEL
105 * -- LVM_ENABLEGROUPVIEW
106 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
107 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
108 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
109 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
110 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
111 * -- LVM_GETINSERTMARKRECT
112 * -- LVM_GETNUMBEROFWORKAREAS
113 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
114 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
115 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
116 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
117 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
118 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
119 * -- LVM_GETVIEW, LVM_SETVIEW
120 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
121 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
122 * -- LVM_INSERTGROUPSORTED
123 * -- LVM_INSERTMARKHITTEST
124 * -- LVM_ISGROUPVIEWENABLED
125 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
127 * -- LVM_MOVEITEMTOGROUP
129 * -- LVM_SETTILEWIDTH
133 * Known differences in message stream from native control (not known if
134 * these differences cause problems):
135 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
136 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
137 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
138 * processing for "USEDOUBLECLICKTIME".
142 #include "wine/port.h"
158 #include "commctrl.h"
159 #include "comctl32.h"
161 #include "wine/debug.h"
162 #include "wine/unicode.h"
164 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
166 /* make sure you set this to 0 for production use! */
167 #define DEBUG_RANGES 1
169 typedef struct tagCOLUMN_INFO
171 RECT rcHeader
; /* tracks the header's rectangle */
172 int fmt
; /* same as LVCOLUMN.fmt */
175 typedef struct tagITEMHDR
179 } ITEMHDR
, *LPITEMHDR
;
181 typedef struct tagSUBITEM_INFO
187 typedef struct tagITEM_INFO
195 typedef struct tagRANGE
201 typedef struct tagRANGES
206 typedef struct tagITERATOR
215 typedef struct tagLISTVIEW_INFO
222 COLORREF clrTextBkDefault
;
223 HIMAGELIST himlNormal
;
224 HIMAGELIST himlSmall
;
225 HIMAGELIST himlState
;
228 BOOL bNoItemMetrics
; /* flags if item metrics are not yet computed */
231 RANGES selectionRanges
;
236 RECT rcList
; /* This rectangle is really the window
237 * client rectangle possibly reduced by the
238 * horizontal scroll bar and/or header - see
239 * LISTVIEW_UpdateSize. This rectangle offset
240 * by the LISTVIEW_GetOrigin value is in
241 * client coordinates */
250 INT ntmHeight
; /* Some cached metrics of the font used */
251 INT ntmAveCharWidth
; /* by the listview to draw items */
252 BOOL bRedraw
; /* Turns on/off repaints & invalidations */
253 BOOL bAutoarrange
; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
255 BOOL bDoChangeNotify
; /* send change notification messages? */
258 DWORD dwStyle
; /* the cached window GWL_STYLE */
259 DWORD dwLvExStyle
; /* extended listview style */
260 INT nItemCount
; /* the number of items in the list */
261 HDPA hdpaItems
; /* array ITEM_INFO pointers */
262 HDPA hdpaPosX
; /* maintains the (X, Y) coordinates of the */
263 HDPA hdpaPosY
; /* items in LVS_ICON, and LVS_SMALLICON modes */
264 HDPA hdpaColumns
; /* array of COLUMN_INFO pointers */
265 POINT currIconPos
; /* this is the position next icon will be placed */
266 PFNLVCOMPARE pfnCompare
;
274 DWORD cditemmode
; /* Keep the custom draw flags for an item/row */
276 DWORD lastKeyPressTimestamp
;
278 INT nSearchParamLength
;
279 WCHAR szSearchParam
[ MAX_PATH
];
286 /* How many we debug buffer to allocate */
287 #define DEBUG_BUFFERS 20
288 /* The size of a single debug bbuffer */
289 #define DEBUG_BUFFER_SIZE 256
291 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
292 #define SB_INTERNAL -1
294 /* maximum size of a label */
295 #define DISP_TEXT_SIZE 512
297 /* padding for items in list and small icon display modes */
298 #define WIDTH_PADDING 12
300 /* padding for items in list, report and small icon display modes */
301 #define HEIGHT_PADDING 1
303 /* offset of items in report display mode */
304 #define REPORT_MARGINX 2
306 /* padding for icon in large icon display mode
307 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
308 * that HITTEST will see.
309 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
310 * ICON_TOP_PADDING - sum of the two above.
311 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
312 * LABEL_HOR_PADDING - between text and sides of box
313 * LABEL_VERT_PADDING - between bottom of text and end of box
315 * ICON_LR_PADDING - additional width above icon size.
316 * ICON_LR_HALF - half of the above value
318 #define ICON_TOP_PADDING_NOTHITABLE 2
319 #define ICON_TOP_PADDING_HITABLE 2
320 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
321 #define ICON_BOTTOM_PADDING 4
322 #define LABEL_HOR_PADDING 5
323 #define LABEL_VERT_PADDING 7
324 #define ICON_LR_PADDING 16
325 #define ICON_LR_HALF (ICON_LR_PADDING/2)
327 /* default label width for items in list and small icon display modes */
328 #define DEFAULT_LABEL_WIDTH 40
330 /* default column width for items in list display mode */
331 #define DEFAULT_COLUMN_WIDTH 128
333 /* Size of "line" scroll for V & H scrolls */
334 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
336 /* Padding betwen image and label */
337 #define IMAGE_PADDING 2
339 /* Padding behind the label */
340 #define TRAILING_LABEL_PADDING 12
341 #define TRAILING_HEADER_PADDING 11
343 /* Border for the icon caption */
344 #define CAPTION_BORDER 2
346 /* Standard DrawText flags */
347 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
348 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
349 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
351 /* The time in milliseconds to reset the search in the list */
352 #define KEY_DELAY 450
354 /* Dump the LISTVIEW_INFO structure to the debug channel */
355 #define LISTVIEW_DUMP(iP) do { \
356 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
357 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
358 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
359 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
360 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
361 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
362 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
363 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
364 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
365 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
369 * forward declarations
371 static BOOL
LISTVIEW_GetItemT(LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
372 static void LISTVIEW_GetItemBox(LISTVIEW_INFO
*, INT
, LPRECT
);
373 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO
*, INT
, LPPOINT
);
374 static BOOL
LISTVIEW_GetItemPosition(LISTVIEW_INFO
*, INT
, LPPOINT
);
375 static BOOL
LISTVIEW_GetItemRect(LISTVIEW_INFO
*, INT
, LPRECT
);
376 static INT
LISTVIEW_GetLabelWidth(LISTVIEW_INFO
*, INT
);
377 static void LISTVIEW_GetOrigin(LISTVIEW_INFO
*, LPPOINT
);
378 static BOOL
LISTVIEW_GetViewRect(LISTVIEW_INFO
*, LPRECT
);
379 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*, INT
);
380 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*, const LVITEMW
*, BOOL
);
381 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO
*);
382 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*, INT
);
383 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
384 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*, INT
, BOOL
);
385 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*, WPARAM
, LPARAM
);
386 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*, PFNLVCOMPARE
, LPARAM
);
387 static INT
LISTVIEW_GetStringWidthT(LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
388 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
);
389 static UINT
LISTVIEW_GetItemState(LISTVIEW_INFO
*, INT
, UINT
);
390 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, const LVITEMW
*);
391 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
392 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
393 static INT
LISTVIEW_GetTopIndex(LISTVIEW_INFO
*);
394 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
395 static HWND
CreateEditLabelT(LISTVIEW_INFO
*, LPCWSTR
, DWORD
, INT
, INT
, INT
, INT
, BOOL
);
396 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*, INT
, HIMAGELIST
);
398 /******** Text handling functions *************************************/
400 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
401 * text string. The string may be ANSI or Unicode, in which case
402 * the boolean isW tells us the type of the string.
404 * The name of the function tell what type of strings it expects:
405 * W: Unicode, T: ANSI/Unicode - function of isW
408 static inline BOOL
is_textW(LPCWSTR text
)
410 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
413 static inline BOOL
is_textT(LPCWSTR text
, BOOL isW
)
415 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
416 return is_textW(text
);
419 static inline int textlenT(LPCWSTR text
, BOOL isW
)
421 return !is_textT(text
, isW
) ? 0 :
422 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
425 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
428 if (isSrcW
) lstrcpynW(dest
, src
, max
);
429 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
431 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
432 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
435 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
437 LPWSTR wstr
= (LPWSTR
)text
;
439 if (!isW
&& is_textT(text
, isW
))
441 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
442 wstr
= Alloc(len
* sizeof(WCHAR
));
443 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
445 TRACE(" wstr=%s\n", text
== LPSTR_TEXTCALLBACKW
? "(callback)" : debugstr_w(wstr
));
449 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
451 if (!isW
&& is_textT(wstr
, isW
)) Free (wstr
);
455 * dest is a pointer to a Unicode string
456 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
458 static BOOL
textsetptrT(LPWSTR
*dest
, LPWSTR src
, BOOL isW
)
462 if (src
== LPSTR_TEXTCALLBACKW
)
464 if (is_textW(*dest
)) Free(*dest
);
465 *dest
= LPSTR_TEXTCALLBACKW
;
469 LPWSTR pszText
= textdupTtoW(src
, isW
);
470 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
471 bResult
= Str_SetPtrW(dest
, pszText
);
472 textfreeT(pszText
, isW
);
478 * compares a Unicode to a Unicode/ANSI text string
480 static inline int textcmpWT(LPCWSTR aw
, LPCWSTR bt
, BOOL isW
)
482 if (!aw
) return bt
? -1 : 0;
483 if (!bt
) return aw
? 1 : 0;
484 if (aw
== LPSTR_TEXTCALLBACKW
)
485 return bt
== LPSTR_TEXTCALLBACKW
? 0 : -1;
486 if (bt
!= LPSTR_TEXTCALLBACKW
)
488 LPWSTR bw
= textdupTtoW(bt
, isW
);
489 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
497 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
501 n
= min(min(n
, strlenW(s1
)), strlenW(s2
));
502 res
= CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
);
503 return res
? res
- sizeof(WCHAR
) : res
;
506 /******** Debugging functions *****************************************/
508 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
510 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
511 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
514 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
516 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
517 n
= min(textlenT(text
, isW
), n
);
518 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
521 static char* debug_getbuf()
523 static int index
= 0;
524 static char buffers
[DEBUG_BUFFERS
][DEBUG_BUFFER_SIZE
];
525 return buffers
[index
++ % DEBUG_BUFFERS
];
528 static inline const char* debugrange(const RANGE
*lprng
)
532 char* buf
= debug_getbuf();
533 snprintf(buf
, DEBUG_BUFFER_SIZE
, "[%d, %d)", lprng
->lower
, lprng
->upper
);
535 } else return "(null)";
538 static inline const char* debugpoint(const POINT
*lppt
)
542 char* buf
= debug_getbuf();
543 snprintf(buf
, DEBUG_BUFFER_SIZE
, "(%ld, %ld)", lppt
->x
, lppt
->y
);
545 } else return "(null)";
548 static inline const char* debugrect(const RECT
*rect
)
552 char* buf
= debug_getbuf();
553 snprintf(buf
, DEBUG_BUFFER_SIZE
, "[(%ld, %ld);(%ld, %ld)]",
554 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
556 } else return "(null)";
559 static const char* debugscrollinfo(const SCROLLINFO
*pScrollInfo
)
561 char* buf
= debug_getbuf(), *text
= buf
;
562 int len
, size
= DEBUG_BUFFER_SIZE
;
564 if (pScrollInfo
== NULL
) return "(null)";
565 len
= snprintf(buf
, size
, "{cbSize=%d, ", pScrollInfo
->cbSize
);
566 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
567 if (pScrollInfo
->fMask
& SIF_RANGE
)
568 len
= snprintf(buf
, size
, "nMin=%d, nMax=%d, ", pScrollInfo
->nMin
, pScrollInfo
->nMax
);
570 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
571 if (pScrollInfo
->fMask
& SIF_PAGE
)
572 len
= snprintf(buf
, size
, "nPage=%u, ", pScrollInfo
->nPage
);
574 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
575 if (pScrollInfo
->fMask
& SIF_POS
)
576 len
= snprintf(buf
, size
, "nPos=%d, ", pScrollInfo
->nPos
);
578 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
579 if (pScrollInfo
->fMask
& SIF_TRACKPOS
)
580 len
= snprintf(buf
, size
, "nTrackPos=%d, ", pScrollInfo
->nTrackPos
);
582 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
585 buf
= text
+ strlen(text
);
587 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
591 static const char* debugnmlistview(const NMLISTVIEW
*plvnm
)
595 char* buf
= debug_getbuf();
596 snprintf(buf
, DEBUG_BUFFER_SIZE
, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
597 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
598 plvnm
->iItem
, plvnm
->iSubItem
, plvnm
->uNewState
, plvnm
->uOldState
,
599 plvnm
->uChanged
, debugpoint(&plvnm
->ptAction
), plvnm
->lParam
);
601 } else return "(null)";
604 static const char* debuglvitem_t(const LVITEMW
*lpLVItem
, BOOL isW
)
606 char* buf
= debug_getbuf(), *text
= buf
;
607 int len
, size
= DEBUG_BUFFER_SIZE
;
609 if (lpLVItem
== NULL
) return "(null)";
610 len
= snprintf(buf
, size
, "{iItem=%d, iSubItem=%d, ", lpLVItem
->iItem
, lpLVItem
->iSubItem
);
611 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
612 if (lpLVItem
->mask
& LVIF_STATE
)
613 len
= snprintf(buf
, size
, "state=%x, stateMask=%x, ", lpLVItem
->state
, lpLVItem
->stateMask
);
615 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
616 if (lpLVItem
->mask
& LVIF_TEXT
)
617 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem
->pszText
, isW
, 80), lpLVItem
->cchTextMax
);
619 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
620 if (lpLVItem
->mask
& LVIF_IMAGE
)
621 len
= snprintf(buf
, size
, "iImage=%d, ", lpLVItem
->iImage
);
623 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
624 if (lpLVItem
->mask
& LVIF_PARAM
)
625 len
= snprintf(buf
, size
, "lParam=%lx, ", lpLVItem
->lParam
);
627 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
628 if (lpLVItem
->mask
& LVIF_INDENT
)
629 len
= snprintf(buf
, size
, "iIndent=%d, ", lpLVItem
->iIndent
);
631 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
634 buf
= text
+ strlen(text
);
636 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
640 static const char* debuglvcolumn_t(const LVCOLUMNW
*lpColumn
, BOOL isW
)
642 char* buf
= debug_getbuf(), *text
= buf
;
643 int len
, size
= DEBUG_BUFFER_SIZE
;
645 if (lpColumn
== NULL
) return "(null)";
646 len
= snprintf(buf
, size
, "{");
647 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
648 if (lpColumn
->mask
& LVCF_SUBITEM
)
649 len
= snprintf(buf
, size
, "iSubItem=%d, ", lpColumn
->iSubItem
);
651 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
652 if (lpColumn
->mask
& LVCF_FMT
)
653 len
= snprintf(buf
, size
, "fmt=%x, ", lpColumn
->fmt
);
655 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
656 if (lpColumn
->mask
& LVCF_WIDTH
)
657 len
= snprintf(buf
, size
, "cx=%d, ", lpColumn
->cx
);
659 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
660 if (lpColumn
->mask
& LVCF_TEXT
)
661 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn
->pszText
, isW
, 80), lpColumn
->cchTextMax
);
663 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
664 if (lpColumn
->mask
& LVCF_IMAGE
)
665 len
= snprintf(buf
, size
, "iImage=%d, ", lpColumn
->iImage
);
667 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
668 if (lpColumn
->mask
& LVCF_ORDER
)
669 len
= snprintf(buf
, size
, "iOrder=%d, ", lpColumn
->iOrder
);
671 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
674 buf
= text
+ strlen(text
);
676 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
680 static const char* debuglvhittestinfo(const LVHITTESTINFO
*lpht
)
684 char* buf
= debug_getbuf();
685 snprintf(buf
, DEBUG_BUFFER_SIZE
, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
686 debugpoint(&lpht
->pt
), lpht
->flags
, lpht
->iItem
, lpht
->iSubItem
);
688 } else return "(null)";
691 /* Return the corresponding text for a given scroll value */
692 static inline LPCSTR
debugscrollcode(int nScrollCode
)
696 case SB_LINELEFT
: return "SB_LINELEFT";
697 case SB_LINERIGHT
: return "SB_LINERIGHT";
698 case SB_PAGELEFT
: return "SB_PAGELEFT";
699 case SB_PAGERIGHT
: return "SB_PAGERIGHT";
700 case SB_THUMBPOSITION
: return "SB_THUMBPOSITION";
701 case SB_THUMBTRACK
: return "SB_THUMBTRACK";
702 case SB_ENDSCROLL
: return "SB_ENDSCROLL";
703 case SB_INTERNAL
: return "SB_INTERNAL";
704 default: return "unknown";
709 /******** Notification functions i************************************/
711 static LRESULT
notify_hdr(LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
715 TRACE("(code=%d)\n", code
);
717 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
718 pnmh
->idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
720 result
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
721 (WPARAM
)pnmh
->idFrom
, (LPARAM
)pnmh
);
723 TRACE(" <= %ld\n", result
);
728 static inline LRESULT
notify(LISTVIEW_INFO
*infoPtr
, INT code
)
731 return notify_hdr(infoPtr
, code
, &nmh
);
734 static inline void notify_itemactivate(LISTVIEW_INFO
*infoPtr
, LVHITTESTINFO
*htInfo
)
745 item
.mask
= LVIF_PARAM
|LVIF_STATE
;
746 item
.iItem
= htInfo
->iItem
;
748 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) {
749 nmia
.lParam
= item
.lParam
;
750 nmia
.uOldState
= item
.state
;
751 nmia
.uNewState
= item
.state
| LVIS_ACTIVATING
;
752 nmia
.uChanged
= LVIF_STATE
;
755 nmia
.iItem
= htInfo
->iItem
;
756 nmia
.iSubItem
= htInfo
->iSubItem
;
757 nmia
.ptAction
= htInfo
->pt
;
759 if (GetKeyState(VK_SHIFT
) & 0x8000) nmia
.uKeyFlags
|= LVKF_SHIFT
;
760 if (GetKeyState(VK_CONTROL
) & 0x8000) nmia
.uKeyFlags
|= LVKF_CONTROL
;
761 if (GetKeyState(VK_MENU
) & 0x8000) nmia
.uKeyFlags
|= LVKF_ALT
;
763 notify_hdr(infoPtr
, LVN_ITEMACTIVATE
, (LPNMHDR
)&nmia
);
766 static inline LRESULT
notify_listview(LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
768 TRACE("(code=%d, plvnm=%s)\n", code
, debugnmlistview(plvnm
));
769 return notify_hdr(infoPtr
, code
, (LPNMHDR
)plvnm
);
772 static LRESULT
notify_click(LISTVIEW_INFO
*infoPtr
, INT code
, LVHITTESTINFO
*lvht
)
777 TRACE("code=%d, lvht=%s\n", code
, debuglvhittestinfo(lvht
));
778 ZeroMemory(&nmlv
, sizeof(nmlv
));
779 nmlv
.iItem
= lvht
->iItem
;
780 nmlv
.iSubItem
= lvht
->iSubItem
;
781 nmlv
.ptAction
= lvht
->pt
;
782 item
.mask
= LVIF_PARAM
;
783 item
.iItem
= lvht
->iItem
;
785 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
786 return notify_listview(infoPtr
, code
, &nmlv
);
789 static void notify_deleteitem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
794 ZeroMemory(&nmlv
, sizeof (NMLISTVIEW
));
796 item
.mask
= LVIF_PARAM
;
799 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
800 notify_listview(infoPtr
, LVN_DELETEITEM
, &nmlv
);
803 static int get_ansi_notification(INT unicodeNotificationCode
)
805 switch (unicodeNotificationCode
)
807 case LVN_BEGINLABELEDITW
: return LVN_BEGINLABELEDITA
;
808 case LVN_ENDLABELEDITW
: return LVN_ENDLABELEDITA
;
809 case LVN_GETDISPINFOW
: return LVN_GETDISPINFOA
;
810 case LVN_SETDISPINFOW
: return LVN_SETDISPINFOA
;
811 case LVN_ODFINDITEMW
: return LVN_ODFINDITEMA
;
812 case LVN_GETINFOTIPW
: return LVN_GETINFOTIPA
;
814 ERR("unknown notification %x\n", unicodeNotificationCode
);
820 Send notification. depends on dispinfoW having same
821 structure as dispinfoA.
822 infoPtr : listview struct
823 notificationCode : *Unicode* notification code
824 pdi : dispinfo structure (can be unicode or ansi)
825 isW : TRUE if dispinfo is Unicode
827 static BOOL
notify_dispinfoT(LISTVIEW_INFO
*infoPtr
, INT notificationCode
, LPNMLVDISPINFOW pdi
, BOOL isW
)
829 BOOL bResult
= FALSE
;
830 BOOL convertToAnsi
= FALSE
, convertToUnicode
= FALSE
;
831 INT cchTempBufMax
= 0, savCchTextMax
= 0, realNotifCode
;
832 LPWSTR pszTempBuf
= NULL
, savPszText
= NULL
;
834 if ((pdi
->item
.mask
& LVIF_TEXT
) && is_textT(pdi
->item
.pszText
, isW
))
836 convertToAnsi
= (isW
&& infoPtr
->notifyFormat
== NFR_ANSI
);
837 convertToUnicode
= (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
);
840 if (convertToAnsi
|| convertToUnicode
)
842 if (notificationCode
!= LVN_GETDISPINFOW
)
844 cchTempBufMax
= convertToUnicode
?
845 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
846 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
850 cchTempBufMax
= pdi
->item
.cchTextMax
;
851 *pdi
->item
.pszText
= 0; /* make sure we don't process garbage */
854 pszTempBuf
= Alloc( (convertToUnicode
? sizeof(WCHAR
) : sizeof(CHAR
)) * cchTempBufMax
);
855 if (!pszTempBuf
) return FALSE
;
857 if (convertToUnicode
)
858 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
859 pszTempBuf
, cchTempBufMax
);
861 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) pszTempBuf
,
862 cchTempBufMax
, NULL
, NULL
);
864 savCchTextMax
= pdi
->item
.cchTextMax
;
865 savPszText
= pdi
->item
.pszText
;
866 pdi
->item
.pszText
= pszTempBuf
;
867 pdi
->item
.cchTextMax
= cchTempBufMax
;
870 if (infoPtr
->notifyFormat
== NFR_ANSI
)
871 realNotifCode
= get_ansi_notification(notificationCode
);
873 realNotifCode
= notificationCode
;
874 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi
->item
, infoPtr
->notifyFormat
!= NFR_ANSI
));
875 bResult
= notify_hdr(infoPtr
, realNotifCode
, &pdi
->hdr
);
877 if (convertToUnicode
|| convertToAnsi
)
879 if (convertToUnicode
) /* note : pointer can be changed by app ! */
880 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) savPszText
,
881 savCchTextMax
, NULL
, NULL
);
883 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
884 savPszText
, savCchTextMax
);
885 pdi
->item
.pszText
= savPszText
; /* restores our buffer */
886 pdi
->item
.cchTextMax
= savCchTextMax
;
892 static void customdraw_fill(NMLVCUSTOMDRAW
*lpnmlvcd
, LISTVIEW_INFO
*infoPtr
, HDC hdc
,
893 const RECT
*rcBounds
, const LVITEMW
*lplvItem
)
895 ZeroMemory(lpnmlvcd
, sizeof(NMLVCUSTOMDRAW
));
896 lpnmlvcd
->nmcd
.hdc
= hdc
;
897 lpnmlvcd
->nmcd
.rc
= *rcBounds
;
898 lpnmlvcd
->clrTextBk
= infoPtr
->clrTextBk
;
899 lpnmlvcd
->clrText
= infoPtr
->clrText
;
900 if (!lplvItem
) return;
901 lpnmlvcd
->nmcd
.dwItemSpec
= lplvItem
->iItem
+ 1;
902 lpnmlvcd
->iSubItem
= lplvItem
->iSubItem
;
903 if (lplvItem
->state
& LVIS_SELECTED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_SELECTED
;
904 if (lplvItem
->state
& LVIS_FOCUSED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_FOCUS
;
905 if (lplvItem
->iItem
== infoPtr
->nHotItem
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_HOT
;
906 lpnmlvcd
->nmcd
.lItemlParam
= lplvItem
->lParam
;
909 static inline DWORD
notify_customdraw (LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, NMLVCUSTOMDRAW
*lpnmlvcd
)
911 BOOL isForItem
= (lpnmlvcd
->nmcd
.dwItemSpec
!= 0);
914 lpnmlvcd
->nmcd
.dwDrawStage
= dwDrawStage
;
915 if (isForItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_ITEM
;
916 if (lpnmlvcd
->iSubItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_SUBITEM
;
917 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
--;
918 result
= notify_hdr(infoPtr
, NM_CUSTOMDRAW
, &lpnmlvcd
->nmcd
.hdr
);
919 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
++;
923 static void prepaint_setup (LISTVIEW_INFO
*infoPtr
, HDC hdc
, NMLVCUSTOMDRAW
*lpnmlvcd
)
925 /* apprently, for selected items, we have to override the returned values */
926 if (lpnmlvcd
->nmcd
.uItemState
& CDIS_SELECTED
)
930 lpnmlvcd
->clrTextBk
= comctl32_color
.clrHighlight
;
931 lpnmlvcd
->clrText
= comctl32_color
.clrHighlightText
;
933 else if (infoPtr
->dwStyle
& LVS_SHOWSELALWAYS
)
935 lpnmlvcd
->clrTextBk
= comctl32_color
.clr3dFace
;
936 lpnmlvcd
->clrText
= comctl32_color
.clrBtnText
;
940 /* Set the text attributes */
941 if (lpnmlvcd
->clrTextBk
!= CLR_NONE
)
943 SetBkMode(hdc
, OPAQUE
);
944 if (lpnmlvcd
->clrTextBk
== CLR_DEFAULT
)
945 SetBkColor(hdc
, infoPtr
->clrTextBkDefault
);
947 SetBkColor(hdc
,lpnmlvcd
->clrTextBk
);
950 SetBkMode(hdc
, TRANSPARENT
);
951 SetTextColor(hdc
, lpnmlvcd
->clrText
);
954 static inline DWORD
notify_postpaint (LISTVIEW_INFO
*infoPtr
, NMLVCUSTOMDRAW
*lpnmlvcd
)
956 return notify_customdraw(infoPtr
, CDDS_POSTPAINT
, lpnmlvcd
);
959 /******** Item iterator functions **********************************/
961 static RANGES
ranges_create(int count
);
962 static void ranges_destroy(RANGES ranges
);
963 static BOOL
ranges_add(RANGES ranges
, RANGE range
);
964 static BOOL
ranges_del(RANGES ranges
, RANGE range
);
965 static void ranges_dump(RANGES ranges
);
967 static inline BOOL
ranges_additem(RANGES ranges
, INT nItem
)
969 RANGE range
= { nItem
, nItem
+ 1 };
971 return ranges_add(ranges
, range
);
974 static inline BOOL
ranges_delitem(RANGES ranges
, INT nItem
)
976 RANGE range
= { nItem
, nItem
+ 1 };
978 return ranges_del(ranges
, range
);
982 * ITERATOR DOCUMENTATION
984 * The iterator functions allow for easy, and convenient iteration
985 * over items of iterest in the list. Typically, you create a
986 * iterator, use it, and destroy it, as such:
989 * iterator_xxxitems(&i, ...);
990 * while (iterator_{prev,next}(&i)
992 * //code which uses i.nItem
994 * iterator_destroy(&i);
996 * where xxx is either: framed, or visible.
997 * Note that it is important that the code destroys the iterator
998 * after it's done with it, as the creation of the iterator may
999 * allocate memory, which thus needs to be freed.
1001 * You can iterate both forwards, and backwards through the list,
1002 * by using iterator_next or iterator_prev respectively.
1004 * Lower numbered items are draw on top of higher number items in
1005 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1006 * items may overlap). So, to test items, you should use
1008 * which lists the items top to bottom (in Z-order).
1009 * For drawing items, you should use
1011 * which lists the items bottom to top (in Z-order).
1012 * If you keep iterating over the items after the end-of-items
1013 * marker (-1) is returned, the iterator will start from the
1014 * beginning. Typically, you don't need to test for -1,
1015 * because iterator_{next,prev} will return TRUE if more items
1016 * are to be iterated over, or FALSE otherwise.
1018 * Note: the iterator is defined to be bidirectional. That is,
1019 * any number of prev followed by any number of next, or
1020 * five versa, should leave the iterator at the same item:
1021 * prev * n, next * n = next * n, prev * n
1023 * The iterator has a notion of an out-of-order, special item,
1024 * which sits at the start of the list. This is used in
1025 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1026 * which needs to be first, as it may overlap other items.
1028 * The code is a bit messy because we have:
1029 * - a special item to deal with
1030 * - simple range, or composite range
1032 * If you find bugs, or want to add features, please make sure you
1033 * always check/modify *both* iterator_prev, and iterator_next.
1037 * This function iterates through the items in increasing order,
1038 * but prefixed by the special item, then -1. That is:
1039 * special, 1, 2, 3, ..., n, -1.
1040 * Each item is listed only once.
1042 static inline BOOL
iterator_next(ITERATOR
* i
)
1046 i
->nItem
= i
->nSpecial
;
1047 if (i
->nItem
!= -1) return TRUE
;
1049 if (i
->nItem
== i
->nSpecial
)
1051 if (i
->ranges
) i
->index
= 0;
1057 if (i
->nItem
== i
->nSpecial
) i
->nItem
++;
1058 if (i
->nItem
< i
->range
.upper
) return TRUE
;
1063 if (i
->index
< DPA_GetPtrCount(i
->ranges
->hdpa
))
1064 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, i
->index
++);
1067 else if (i
->nItem
>= i
->range
.upper
) goto end
;
1069 i
->nItem
= i
->range
.lower
;
1070 if (i
->nItem
>= 0) goto testitem
;
1077 * This function iterates through the items in decreasing order,
1078 * followed by the special item, then -1. That is:
1079 * n, n-1, ..., 3, 2, 1, special, -1.
1080 * Each item is listed only once.
1082 static inline BOOL
iterator_prev(ITERATOR
* i
)
1089 if (i
->ranges
) i
->index
= DPA_GetPtrCount(i
->ranges
->hdpa
);
1092 if (i
->nItem
== i
->nSpecial
)
1100 if (i
->nItem
== i
->nSpecial
) i
->nItem
--;
1101 if (i
->nItem
>= i
->range
.lower
) return TRUE
;
1107 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, --i
->index
);
1110 else if (!start
&& i
->nItem
< i
->range
.lower
) goto end
;
1112 i
->nItem
= i
->range
.upper
;
1113 if (i
->nItem
> 0) goto testitem
;
1115 return (i
->nItem
= i
->nSpecial
) != -1;
1118 static RANGE
iterator_range(ITERATOR
* i
)
1122 if (!i
->ranges
) return i
->range
;
1124 if (DPA_GetPtrCount(i
->ranges
->hdpa
) > 0)
1126 range
.lower
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, 0)).lower
;
1127 range
.upper
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, DPA_GetPtrCount(i
->ranges
->hdpa
) - 1)).upper
;
1129 else range
.lower
= range
.upper
= 0;
1135 * Releases resources associated with this ierator.
1137 static inline void iterator_destroy(ITERATOR
* i
)
1139 ranges_destroy(i
->ranges
);
1143 * Create an empty iterator.
1145 static inline BOOL
iterator_empty(ITERATOR
* i
)
1147 ZeroMemory(i
, sizeof(*i
));
1148 i
->nItem
= i
->nSpecial
= i
->range
.lower
= i
->range
.upper
= -1;
1153 * Create an iterator over a range.
1155 static inline BOOL
iterator_rangeitems(ITERATOR
* i
, RANGE range
)
1163 * Create an iterator over a bunch of ranges.
1164 * Please note that the iterator will take ownership of the ranges,
1165 * and will free them upon destruction.
1167 static inline BOOL
iterator_rangesitems(ITERATOR
* i
, RANGES ranges
)
1175 * Creates an iterator over the items which intersect lprc.
1177 static BOOL
iterator_frameditems(ITERATOR
* i
, LISTVIEW_INFO
* infoPtr
, const RECT
*lprc
)
1179 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1180 RECT frame
= *lprc
, rcItem
, rcTemp
;
1183 /* in case we fail, we want to return an empty iterator */
1184 if (!iterator_empty(i
)) return FALSE
;
1186 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1188 TRACE("(lprc=%s)\n", debugrect(lprc
));
1189 OffsetRect(&frame
, -Origin
.x
, -Origin
.y
);
1191 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
1195 if (uView
== LVS_ICON
&& infoPtr
->nFocusedItem
!= -1)
1197 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcItem
);
1198 if (IntersectRect(&rcTemp
, &rcItem
, lprc
))
1199 i
->nSpecial
= infoPtr
->nFocusedItem
;
1201 if (!(iterator_rangesitems(i
, ranges_create(50)))) return FALSE
;
1202 /* to do better here, we need to have PosX, and PosY sorted */
1203 TRACE("building icon ranges:\n");
1204 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
1206 rcItem
.left
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1207 rcItem
.top
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1208 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1209 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1210 if (IntersectRect(&rcTemp
, &rcItem
, &frame
))
1211 ranges_additem(i
->ranges
, nItem
);
1215 else if (uView
== LVS_REPORT
)
1219 if (frame
.left
>= infoPtr
->nItemWidth
) return TRUE
;
1220 if (frame
.top
>= infoPtr
->nItemHeight
* infoPtr
->nItemCount
) return TRUE
;
1222 range
.lower
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1223 range
.upper
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, infoPtr
->nItemCount
- 1) + 1;
1224 if (range
.upper
<= range
.lower
) return TRUE
;
1225 if (!iterator_rangeitems(i
, range
)) return FALSE
;
1226 TRACE(" report=%s\n", debugrange(&i
->range
));
1230 INT nPerCol
= max((infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
, 1);
1231 INT nFirstRow
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1232 INT nLastRow
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, nPerCol
- 1);
1233 INT nFirstCol
= max(frame
.left
/ infoPtr
->nItemWidth
, 0);
1234 INT nLastCol
= min((frame
.right
- 1) / infoPtr
->nItemWidth
, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1235 INT lower
= nFirstCol
* nPerCol
+ nFirstRow
;
1239 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1240 nPerCol
, nFirstRow
, nLastRow
, nFirstCol
, nLastCol
, lower
);
1242 if (nLastCol
< nFirstCol
|| nLastRow
< nFirstRow
) return TRUE
;
1244 if (!(iterator_rangesitems(i
, ranges_create(nLastCol
- nFirstCol
+ 1)))) return FALSE
;
1245 TRACE("building list ranges:\n");
1246 for (nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++)
1248 item_range
.lower
= nCol
* nPerCol
+ nFirstRow
;
1249 if(item_range
.lower
>= infoPtr
->nItemCount
) break;
1250 item_range
.upper
= min(nCol
* nPerCol
+ nLastRow
+ 1, infoPtr
->nItemCount
);
1251 TRACE(" list=%s\n", debugrange(&item_range
));
1252 ranges_add(i
->ranges
, item_range
);
1260 * Creates an iterator over the items which intersect the visible region of hdc.
1262 static BOOL
iterator_visibleitems(ITERATOR
*i
, LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1264 POINT Origin
, Position
;
1265 RECT rcItem
, rcClip
;
1268 rgntype
= GetClipBox(hdc
, &rcClip
);
1269 if (rgntype
== NULLREGION
) return iterator_empty(i
);
1270 if (!iterator_frameditems(i
, infoPtr
, &rcClip
)) return FALSE
;
1271 if (rgntype
== SIMPLEREGION
) return TRUE
;
1273 /* first deal with the special item */
1274 if (i
->nSpecial
!= -1)
1276 LISTVIEW_GetItemBox(infoPtr
, i
->nSpecial
, &rcItem
);
1277 if (!RectVisible(hdc
, &rcItem
)) i
->nSpecial
= -1;
1280 /* if we can't deal with the region, we'll just go with the simple range */
1281 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1282 TRACE("building visible range:\n");
1283 if (!i
->ranges
&& i
->range
.lower
< i
->range
.upper
)
1285 if (!(i
->ranges
= ranges_create(50))) return TRUE
;
1286 if (!ranges_add(i
->ranges
, i
->range
))
1288 ranges_destroy(i
->ranges
);
1294 /* now delete the invisible items from the list */
1295 while(iterator_next(i
))
1297 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
1298 rcItem
.left
= Position
.x
+ Origin
.x
;
1299 rcItem
.top
= Position
.y
+ Origin
.y
;
1300 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1301 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1302 if (!RectVisible(hdc
, &rcItem
))
1303 ranges_delitem(i
->ranges
, i
->nItem
);
1305 /* the iterator should restart on the next iterator_next */
1311 /******** Misc helper functions ************************************/
1313 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
1314 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
1316 if (isW
) return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
1317 else return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
1320 static inline BOOL
is_autoarrange(LISTVIEW_INFO
*infoPtr
)
1322 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1324 return ((infoPtr
->dwStyle
& LVS_AUTOARRANGE
) || infoPtr
->bAutoarrange
) &&
1325 (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
);
1328 /******** Internal API functions ************************************/
1330 static inline COLUMN_INFO
* LISTVIEW_GetColumnInfo(LISTVIEW_INFO
*infoPtr
, INT nSubItem
)
1332 static COLUMN_INFO mainItem
;
1334 if (nSubItem
== 0 && DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0) return &mainItem
;
1335 assert (nSubItem
>= 0 && nSubItem
< DPA_GetPtrCount(infoPtr
->hdpaColumns
));
1336 return (COLUMN_INFO
*)DPA_GetPtr(infoPtr
->hdpaColumns
, nSubItem
);
1339 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO
*infoPtr
, INT nSubItem
, RECT
*lprc
)
1341 *lprc
= LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->rcHeader
;
1344 static inline BOOL
LISTVIEW_GetItemW(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
1346 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
1349 /* Listview invalidation functions: use _only_ these functions to invalidate */
1351 static inline BOOL
is_redrawing(LISTVIEW_INFO
*infoPtr
)
1353 return infoPtr
->bRedraw
;
1356 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO
*infoPtr
, const RECT
* rect
)
1358 if(!is_redrawing(infoPtr
)) return;
1359 TRACE(" invalidating rect=%s\n", debugrect(rect
));
1360 InvalidateRect(infoPtr
->hwndSelf
, rect
, TRUE
);
1363 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1367 if(!is_redrawing(infoPtr
)) return;
1368 LISTVIEW_GetItemBox(infoPtr
, nItem
, &rcBox
);
1369 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1372 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
)
1374 POINT Origin
, Position
;
1377 if(!is_redrawing(infoPtr
)) return;
1378 assert ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
);
1379 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1380 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
1381 LISTVIEW_GetHeaderRect(infoPtr
, nSubItem
, &rcBox
);
1383 rcBox
.bottom
= infoPtr
->nItemHeight
;
1384 OffsetRect(&rcBox
, Origin
.x
+ Position
.x
, Origin
.y
+ Position
.y
);
1385 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1388 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO
*infoPtr
)
1390 LISTVIEW_InvalidateRect(infoPtr
, NULL
);
1393 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
1397 if(!is_redrawing(infoPtr
)) return;
1398 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
1399 rcCol
.top
= infoPtr
->rcList
.top
;
1400 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
1401 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
1406 * Retrieves the number of items that can fit vertically in the client area.
1409 * [I] infoPtr : valid pointer to the listview structure
1412 * Number of items per row.
1414 static inline INT
LISTVIEW_GetCountPerRow(LISTVIEW_INFO
*infoPtr
)
1416 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1418 return max(nListWidth
/infoPtr
->nItemWidth
, 1);
1423 * Retrieves the number of items that can fit horizontally in the client
1427 * [I] infoPtr : valid pointer to the listview structure
1430 * Number of items per column.
1432 static inline INT
LISTVIEW_GetCountPerColumn(LISTVIEW_INFO
*infoPtr
)
1434 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1436 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
1440 /*************************************************************************
1441 * LISTVIEW_ProcessLetterKeys
1443 * Processes keyboard messages generated by pressing the letter keys
1445 * What this does is perform a case insensitive search from the
1446 * current position with the following quirks:
1447 * - If two chars or more are pressed in quick succession we search
1448 * for the corresponding string (e.g. 'abc').
1449 * - If there is a delay we wipe away the current search string and
1450 * restart with just that char.
1451 * - If the user keeps pressing the same character, whether slowly or
1452 * fast, so that the search string is entirely composed of this
1453 * character ('aaaaa' for instance), then we search for first item
1454 * that starting with that character.
1455 * - If the user types the above character in quick succession, then
1456 * we must also search for the corresponding string ('aaaaa'), and
1457 * go to that string if there is a match.
1460 * [I] hwnd : handle to the window
1461 * [I] charCode : the character code, the actual character
1462 * [I] keyData : key data
1470 * - The current implementation has a list of characters it will
1471 * accept and it ignores averything else. In particular it will
1472 * ignore accentuated characters which seems to match what
1473 * Windows does. But I'm not sure it makes sense to follow
1475 * - We don't sound a beep when the search fails.
1479 * TREEVIEW_ProcessLetterKeys
1481 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
1486 WCHAR buffer
[MAX_PATH
];
1487 DWORD lastKeyPressTimestamp
= infoPtr
->lastKeyPressTimestamp
;
1489 /* simple parameter checking */
1490 if (!charCode
|| !keyData
) return 0;
1492 /* only allow the valid WM_CHARs through */
1493 if (!isalnum(charCode
) &&
1494 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
1495 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
1496 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
1497 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
1498 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
1499 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
1500 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
1501 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
1502 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
1505 /* if there's one item or less, there is no where to go */
1506 if (infoPtr
->nItemCount
<= 1) return 0;
1508 /* update the search parameters */
1509 infoPtr
->lastKeyPressTimestamp
= GetTickCount();
1510 if (infoPtr
->lastKeyPressTimestamp
- lastKeyPressTimestamp
< KEY_DELAY
) {
1511 if (infoPtr
->nSearchParamLength
< MAX_PATH
)
1512 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++]=charCode
;
1513 if (infoPtr
->charCode
!= charCode
)
1514 infoPtr
->charCode
= charCode
= 0;
1516 infoPtr
->charCode
=charCode
;
1517 infoPtr
->szSearchParam
[0]=charCode
;
1518 infoPtr
->nSearchParamLength
=1;
1519 /* Redundant with the 1 char string */
1523 /* and search from the current position */
1525 if (infoPtr
->nFocusedItem
>= 0) {
1526 endidx
=infoPtr
->nFocusedItem
;
1528 /* if looking for single character match,
1529 * then we must always move forward
1531 if (infoPtr
->nSearchParamLength
== 1)
1534 endidx
=infoPtr
->nItemCount
;
1538 if (idx
== infoPtr
->nItemCount
) {
1539 if (endidx
== infoPtr
->nItemCount
|| endidx
== 0)
1545 item
.mask
= LVIF_TEXT
;
1548 item
.pszText
= buffer
;
1549 item
.cchTextMax
= MAX_PATH
;
1550 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
1552 /* check for a match */
1553 if (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,infoPtr
->nSearchParamLength
) == 0) {
1556 } else if ( (charCode
!= 0) && (nItem
== -1) && (nItem
!= infoPtr
->nFocusedItem
) &&
1557 (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,1) == 0) ) {
1558 /* This would work but we must keep looking for a longer match */
1562 } while (idx
!= endidx
);
1565 LISTVIEW_KeySelection(infoPtr
, nItem
);
1570 /*************************************************************************
1571 * LISTVIEW_UpdateHeaderSize [Internal]
1573 * Function to resize the header control
1576 * [I] hwnd : handle to a window
1577 * [I] nNewScrollPos : scroll pos to set
1582 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
1587 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
1589 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
1590 point
[0].x
= winRect
.left
;
1591 point
[0].y
= winRect
.top
;
1592 point
[1].x
= winRect
.right
;
1593 point
[1].y
= winRect
.bottom
;
1595 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
1596 point
[0].x
= -nNewScrollPos
;
1597 point
[1].x
+= nNewScrollPos
;
1599 SetWindowPos(infoPtr
->hwndHeader
,0,
1600 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
1601 SWP_NOZORDER
| SWP_NOACTIVATE
);
1606 * Update the scrollbars. This functions should be called whenever
1607 * the content, size or view changes.
1610 * [I] infoPtr : valid pointer to the listview structure
1615 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO
*infoPtr
)
1617 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1618 SCROLLINFO horzInfo
, vertInfo
;
1620 if ((infoPtr
->dwStyle
& LVS_NOSCROLL
) || !is_redrawing(infoPtr
)) return;
1622 ZeroMemory(&horzInfo
, sizeof(SCROLLINFO
));
1623 horzInfo
.cbSize
= sizeof(SCROLLINFO
);
1624 horzInfo
.nPage
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1626 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1627 if (uView
== LVS_LIST
)
1629 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
1630 horzInfo
.nMax
= (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
;
1632 /* scroll by at least one column per page */
1633 if(horzInfo
.nPage
< infoPtr
->nItemWidth
)
1634 horzInfo
.nPage
= infoPtr
->nItemWidth
;
1636 horzInfo
.nPage
/= infoPtr
->nItemWidth
;
1638 else if (uView
== LVS_REPORT
)
1640 horzInfo
.nMax
= infoPtr
->nItemWidth
;
1642 else /* LVS_ICON, or LVS_SMALLICON */
1646 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) horzInfo
.nMax
= rcView
.right
- rcView
.left
;
1649 horzInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1650 horzInfo
.nMax
= max(horzInfo
.nMax
- 1, 0);
1651 SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
, TRUE
);
1652 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo
));
1654 /* Setting the horizontal scroll can change the listview size
1655 * (and potentially everything else) so we need to recompute
1656 * everything again for the vertical scroll
1659 ZeroMemory(&vertInfo
, sizeof(SCROLLINFO
));
1660 vertInfo
.cbSize
= sizeof(SCROLLINFO
);
1661 vertInfo
.nPage
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1663 if (uView
== LVS_REPORT
)
1665 vertInfo
.nMax
= infoPtr
->nItemCount
;
1667 /* scroll by at least one page */
1668 if(vertInfo
.nPage
< infoPtr
->nItemHeight
)
1669 vertInfo
.nPage
= infoPtr
->nItemHeight
;
1671 vertInfo
.nPage
/= infoPtr
->nItemHeight
;
1673 else if (uView
!= LVS_LIST
) /* LVS_ICON, or LVS_SMALLICON */
1677 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) vertInfo
.nMax
= rcView
.bottom
- rcView
.top
;
1680 vertInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1681 vertInfo
.nMax
= max(vertInfo
.nMax
- 1, 0);
1682 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &vertInfo
, TRUE
);
1683 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo
));
1685 /* Update the Header Control */
1686 if (uView
== LVS_REPORT
)
1688 horzInfo
.fMask
= SIF_POS
;
1689 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
);
1690 LISTVIEW_UpdateHeaderSize(infoPtr
, horzInfo
.nPos
);
1697 * Shows/hides the focus rectangle.
1700 * [I] infoPtr : valid pointer to the listview structure
1701 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1706 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO
*infoPtr
, BOOL fShow
)
1708 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1711 TRACE("fShow=%d, nItem=%d\n", fShow
, infoPtr
->nFocusedItem
);
1713 if (infoPtr
->nFocusedItem
< 0) return;
1715 /* we need some gymnastics in ICON mode to handle large items */
1716 if ( (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
1720 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcBox
);
1721 if ((rcBox
.bottom
- rcBox
.top
) > infoPtr
->nItemHeight
)
1723 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1728 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) return;
1730 /* for some reason, owner draw should work only in report mode */
1731 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
1736 item
.iItem
= infoPtr
->nFocusedItem
;
1738 item
.mask
= LVIF_PARAM
;
1739 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto done
;
1741 ZeroMemory(&dis
, sizeof(dis
));
1742 dis
.CtlType
= ODT_LISTVIEW
;
1743 dis
.CtlID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1744 dis
.itemID
= item
.iItem
;
1745 dis
.itemAction
= ODA_FOCUS
;
1746 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
1747 dis
.hwndItem
= infoPtr
->hwndSelf
;
1749 LISTVIEW_GetItemBox(infoPtr
, dis
.itemID
, &dis
.rcItem
);
1750 dis
.itemData
= item
.lParam
;
1752 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
1756 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
1759 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1763 * Invalidates all visible selected items.
1765 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO
*infoPtr
)
1769 iterator_frameditems(&i
, infoPtr
, &infoPtr
->rcList
);
1770 while(iterator_next(&i
))
1772 if (LISTVIEW_GetItemState(infoPtr
, i
.nItem
, LVIS_SELECTED
))
1773 LISTVIEW_InvalidateItem(infoPtr
, i
.nItem
);
1775 iterator_destroy(&i
);
1780 * DESCRIPTION: [INTERNAL]
1781 * Computes an item's (left,top) corner, relative to rcView.
1782 * That is, the position has NOT been made relative to the Origin.
1783 * This is deliberate, to avoid computing the Origin over, and
1784 * over again, when this function is call in a loop. Instead,
1785 * one ca factor the computation of the Origin before the loop,
1786 * and offset the value retured by this function, on every iteration.
1789 * [I] infoPtr : valid pointer to the listview structure
1790 * [I] nItem : item number
1791 * [O] lpptOrig : item top, left corner
1796 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
1798 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1800 assert(nItem
>= 0 && nItem
< infoPtr
->nItemCount
);
1802 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1804 lpptPosition
->x
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1805 lpptPosition
->y
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1807 else if (uView
== LVS_LIST
)
1809 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
1810 lpptPosition
->x
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
1811 lpptPosition
->y
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
1813 else /* LVS_REPORT */
1815 lpptPosition
->x
= 0;
1816 lpptPosition
->y
= nItem
* infoPtr
->nItemHeight
;
1821 * DESCRIPTION: [INTERNAL]
1822 * Compute the rectangles of an item. This is to localize all
1823 * the computations in one place. If you are not interested in some
1824 * of these values, simply pass in a NULL -- the fucntion is smart
1825 * enough to compute only what's necessary. The function computes
1826 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1827 * one, the BOX rectangle. This rectangle is very cheap to compute,
1828 * and is guaranteed to contain all the other rectangles. Computing
1829 * the ICON rect is also cheap, but all the others are potentaily
1830 * expensive. This gives an easy and effective optimization when
1831 * searching (like point inclusion, or rectangle intersection):
1832 * first test against the BOX, and if TRUE, test agains the desired
1834 * If the function does not have all the necessary information
1835 * to computed the requested rectangles, will crash with a
1836 * failed assertion. This is done so we catch all programming
1837 * errors, given that the function is called only from our code.
1839 * We have the following 'special' meanings for a few fields:
1840 * * If LVIS_FOCUSED is set, we assume the item has the focus
1841 * This is important in ICON mode, where it might get a larger
1842 * then usual rectange
1844 * Please note that subitem support works only in REPORT mode.
1847 * [I] infoPtr : valid pointer to the listview structure
1848 * [I] lpLVItem : item to compute the measures for
1849 * [O] lprcBox : ptr to Box rectangle
1850 * The internal LVIR_BOX rectangle
1851 * [0] lprcState : ptr to State icon rectangle
1852 * The internal LVIR_STATE rectangle
1853 * [O] lprcIcon : ptr to Icon rectangle
1854 * Same as LVM_GETITEMRECT with LVIR_ICON
1855 * [O] lprcLabel : ptr to Label rectangle
1856 * Same as LVM_GETITEMRECT with LVIR_LABEL
1861 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
,
1862 LPRECT lprcBox
, LPRECT lprcState
,
1863 LPRECT lprcIcon
, LPRECT lprcLabel
)
1865 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1866 BOOL doState
= FALSE
, doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
1867 RECT Box
, State
, Icon
, Label
;
1868 COLUMN_INFO
*lpColumnInfo
= NULL
;
1870 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem
, TRUE
));
1872 /* Be smart and try to figure out the minimum we have to do */
1873 if (lpLVItem
->iSubItem
) assert(uView
== LVS_REPORT
);
1874 if (uView
== LVS_ICON
&& (lprcBox
|| lprcLabel
))
1876 assert((lpLVItem
->mask
& LVIF_STATE
) && (lpLVItem
->stateMask
& LVIS_FOCUSED
));
1877 if (lpLVItem
->state
& LVIS_FOCUSED
) oversizedBox
= doLabel
= TRUE
;
1879 if (lprcLabel
) doLabel
= TRUE
;
1880 if (doLabel
|| lprcIcon
) doIcon
= TRUE
;
1881 if (doIcon
|| lprcState
) doState
= TRUE
;
1883 /************************************************************/
1884 /* compute the box rectangle (it should be cheap to do) */
1885 /************************************************************/
1886 if (lpLVItem
->iSubItem
|| uView
== LVS_REPORT
)
1887 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpLVItem
->iSubItem
);
1889 if (lpLVItem
->iSubItem
)
1891 Box
= lpColumnInfo
->rcHeader
;
1896 Box
.right
= infoPtr
->nItemWidth
;
1899 Box
.bottom
= infoPtr
->nItemHeight
;
1901 /************************************************************/
1902 /* compute STATEICON bounding box */
1903 /************************************************************/
1906 if (uView
== LVS_ICON
)
1908 State
.left
= Box
.left
- infoPtr
->iconStateSize
.cx
- 2;
1909 if (infoPtr
->himlNormal
)
1910 State
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
1911 State
.top
= Box
.top
+ infoPtr
->iconSize
.cy
- infoPtr
->iconStateSize
.cy
+ 4;
1915 /* we need the ident in report mode, if we don't have it, we fail */
1916 State
.left
= Box
.left
;
1917 if (uView
== LVS_REPORT
)
1919 if (lpLVItem
->iSubItem
== 0)
1921 State
.left
+= REPORT_MARGINX
;
1922 assert(lpLVItem
->mask
& LVIF_INDENT
);
1923 State
.left
+= infoPtr
->iconSize
.cx
* lpLVItem
->iIndent
;
1926 State
.top
= Box
.top
;
1928 State
.right
= State
.left
;
1929 State
.bottom
= State
.top
;
1930 if (infoPtr
->himlState
&& lpLVItem
->iSubItem
== 0)
1932 State
.right
+= infoPtr
->iconStateSize
.cx
;
1933 State
.bottom
+= infoPtr
->iconStateSize
.cy
;
1935 if (lprcState
) *lprcState
= State
;
1936 TRACE(" - state=%s\n", debugrect(&State
));
1939 /************************************************************/
1940 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1941 /************************************************************/
1944 if (uView
== LVS_ICON
)
1946 Icon
.left
= Box
.left
;
1947 if (infoPtr
->himlNormal
)
1948 Icon
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
1949 Icon
.top
= Box
.top
+ ICON_TOP_PADDING
;
1950 Icon
.right
= Icon
.left
;
1951 Icon
.bottom
= Icon
.top
;
1952 if (infoPtr
->himlNormal
)
1954 Icon
.right
+= infoPtr
->iconSize
.cx
;
1955 Icon
.bottom
+= infoPtr
->iconSize
.cy
;
1958 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1960 Icon
.left
= State
.right
;
1962 Icon
.right
= Icon
.left
;
1963 if (infoPtr
->himlSmall
&&
1964 (!lpColumnInfo
|| lpLVItem
->iSubItem
== 0 || (lpColumnInfo
->fmt
& LVCFMT_IMAGE
) ||
1965 ((infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
) && lpLVItem
->iImage
!= I_IMAGECALLBACK
)))
1966 Icon
.right
+= infoPtr
->iconSize
.cx
;
1967 Icon
.bottom
= Icon
.top
+ infoPtr
->nItemHeight
;
1969 if(lprcIcon
) *lprcIcon
= Icon
;
1970 TRACE(" - icon=%s\n", debugrect(&Icon
));
1973 /************************************************************/
1974 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1975 /************************************************************/
1978 SIZE labelSize
= { 0, 0 };
1980 /* calculate how far to the right can the label strech */
1981 Label
.right
= Box
.right
;
1982 if (uView
== LVS_REPORT
)
1984 if (lpLVItem
->iSubItem
== 0) Label
= lpColumnInfo
->rcHeader
;
1987 if (lpLVItem
->iSubItem
|| ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && uView
== LVS_REPORT
))
1989 labelSize
.cx
= infoPtr
->nItemWidth
;
1990 labelSize
.cy
= infoPtr
->nItemHeight
;
1994 /* we need the text in non owner draw mode */
1995 assert(lpLVItem
->mask
& LVIF_TEXT
);
1996 if (is_textT(lpLVItem
->pszText
, TRUE
))
1998 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
1999 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2000 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2004 /* compute rough rectangle where the label will go */
2005 SetRectEmpty(&rcText
);
2006 rcText
.right
= infoPtr
->nItemWidth
- TRAILING_LABEL_PADDING
;
2007 rcText
.bottom
= infoPtr
->nItemHeight
;
2008 if (uView
== LVS_ICON
)
2009 rcText
.bottom
-= ICON_TOP_PADDING
+ infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2011 /* now figure out the flags */
2012 if (uView
== LVS_ICON
)
2013 uFormat
= oversizedBox
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
;
2015 uFormat
= LV_SL_DT_FLAGS
;
2017 DrawTextW (hdc
, lpLVItem
->pszText
, -1, &rcText
, uFormat
| DT_CALCRECT
);
2019 labelSize
.cx
= min(rcText
.right
- rcText
.left
+ TRAILING_LABEL_PADDING
, infoPtr
->nItemWidth
);
2020 labelSize
.cy
= rcText
.bottom
- rcText
.top
;
2022 SelectObject(hdc
, hOldFont
);
2023 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2027 if (uView
== LVS_ICON
)
2029 Label
.left
= Box
.left
+ (infoPtr
->nItemWidth
- labelSize
.cx
) / 2;
2030 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
2031 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2032 Label
.right
= Label
.left
+ labelSize
.cx
;
2033 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2034 if (!oversizedBox
&& labelSize
.cy
> infoPtr
->ntmHeight
&&
2037 labelSize
.cy
= min(Box
.bottom
- Label
.top
, labelSize
.cy
);
2038 labelSize
.cy
/= infoPtr
->ntmHeight
;
2039 labelSize
.cy
= max(labelSize
.cy
, 1);
2040 labelSize
.cy
*= infoPtr
->ntmHeight
;
2042 Label
.bottom
= Label
.top
+ labelSize
.cy
+ HEIGHT_PADDING
;
2044 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2046 Label
.left
= Icon
.right
;
2047 Label
.top
= Box
.top
;
2048 Label
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2049 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2052 if (lprcLabel
) *lprcLabel
= Label
;
2053 TRACE(" - label=%s\n", debugrect(&Label
));
2056 /* Fix the Box if necessary */
2059 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
2060 else *lprcBox
= Box
;
2062 TRACE(" - box=%s\n", debugrect(&Box
));
2066 * DESCRIPTION: [INTERNAL]
2069 * [I] infoPtr : valid pointer to the listview structure
2070 * [I] nItem : item number
2071 * [O] lprcBox : ptr to Box rectangle
2076 static void LISTVIEW_GetItemBox(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprcBox
)
2078 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2079 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2080 POINT Position
, Origin
;
2083 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
2084 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
2086 /* Be smart and try to figure out the minimum we have to do */
2088 if (uView
== LVS_ICON
&& infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
2089 lvItem
.mask
|= LVIF_TEXT
;
2090 lvItem
.iItem
= nItem
;
2091 lvItem
.iSubItem
= 0;
2092 lvItem
.pszText
= szDispText
;
2093 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2094 if (lvItem
.mask
) LISTVIEW_GetItemW(infoPtr
, &lvItem
);
2095 if (uView
== LVS_ICON
)
2097 lvItem
.mask
|= LVIF_STATE
;
2098 lvItem
.stateMask
= LVIS_FOCUSED
;
2099 lvItem
.state
= (lvItem
.mask
& LVIF_TEXT
? LVIS_FOCUSED
: 0);
2101 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprcBox
, 0, 0, 0);
2103 OffsetRect(lprcBox
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
2109 * Returns the current icon position, and advances it along the top.
2110 * The returned position is not offset by Origin.
2113 * [I] infoPtr : valid pointer to the listview structure
2114 * [O] lpPos : will get the current icon position
2119 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2121 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2123 *lpPos
= infoPtr
->currIconPos
;
2125 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2126 if (infoPtr
->currIconPos
.x
+ infoPtr
->nItemWidth
<= nListWidth
) return;
2128 infoPtr
->currIconPos
.x
= 0;
2129 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2135 * Returns the current icon position, and advances it down the left edge.
2136 * The returned position is not offset by Origin.
2139 * [I] infoPtr : valid pointer to the listview structure
2140 * [O] lpPos : will get the current icon position
2145 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2147 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2149 *lpPos
= infoPtr
->currIconPos
;
2151 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2152 if (infoPtr
->currIconPos
.y
+ infoPtr
->nItemHeight
<= nListHeight
) return;
2154 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2155 infoPtr
->currIconPos
.y
= 0;
2161 * Moves an icon to the specified position.
2162 * It takes care of invalidating the item, etc.
2165 * [I] infoPtr : valid pointer to the listview structure
2166 * [I] nItem : the item to move
2167 * [I] lpPos : the new icon position
2168 * [I] isNew : flags the item as being new
2174 static BOOL
LISTVIEW_MoveIconTo(LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*lppt
, BOOL isNew
)
2180 old
.x
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2181 old
.y
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2183 if (lppt
->x
== old
.x
&& lppt
->y
== old
.y
) return TRUE
;
2184 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2187 /* Allocating a POINTER for every item is too resource intensive,
2188 * so we'll keep the (x,y) in different arrays */
2189 if (!DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)lppt
->x
)) return FALSE
;
2190 if (!DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)lppt
->y
)) return FALSE
;
2192 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2199 * Arranges listview items in icon display mode.
2202 * [I] infoPtr : valid pointer to the listview structure
2203 * [I] nAlignCode : alignment code
2209 static BOOL
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
2211 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2212 void (*next_pos
)(LISTVIEW_INFO
*, LPPOINT
);
2216 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
) return FALSE
;
2218 TRACE("nAlignCode=%d\n", nAlignCode
);
2220 if (nAlignCode
== LVA_DEFAULT
)
2222 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
) nAlignCode
= LVA_ALIGNLEFT
;
2223 else nAlignCode
= LVA_ALIGNTOP
;
2228 case LVA_ALIGNLEFT
: next_pos
= LISTVIEW_NextIconPosLeft
; break;
2229 case LVA_ALIGNTOP
: next_pos
= LISTVIEW_NextIconPosTop
; break;
2230 case LVA_SNAPTOGRID
: next_pos
= LISTVIEW_NextIconPosTop
; break; /* FIXME */
2231 default: return FALSE
;
2234 infoPtr
->bAutoarrange
= TRUE
;
2235 infoPtr
->currIconPos
.x
= infoPtr
->currIconPos
.y
= 0;
2236 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2238 next_pos(infoPtr
, &pos
);
2239 LISTVIEW_MoveIconTo(infoPtr
, i
, &pos
, FALSE
);
2247 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2250 * [I] infoPtr : valid pointer to the listview structure
2251 * [O] lprcView : bounding rectangle
2257 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2261 SetRectEmpty(lprcView
);
2263 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
2267 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2269 x
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosX
, i
);
2270 y
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosY
, i
);
2271 lprcView
->right
= max(lprcView
->right
, x
);
2272 lprcView
->bottom
= max(lprcView
->bottom
, y
);
2274 if (infoPtr
->nItemCount
> 0)
2276 lprcView
->right
+= infoPtr
->nItemWidth
;
2277 lprcView
->bottom
+= infoPtr
->nItemHeight
;
2282 y
= LISTVIEW_GetCountPerColumn(infoPtr
);
2283 x
= infoPtr
->nItemCount
/ y
;
2284 if (infoPtr
->nItemCount
% y
) x
++;
2285 lprcView
->right
= x
* infoPtr
->nItemWidth
;
2286 lprcView
->bottom
= y
* infoPtr
->nItemHeight
;
2290 lprcView
->right
= infoPtr
->nItemWidth
;
2291 lprcView
->bottom
= infoPtr
->nItemCount
* infoPtr
->nItemHeight
;
2298 * Retrieves the bounding rectangle of all the items.
2301 * [I] infoPtr : valid pointer to the listview structure
2302 * [O] lprcView : bounding rectangle
2308 static BOOL
LISTVIEW_GetViewRect(LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2312 TRACE("(lprcView=%p)\n", lprcView
);
2314 if (!lprcView
) return FALSE
;
2316 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
2317 LISTVIEW_GetAreaRect(infoPtr
, lprcView
);
2318 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
2320 TRACE("lprcView=%s\n", debugrect(lprcView
));
2327 * Retrieves the subitem pointer associated with the subitem index.
2330 * [I] hdpaSubItems : DPA handle for a specific item
2331 * [I] nSubItem : index of subitem
2334 * SUCCESS : subitem pointer
2337 static SUBITEM_INFO
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
, INT nSubItem
)
2339 SUBITEM_INFO
*lpSubItem
;
2342 /* we should binary search here if need be */
2343 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
2345 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
2346 if (lpSubItem
->iSubItem
== nSubItem
)
2356 * Caclulates the desired item width.
2359 * [I] infoPtr : valid pointer to the listview structure
2362 * The desired item width.
2364 static INT
LISTVIEW_CalculateItemWidth(LISTVIEW_INFO
*infoPtr
)
2366 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2369 TRACE("uView=%d\n", uView
);
2371 if (uView
== LVS_ICON
)
2372 nItemWidth
= infoPtr
->iconSpacing
.cx
;
2373 else if (uView
== LVS_REPORT
)
2377 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2379 LISTVIEW_GetHeaderRect(infoPtr
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, &rcHeader
);
2380 nItemWidth
= rcHeader
.right
;
2383 else /* LVS_SMALLICON, or LVS_LIST */
2387 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2388 nItemWidth
= max(LISTVIEW_GetLabelWidth(infoPtr
, i
), nItemWidth
);
2390 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
2391 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
2393 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
+ WIDTH_PADDING
);
2396 return max(nItemWidth
, 1);
2401 * Caclulates the desired item height.
2404 * [I] infoPtr : valid pointer to the listview structure
2407 * The desired item height.
2409 static INT
LISTVIEW_CalculateItemHeight(LISTVIEW_INFO
*infoPtr
)
2411 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2414 TRACE("uView=%d\n", uView
);
2416 if (uView
== LVS_ICON
)
2417 nItemHeight
= infoPtr
->iconSpacing
.cy
;
2420 nItemHeight
= infoPtr
->ntmHeight
;
2421 if (infoPtr
->himlState
)
2422 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
2423 if (infoPtr
->himlSmall
)
2424 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
2425 if (infoPtr
->himlState
|| infoPtr
->himlSmall
)
2426 nItemHeight
+= HEIGHT_PADDING
;
2429 return max(nItemHeight
, 1);
2434 * Updates the width, and height of an item.
2437 * [I] infoPtr : valid pointer to the listview structure
2442 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO
*infoPtr
)
2444 infoPtr
->nItemWidth
= LISTVIEW_CalculateItemWidth(infoPtr
);
2445 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
2451 * Retrieves and saves important text metrics info for the current
2455 * [I] infoPtr : valid pointer to the listview structure
2458 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
2460 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2461 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2462 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2465 if (GetTextMetricsW(hdc
, &tm
))
2467 infoPtr
->ntmHeight
= tm
.tmHeight
;
2468 infoPtr
->ntmAveCharWidth
= tm
.tmAveCharWidth
;
2470 SelectObject(hdc
, hOldFont
);
2471 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2473 TRACE("tmHeight=%d\n", infoPtr
->ntmHeight
);
2478 * A compare function for ranges
2481 * [I] range1 : pointer to range 1;
2482 * [I] range2 : pointer to range 2;
2486 * > 0 : if range 1 > range 2
2487 * < 0 : if range 2 > range 1
2488 * = 0 : if range intersects range 2
2490 static INT CALLBACK
ranges_cmp(LPVOID range1
, LPVOID range2
, LPARAM flags
)
2494 if (((RANGE
*)range1
)->upper
<= ((RANGE
*)range2
)->lower
)
2496 else if (((RANGE
*)range2
)->upper
<= ((RANGE
*)range1
)->lower
)
2501 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE
*)range1
), debugrange((RANGE
*)range2
), cmp
);
2507 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2509 #define ranges_check(ranges, desc) do { } while(0)
2512 static void ranges_assert(RANGES ranges
, LPCSTR desc
, const char *func
, int line
)
2517 TRACE("*** Checking %s:%d:%s ***\n", func
, line
, desc
);
2519 assert (DPA_GetPtrCount(ranges
->hdpa
) >= 0);
2520 ranges_dump(ranges
);
2521 prev
= (RANGE
*)DPA_GetPtr(ranges
->hdpa
, 0);
2522 if (DPA_GetPtrCount(ranges
->hdpa
) > 0)
2523 assert (prev
->lower
>= 0 && prev
->lower
< prev
->upper
);
2524 for (i
= 1; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2526 curr
= (RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
);
2527 assert (prev
->upper
<= curr
->lower
);
2528 assert (curr
->lower
< curr
->upper
);
2531 TRACE("--- Done checking---\n");
2534 static RANGES
ranges_create(int count
)
2536 RANGES ranges
= (RANGES
)Alloc(sizeof(struct tagRANGES
));
2537 if (!ranges
) return NULL
;
2538 ranges
->hdpa
= DPA_Create(count
);
2539 if (ranges
->hdpa
) return ranges
;
2544 static void ranges_clear(RANGES ranges
)
2548 for(i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2549 Free(DPA_GetPtr(ranges
->hdpa
, i
));
2550 DPA_DeleteAllPtrs(ranges
->hdpa
);
2554 static void ranges_destroy(RANGES ranges
)
2556 if (!ranges
) return;
2557 ranges_clear(ranges
);
2558 DPA_Destroy(ranges
->hdpa
);
2562 static RANGES
ranges_clone(RANGES ranges
)
2567 if (!(clone
= ranges_create(DPA_GetPtrCount(ranges
->hdpa
)))) goto fail
;
2569 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2571 RANGE
*newrng
= (RANGE
*)Alloc(sizeof(RANGE
));
2572 if (!newrng
) goto fail
;
2573 *newrng
= *((RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
));
2574 DPA_SetPtr(clone
->hdpa
, i
, newrng
);
2579 TRACE ("clone failed\n");
2580 ranges_destroy(clone
);
2584 static RANGES
ranges_diff(RANGES ranges
, RANGES sub
)
2588 for (i
= 0; i
< DPA_GetPtrCount(sub
->hdpa
); i
++)
2589 ranges_del(ranges
, *((RANGE
*)DPA_GetPtr(sub
->hdpa
, i
)));
2594 static void ranges_dump(RANGES ranges
)
2598 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2599 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges
->hdpa
, i
)));
2602 static inline BOOL
ranges_contain(RANGES ranges
, INT nItem
)
2604 RANGE srchrng
= { nItem
, nItem
+ 1 };
2606 TRACE("(nItem=%d)\n", nItem
);
2607 ranges_check(ranges
, "before contain");
2608 return DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
) != -1;
2611 static INT
ranges_itemcount(RANGES ranges
)
2615 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2617 RANGE
*sel
= DPA_GetPtr(ranges
->hdpa
, i
);
2618 count
+= sel
->upper
- sel
->lower
;
2624 static BOOL
ranges_shift(RANGES ranges
, INT nItem
, INT delta
, INT nUpper
)
2626 RANGE srchrng
= { nItem
, nItem
+ 1 }, *chkrng
;
2629 index
= DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2630 if (index
== -1) return TRUE
;
2632 for (; index
< DPA_GetPtrCount(ranges
->hdpa
); index
++)
2634 chkrng
= DPA_GetPtr(ranges
->hdpa
, index
);
2635 if (chkrng
->lower
>= nItem
)
2636 chkrng
->lower
= max(min(chkrng
->lower
+ delta
, nUpper
- 1), 0);
2637 if (chkrng
->upper
> nItem
)
2638 chkrng
->upper
= max(min(chkrng
->upper
+ delta
, nUpper
), 0);
2643 static BOOL
ranges_add(RANGES ranges
, RANGE range
)
2648 TRACE("(%s)\n", debugrange(&range
));
2649 ranges_check(ranges
, "before add");
2651 /* try find overlapping regions first */
2652 srchrgn
.lower
= range
.lower
- 1;
2653 srchrgn
.upper
= range
.upper
+ 1;
2654 index
= DPA_Search(ranges
->hdpa
, &srchrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
);
2660 TRACE("Adding new range\n");
2662 /* create the brand new range to insert */
2663 newrgn
= (RANGE
*)Alloc(sizeof(RANGE
));
2664 if(!newrgn
) goto fail
;
2667 /* figure out where to insert it */
2668 index
= DPA_Search(ranges
->hdpa
, newrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2669 TRACE("index=%d\n", index
);
2670 if (index
== -1) index
= 0;
2672 /* and get it over with */
2673 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2681 RANGE
*chkrgn
, *mrgrgn
;
2682 INT fromindex
, mergeindex
;
2684 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2685 TRACE("Merge with %s @%d\n", debugrange(chkrgn
), index
);
2687 chkrgn
->lower
= min(range
.lower
, chkrgn
->lower
);
2688 chkrgn
->upper
= max(range
.upper
, chkrgn
->upper
);
2690 TRACE("New range %s @%d\n", debugrange(chkrgn
), index
);
2692 /* merge now common anges */
2694 srchrgn
.lower
= chkrgn
->lower
- 1;
2695 srchrgn
.upper
= chkrgn
->upper
+ 1;
2699 mergeindex
= DPA_Search(ranges
->hdpa
, &srchrgn
, fromindex
, ranges_cmp
, 0, 0);
2700 if (mergeindex
== -1) break;
2701 if (mergeindex
== index
)
2703 fromindex
= index
+ 1;
2707 TRACE("Merge with index %i\n", mergeindex
);
2709 mrgrgn
= DPA_GetPtr(ranges
->hdpa
, mergeindex
);
2710 chkrgn
->lower
= min(chkrgn
->lower
, mrgrgn
->lower
);
2711 chkrgn
->upper
= max(chkrgn
->upper
, mrgrgn
->upper
);
2713 DPA_DeletePtr(ranges
->hdpa
, mergeindex
);
2714 if (mergeindex
< index
) index
--;
2718 ranges_check(ranges
, "after add");
2722 ranges_check(ranges
, "failed add");
2726 static BOOL
ranges_del(RANGES ranges
, RANGE range
)
2731 TRACE("(%s)\n", debugrange(&range
));
2732 ranges_check(ranges
, "before del");
2734 /* we don't use DPAS_SORTED here, since we need *
2735 * to find the first overlapping range */
2736 index
= DPA_Search(ranges
->hdpa
, &range
, 0, ranges_cmp
, 0, 0);
2739 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2741 TRACE("Matches range %s @%d\n", debugrange(chkrgn
), index
);
2743 /* case 1: Same range */
2744 if ( (chkrgn
->upper
== range
.upper
) &&
2745 (chkrgn
->lower
== range
.lower
) )
2747 DPA_DeletePtr(ranges
->hdpa
, index
);
2750 /* case 2: engulf */
2751 else if ( (chkrgn
->upper
<= range
.upper
) &&
2752 (chkrgn
->lower
>= range
.lower
) )
2754 DPA_DeletePtr(ranges
->hdpa
, index
);
2756 /* case 3: overlap upper */
2757 else if ( (chkrgn
->upper
<= range
.upper
) &&
2758 (chkrgn
->lower
< range
.lower
) )
2760 chkrgn
->upper
= range
.lower
;
2762 /* case 4: overlap lower */
2763 else if ( (chkrgn
->upper
> range
.upper
) &&
2764 (chkrgn
->lower
>= range
.lower
) )
2766 chkrgn
->lower
= range
.upper
;
2769 /* case 5: fully internal */
2772 RANGE tmprgn
= *chkrgn
, *newrgn
;
2774 if (!(newrgn
= (RANGE
*)Alloc(sizeof(RANGE
)))) goto fail
;
2775 newrgn
->lower
= chkrgn
->lower
;
2776 newrgn
->upper
= range
.lower
;
2777 chkrgn
->lower
= range
.upper
;
2778 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2787 index
= DPA_Search(ranges
->hdpa
, &range
, index
, ranges_cmp
, 0, 0);
2790 ranges_check(ranges
, "after del");
2794 ranges_check(ranges
, "failed del");
2800 * Removes all selection ranges
2803 * [I] infoPtr : valid pointer to the listview structure
2804 * [I] toSkip : item range to skip removing the selection
2810 static BOOL
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO
*infoPtr
, RANGES toSkip
)
2819 lvItem
.stateMask
= LVIS_SELECTED
;
2821 /* need to clone the DPA because callbacks can change it */
2822 if (!(clone
= ranges_clone(infoPtr
->selectionRanges
))) return FALSE
;
2823 iterator_rangesitems(&i
, ranges_diff(clone
, toSkip
));
2824 while(iterator_next(&i
))
2825 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &lvItem
);
2826 /* note that the iterator destructor will free the cloned range */
2827 iterator_destroy(&i
);
2832 static inline BOOL
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2836 if (!(toSkip
= ranges_create(1))) return FALSE
;
2837 if (nItem
!= -1) ranges_additem(toSkip
, nItem
);
2838 LISTVIEW_DeselectAllSkipItems(infoPtr
, toSkip
);
2839 ranges_destroy(toSkip
);
2843 static inline BOOL
LISTVIEW_DeselectAll(LISTVIEW_INFO
*infoPtr
)
2845 return LISTVIEW_DeselectAllSkipItem(infoPtr
, -1);
2850 * Retrieves the number of items that are marked as selected.
2853 * [I] infoPtr : valid pointer to the listview structure
2856 * Number of items selected.
2858 static INT
LISTVIEW_GetSelectedCount(LISTVIEW_INFO
*infoPtr
)
2860 INT nSelectedCount
= 0;
2862 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
2865 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2867 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
2872 nSelectedCount
= ranges_itemcount(infoPtr
->selectionRanges
);
2874 TRACE("nSelectedCount=%d\n", nSelectedCount
);
2875 return nSelectedCount
;
2880 * Manages the item focus.
2883 * [I] infoPtr : valid pointer to the listview structure
2884 * [I] nItem : item index
2887 * TRUE : focused item changed
2888 * FALSE : focused item has NOT changed
2890 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2892 INT oldFocus
= infoPtr
->nFocusedItem
;
2895 if (nItem
== infoPtr
->nFocusedItem
) return FALSE
;
2897 lvItem
.state
= nItem
== -1 ? 0 : LVIS_FOCUSED
;
2898 lvItem
.stateMask
= LVIS_FOCUSED
;
2899 LISTVIEW_SetItemState(infoPtr
, nItem
== -1 ? infoPtr
->nFocusedItem
: nItem
, &lvItem
);
2901 return oldFocus
!= infoPtr
->nFocusedItem
;
2904 /* Helper function for LISTVIEW_ShiftIndices *only* */
2905 static INT
shift_item(LISTVIEW_INFO
*infoPtr
, INT nShiftItem
, INT nItem
, INT direction
)
2907 if (nShiftItem
< nItem
) return nShiftItem
;
2909 if (nShiftItem
> nItem
) return nShiftItem
+ direction
;
2911 if (direction
> 0) return nShiftItem
+ direction
;
2913 return min(nShiftItem
, infoPtr
->nItemCount
- 1);
2918 * Updates the various indices after an item has been inserted or deleted.
2921 * [I] infoPtr : valid pointer to the listview structure
2922 * [I] nItem : item index
2923 * [I] direction : Direction of shift, +1 or -1.
2928 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
2933 /* temporarily disable change notification while shifting items */
2934 bOldChange
= infoPtr
->bDoChangeNotify
;
2935 infoPtr
->bDoChangeNotify
= FALSE
;
2937 TRACE("Shifting %iu, %i steps\n", nItem
, direction
);
2939 ranges_shift(infoPtr
->selectionRanges
, nItem
, direction
, infoPtr
->nItemCount
);
2941 assert(abs(direction
) == 1);
2943 infoPtr
->nSelectionMark
= shift_item(infoPtr
, infoPtr
->nSelectionMark
, nItem
, direction
);
2945 nNewFocus
= shift_item(infoPtr
, infoPtr
->nFocusedItem
, nItem
, direction
);
2946 if (nNewFocus
!= infoPtr
->nFocusedItem
)
2947 LISTVIEW_SetItemFocus(infoPtr
, nNewFocus
);
2949 /* But we are not supposed to modify nHotItem! */
2951 infoPtr
->bDoChangeNotify
= bOldChange
;
2957 * Adds a block of selections.
2960 * [I] infoPtr : valid pointer to the listview structure
2961 * [I] nItem : item index
2966 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2968 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
2969 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
2970 NMLVODSTATECHANGE nmlv
;
2975 /* Temporarily disable change notification
2976 * If the control is LVS_OWNERDATA, we need to send
2977 * only one LVN_ODSTATECHANGED notification.
2978 * See MSDN documentation for LVN_ITEMCHANGED.
2980 bOldChange
= infoPtr
->bDoChangeNotify
;
2981 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
2983 if (nFirst
== -1) nFirst
= nItem
;
2985 item
.state
= LVIS_SELECTED
;
2986 item
.stateMask
= LVIS_SELECTED
;
2988 for (i
= nFirst
; i
<= nLast
; i
++)
2989 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
2991 ZeroMemory(&nmlv
, sizeof(nmlv
));
2992 nmlv
.iFrom
= nFirst
;
2995 nmlv
.uOldState
= item
.state
;
2997 notify_hdr(infoPtr
, LVN_ODSTATECHANGED
, (LPNMHDR
)&nmlv
);
2998 infoPtr
->bDoChangeNotify
= bOldChange
;
3004 * Sets a single group selection.
3007 * [I] infoPtr : valid pointer to the listview structure
3008 * [I] nItem : item index
3013 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3015 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3020 if (!(selection
= ranges_create(100))) return;
3022 item
.state
= LVIS_SELECTED
;
3023 item
.stateMask
= LVIS_SELECTED
;
3025 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
3027 if (infoPtr
->nSelectionMark
== -1)
3029 infoPtr
->nSelectionMark
= nItem
;
3030 ranges_additem(selection
, nItem
);
3036 sel
.lower
= min(infoPtr
->nSelectionMark
, nItem
);
3037 sel
.upper
= max(infoPtr
->nSelectionMark
, nItem
) + 1;
3038 ranges_add(selection
, sel
);
3043 RECT rcItem
, rcSel
, rcSelMark
;
3046 rcItem
.left
= LVIR_BOUNDS
;
3047 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return;
3048 rcSelMark
.left
= LVIR_BOUNDS
;
3049 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) return;
3050 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
3051 iterator_frameditems(&i
, infoPtr
, &rcSel
);
3052 while(iterator_next(&i
))
3054 LISTVIEW_GetItemPosition(infoPtr
, i
.nItem
, &ptItem
);
3055 if (PtInRect(&rcSel
, ptItem
)) ranges_additem(selection
, i
.nItem
);
3057 iterator_destroy(&i
);
3060 LISTVIEW_DeselectAllSkipItems(infoPtr
, selection
);
3061 iterator_rangesitems(&i
, selection
);
3062 while(iterator_next(&i
))
3063 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &item
);
3064 /* this will also destroy the selection */
3065 iterator_destroy(&i
);
3067 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3072 * Sets a single selection.
3075 * [I] infoPtr : valid pointer to the listview structure
3076 * [I] nItem : item index
3081 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3085 TRACE("nItem=%d\n", nItem
);
3087 LISTVIEW_DeselectAllSkipItem(infoPtr
, nItem
);
3089 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3090 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3091 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3093 infoPtr
->nSelectionMark
= nItem
;
3098 * Set selection(s) with keyboard.
3101 * [I] infoPtr : valid pointer to the listview structure
3102 * [I] nItem : item index
3105 * SUCCESS : TRUE (needs to be repainted)
3106 * FAILURE : FALSE (nothing has changed)
3108 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3110 /* FIXME: pass in the state */
3111 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
3112 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
3113 BOOL bResult
= FALSE
;
3115 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3117 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
3120 LISTVIEW_SetSelection(infoPtr
, nItem
);
3127 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3131 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3136 LISTVIEW_SetSelection(infoPtr
, nItem
);
3139 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3142 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3149 * Called when the mouse is being actively tracked and has hovered for a specified
3153 * [I] infoPtr : valid pointer to the listview structure
3154 * [I] fwKeys : key indicator
3155 * [I] x,y : mouse position
3158 * 0 if the message was processed, non-zero if there was an error
3161 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3162 * over the item for a certain period of time.
3165 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, WORD fwKyes
, INT x
, INT y
)
3167 if(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)
3168 /* FIXME: select the item!!! */
3169 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3176 * Called whenever WM_MOUSEMOVE is received.
3179 * [I] infoPtr : valid pointer to the listview structure
3180 * [I] fwKeys : key indicator
3181 * [I] x,y : mouse position
3184 * 0 if the message is processed, non-zero if there was an error
3186 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3188 TRACKMOUSEEVENT trackinfo
;
3190 /* see if we are supposed to be tracking mouse hovering */
3191 if(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) {
3192 /* fill in the trackinfo struct */
3193 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3194 trackinfo
.dwFlags
= TME_QUERY
;
3195 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
3196 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
3198 /* see if we are already tracking this hwnd */
3199 _TrackMouseEvent(&trackinfo
);
3201 if(!(trackinfo
.dwFlags
& TME_HOVER
)) {
3202 trackinfo
.dwFlags
= TME_HOVER
;
3204 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3205 _TrackMouseEvent(&trackinfo
);
3214 * Tests wheather the item is assignable to a list with style lStyle
3216 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
3218 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
3219 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
3220 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
3228 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3231 * [I] infoPtr : valid pointer to the listview structure
3232 * [I] lpLVItem : valid pointer to new item atttributes
3233 * [I] isNew : the item being set is being inserted
3234 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3235 * [O] bChanged : will be set to TRUE if the item really changed
3241 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
3243 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3251 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
3253 if (lpLVItem
->mask
== 0) return TRUE
;
3255 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
3257 /* a virtual listview we stores only selection and focus */
3258 if (lpLVItem
->mask
& ~LVIF_STATE
)
3264 HDPA hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3265 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
3269 /* we need to get the lParam and state of the item */
3270 item
.iItem
= lpLVItem
->iItem
;
3271 item
.iSubItem
= lpLVItem
->iSubItem
;
3272 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
3273 item
.stateMask
= ~0;
3276 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
3278 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
3279 /* determine what fields will change */
3280 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
))
3281 uChanged
|= LVIF_STATE
;
3283 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
3284 uChanged
|= LVIF_IMAGE
;
3286 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
3287 uChanged
|= LVIF_PARAM
;
3289 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
3290 uChanged
|= LVIF_INDENT
;
3292 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
3293 uChanged
|= LVIF_TEXT
;
3295 TRACE("uChanged=0x%x\n", uChanged
);
3296 if (!uChanged
) return TRUE
;
3299 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
3300 nmlv
.iItem
= lpLVItem
->iItem
;
3301 nmlv
.uNewState
= (item
.state
& ~lpLVItem
->stateMask
) | (lpLVItem
->state
& lpLVItem
->stateMask
);
3302 nmlv
.uOldState
= item
.state
;
3303 nmlv
.uChanged
= uChanged
;
3304 nmlv
.lParam
= item
.lParam
;
3306 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3307 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3309 if(lpItem
&& !isNew
&& infoPtr
->bDoChangeNotify
&&
3310 notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
3313 /* copy information */
3314 if (lpLVItem
->mask
& LVIF_TEXT
)
3315 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3317 if (lpLVItem
->mask
& LVIF_IMAGE
)
3318 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
3320 if (lpLVItem
->mask
& LVIF_PARAM
)
3321 lpItem
->lParam
= lpLVItem
->lParam
;
3323 if (lpLVItem
->mask
& LVIF_INDENT
)
3324 lpItem
->iIndent
= lpLVItem
->iIndent
;
3326 if (uChanged
& LVIF_STATE
)
3328 if (lpItem
&& (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& ~(LVIS_FOCUSED
| LVIS_SELECTED
)))
3330 lpItem
->state
&= ~lpLVItem
->stateMask
;
3331 lpItem
->state
|= (lpLVItem
->state
& lpLVItem
->stateMask
);
3333 if (lpLVItem
->state
& lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3335 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
3336 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3338 else if (lpLVItem
->stateMask
& LVIS_SELECTED
)
3339 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3341 /* if we are asked to change focus, and we manage it, do it */
3342 if (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
3344 if (lpLVItem
->state
& LVIS_FOCUSED
)
3346 LISTVIEW_SetItemFocus(infoPtr
, -1);
3347 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
3348 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, uView
== LVS_LIST
);
3350 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
3351 infoPtr
->nFocusedItem
= -1;
3355 /* if we're inserting the item, we're done */
3356 if (isNew
) return TRUE
;
3358 /* send LVN_ITEMCHANGED notification */
3359 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
3360 if (infoPtr
->bDoChangeNotify
) notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
3367 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3370 * [I] infoPtr : valid pointer to the listview structure
3371 * [I] lpLVItem : valid pointer to new subitem atttributes
3372 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3373 * [O] bChanged : will be set to TRUE if the item really changed
3379 static BOOL
set_sub_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
3382 SUBITEM_INFO
*lpSubItem
;
3384 /* we do not support subitems for virtual listviews */
3385 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
3387 /* set subitem only if column is present */
3388 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
3390 /* First do some sanity checks */
3391 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
)) return FALSE
;
3392 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
))) return TRUE
;
3394 /* get the subitem structure, and create it if not there */
3395 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3396 assert (hdpaSubItems
);
3398 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
3401 SUBITEM_INFO
*tmpSubItem
;
3404 lpSubItem
= (SUBITEM_INFO
*)Alloc(sizeof(SUBITEM_INFO
));
3405 if (!lpSubItem
) return FALSE
;
3406 /* we could binary search here, if need be...*/
3407 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
3409 tmpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
3410 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
3412 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
3417 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
3418 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
3422 if (lpLVItem
->mask
& LVIF_IMAGE
)
3423 if (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
)
3425 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
3429 if (lpLVItem
->mask
& LVIF_TEXT
)
3430 if (lpSubItem
->hdr
.pszText
!= lpLVItem
->pszText
)
3432 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3441 * Sets item attributes.
3444 * [I] infoPtr : valid pointer to the listview structure
3445 * [I] lpLVItem : new item atttributes
3446 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3452 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
3454 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3455 LPWSTR pszText
= NULL
;
3456 BOOL bResult
, bChanged
= FALSE
;
3458 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
3460 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
3463 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3464 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_textW(lpLVItem
->pszText
))
3466 pszText
= lpLVItem
->pszText
;
3467 ((LVITEMW
*)lpLVItem
)->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
3470 /* actually set the fields */
3471 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
3473 if (lpLVItem
->iSubItem
)
3474 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
3476 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
3478 /* redraw item, if necessary */
3479 if (bChanged
&& !infoPtr
->bIsDrawing
)
3481 /* this little optimization eliminates some nasty flicker */
3482 if ( uView
== LVS_REPORT
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
3483 (!(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) || lpLVItem
->iSubItem
) )
3484 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
3486 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
3491 textfreeT(lpLVItem
->pszText
, isW
);
3492 ((LVITEMW
*)lpLVItem
)->pszText
= pszText
;
3500 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3503 * [I] infoPtr : valid pointer to the listview structure
3508 static INT
LISTVIEW_GetTopIndex(LISTVIEW_INFO
*infoPtr
)
3510 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3512 SCROLLINFO scrollInfo
;
3514 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3515 scrollInfo
.fMask
= SIF_POS
;
3517 if (uView
== LVS_LIST
)
3519 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
3520 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
3522 else if (uView
== LVS_REPORT
)
3524 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3525 nItem
= scrollInfo
.nPos
;
3529 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3530 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
3533 TRACE("nItem=%d\n", nItem
);
3541 * Erases the background of the given rectangle
3544 * [I] infoPtr : valid pointer to the listview structure
3545 * [I] hdc : device context handle
3546 * [I] lprcBox : clipping rectangle
3552 static inline BOOL
LISTVIEW_FillBkgnd(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
3554 if (!infoPtr
->hBkBrush
) return FALSE
;
3556 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, debugrect(lprcBox
), infoPtr
->hBkBrush
);
3558 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
3566 * [I] infoPtr : valid pointer to the listview structure
3567 * [I] hdc : device context handle
3568 * [I] nItem : item index
3569 * [I] nSubItem : subitem index
3570 * [I] pos : item position in client coordinates
3571 * [I] cdmode : custom draw mode
3577 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, INT nSubItem
, POINT pos
, DWORD cdmode
)
3579 UINT uFormat
, uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3580 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
3581 static const WCHAR szCallback
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3582 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
3583 RECT
* lprcFocus
, rcSelect
, rcBox
, rcState
, rcIcon
, rcLabel
;
3584 NMLVCUSTOMDRAW nmlvcd
;
3588 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc
, nItem
, nSubItem
, debugpoint(&pos
));
3590 /* get information needed for drawing the item */
3591 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
;
3592 if (nSubItem
== 0) lvItem
.mask
|= LVIF_STATE
| LVIF_PARAM
;
3593 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
3594 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
;
3595 lvItem
.iItem
= nItem
;
3596 lvItem
.iSubItem
= nSubItem
;
3599 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3600 lvItem
.pszText
= szDispText
;
3601 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
3602 if (nSubItem
> 0 && (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3603 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3604 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= (LPWSTR
)szCallback
;
3605 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3607 /* now check if we need to update the focus rectangle */
3608 lprcFocus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
3610 if (!lprcFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
3611 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcState
, &rcIcon
, &rcLabel
);
3612 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
3613 OffsetRect(&rcState
, pos
.x
, pos
.y
);
3614 OffsetRect(&rcIcon
, pos
.x
, pos
.y
);
3615 OffsetRect(&rcLabel
, pos
.x
, pos
.y
);
3616 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3617 debugrect(&rcBox
), debugrect(&rcState
), debugrect(&rcIcon
), debugrect(&rcLabel
));
3619 /* fill in the custom draw structure */
3620 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
3622 if (nSubItem
> 0) cdmode
= infoPtr
->cditemmode
;
3623 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3624 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3625 if (nSubItem
== 0) infoPtr
->cditemmode
= cdsubitemmode
;
3626 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3627 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3628 if (nSubItem
== 0 && cdsubitemmode
== CDRF_NOTIFYITEMDRAW
)
3630 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
3631 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3633 if (nSubItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
3634 prepaint_setup(infoPtr
, hdc
, &nmlvcd
);
3636 /* in full row select, subitems, will just use main item's colors */
3637 if (nSubItem
&& uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3638 nmlvcd
.clrTextBk
= CLR_NONE
;
3641 if (infoPtr
->himlState
&& !IsRectEmpty(&rcState
))
3643 UINT uStateImage
= (lvItem
.state
& LVIS_STATEIMAGEMASK
) >> 12;
3646 TRACE("uStateImage=%d\n", uStateImage
);
3647 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
, rcState
.left
, rcState
.top
, ILD_NORMAL
);
3652 himl
= (uView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
3653 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
))
3655 TRACE("iImage=%d\n", lvItem
.iImage
);
3656 ImageList_Draw(himl
, lvItem
.iImage
, hdc
, rcIcon
.left
, rcIcon
.top
,
3657 (lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
) ? ILD_SELECTED
: ILD_NORMAL
);
3660 /* Don't bother painting item being edited */
3661 if (infoPtr
->hwndEdit
&& nItem
== infoPtr
->nEditLabelItem
&& nSubItem
== 0) goto postpaint
;
3663 /* draw the selection background, if we're drawing the main item */
3667 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3668 rcSelect
.right
= rcBox
.right
;
3670 if (nmlvcd
.clrTextBk
!= CLR_NONE
)
3671 ExtTextOutW(hdc
, rcSelect
.left
, rcSelect
.top
, ETO_OPAQUE
, &rcSelect
, 0, 0, 0);
3672 if(lprcFocus
) *lprcFocus
= rcSelect
;
3675 /* figure out the text drawing flags */
3676 uFormat
= (uView
== LVS_ICON
? (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
3677 if (uView
== LVS_ICON
)
3678 uFormat
= (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
3681 switch (LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
3683 case LVCFMT_RIGHT
: uFormat
|= DT_RIGHT
; break;
3684 case LVCFMT_CENTER
: uFormat
|= DT_CENTER
; break;
3685 default: uFormat
|= DT_LEFT
;
3688 if (!(uFormat
& (DT_RIGHT
| DT_CENTER
)))
3690 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
3691 else rcLabel
.left
+= LABEL_HOR_PADDING
;
3693 else if (uFormat
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
3694 DrawTextW(hdc
, lvItem
.pszText
, -1, &rcLabel
, uFormat
);
3697 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
3698 notify_postpaint(infoPtr
, &nmlvcd
);
3704 * Draws listview items when in owner draw mode.
3707 * [I] infoPtr : valid pointer to the listview structure
3708 * [I] hdc : device context handle
3713 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3715 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
3716 DWORD cditemmode
= CDRF_DODEFAULT
;
3717 NMLVCUSTOMDRAW nmlvcd
;
3718 POINT Origin
, Position
;
3724 ZeroMemory(&dis
, sizeof(dis
));
3726 /* Get scroll info once before loop */
3727 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3729 /* iterate through the invalidated rows */
3730 while(iterator_next(i
))
3732 item
.iItem
= i
->nItem
;
3734 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
3735 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
3736 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
3738 dis
.CtlType
= ODT_LISTVIEW
;
3740 dis
.itemID
= item
.iItem
;
3741 dis
.itemAction
= ODA_DRAWENTIRE
;
3743 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
3744 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
3745 dis
.hwndItem
= infoPtr
->hwndSelf
;
3747 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
3748 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
3749 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
3750 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
3751 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
3752 dis
.itemData
= item
.lParam
;
3754 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), debugrect(&dis
.rcItem
));
3757 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3758 * structure for the rest. of the paint cycle
3760 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
3761 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3762 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3764 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
3766 prepaint_setup (infoPtr
, hdc
, &nmlvcd
);
3767 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
3770 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3771 notify_postpaint(infoPtr
, &nmlvcd
);
3777 * Draws listview items when in report display mode.
3780 * [I] infoPtr : valid pointer to the listview structure
3781 * [I] hdc : device context handle
3782 * [I] cdmode : custom draw mode
3787 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3790 RECT rcClip
, rcItem
;
3791 POINT Origin
, Position
;
3797 /* figure out what to draw */
3798 rgntype
= GetClipBox(hdc
, &rcClip
);
3799 if (rgntype
== NULLREGION
) return;
3801 /* Get scroll info once before loop */
3802 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3804 /* narrow down the columns we need to paint */
3805 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
3807 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
3808 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
3810 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
3812 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
3813 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
3815 iterator_rangeitems(&j
, colRange
);
3817 /* in full row select, we _have_ to draw the main item */
3818 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
3821 /* iterate through the invalidated rows */
3822 while(iterator_next(i
))
3824 /* iterate through the invalidated columns */
3825 while(iterator_next(&j
))
3827 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
3828 Position
.x
+= Origin
.x
;
3829 Position
.y
+= Origin
.y
;
3831 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
3833 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
3835 rcItem
.bottom
= infoPtr
->nItemHeight
;
3836 OffsetRect(&rcItem
, Position
.x
, Position
.y
);
3837 if (!RectVisible(hdc
, &rcItem
)) continue;
3840 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, j
.nItem
, Position
, cdmode
);
3843 iterator_destroy(&j
);
3848 * Draws listview items when in list display mode.
3851 * [I] infoPtr : valid pointer to the listview structure
3852 * [I] hdc : device context handle
3853 * [I] cdmode : custom draw mode
3858 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3860 POINT Origin
, Position
;
3862 /* Get scroll info once before loop */
3863 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3865 while(iterator_prev(i
))
3867 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
3868 Position
.x
+= Origin
.x
;
3869 Position
.y
+= Origin
.y
;
3871 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, 0, Position
, cdmode
);
3878 * Draws listview items.
3881 * [I] infoPtr : valid pointer to the listview structure
3882 * [I] hdc : device context handle
3887 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
3889 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3890 COLORREF oldTextColor
, oldClrTextBk
, oldClrText
;
3891 NMLVCUSTOMDRAW nmlvcd
;
3898 LISTVIEW_DUMP(infoPtr
);
3900 infoPtr
->bIsDrawing
= TRUE
;
3902 /* save dc values we're gonna trash while drawing */
3903 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
3904 oldBkMode
= GetBkMode(hdc
);
3905 infoPtr
->clrTextBkDefault
= GetBkColor(hdc
);
3906 oldTextColor
= GetTextColor(hdc
);
3908 oldClrTextBk
= infoPtr
->clrTextBk
;
3909 oldClrText
= infoPtr
->clrText
;
3911 infoPtr
->cditemmode
= CDRF_DODEFAULT
;
3913 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
3914 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
3915 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3916 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
3917 prepaint_setup(infoPtr
, hdc
, &nmlvcd
);
3919 /* Use these colors to draw the items */
3920 infoPtr
->clrTextBk
= nmlvcd
.clrTextBk
;
3921 infoPtr
->clrText
= nmlvcd
.clrText
;
3923 /* nothing to draw */
3924 if(infoPtr
->nItemCount
== 0) goto enddraw
;
3926 /* figure out what we need to draw */
3927 iterator_visibleitems(&i
, infoPtr
, hdc
);
3929 /* send cache hint notification */
3930 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
3932 RANGE range
= iterator_range(&i
);
3935 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
3936 nmlv
.iFrom
= range
.lower
;
3937 nmlv
.iTo
= range
.upper
- 1;
3938 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
3941 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
3942 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
3945 if (uView
== LVS_REPORT
)
3946 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
3947 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3948 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
3950 /* if we have a focus rect, draw it */
3951 if (infoPtr
->bFocus
)
3952 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
3954 iterator_destroy(&i
);
3957 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
3958 notify_postpaint(infoPtr
, &nmlvcd
);
3960 infoPtr
->clrTextBk
= oldClrTextBk
;
3961 infoPtr
->clrText
= oldClrText
;
3963 SelectObject(hdc
, hOldFont
);
3964 SetBkMode(hdc
, oldBkMode
);
3965 SetBkColor(hdc
, infoPtr
->clrTextBkDefault
);
3966 SetTextColor(hdc
, oldTextColor
);
3967 infoPtr
->bIsDrawing
= FALSE
;
3973 * Calculates the approximate width and height of a given number of items.
3976 * [I] infoPtr : valid pointer to the listview structure
3977 * [I] nItemCount : number of items
3978 * [I] wWidth : width
3979 * [I] wHeight : height
3982 * Returns a DWORD. The width in the low word and the height in high word.
3984 static DWORD
LISTVIEW_ApproximateViewRect(LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
3985 WORD wWidth
, WORD wHeight
)
3987 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3988 INT nItemCountPerColumn
= 1;
3989 INT nColumnCount
= 0;
3990 DWORD dwViewRect
= 0;
3992 if (nItemCount
== -1)
3993 nItemCount
= infoPtr
->nItemCount
;
3995 if (uView
== LVS_LIST
)
3997 if (wHeight
== 0xFFFF)
3999 /* use current height */
4000 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4003 if (wHeight
< infoPtr
->nItemHeight
)
4004 wHeight
= infoPtr
->nItemHeight
;
4008 if (infoPtr
->nItemHeight
> 0)
4010 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
4011 if (nItemCountPerColumn
== 0)
4012 nItemCountPerColumn
= 1;
4014 if (nItemCount
% nItemCountPerColumn
!= 0)
4015 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
4017 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
4021 /* Microsoft padding magic */
4022 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
4023 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
4025 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4027 else if (uView
== LVS_REPORT
)
4031 if (infoPtr
->nItemCount
> 0)
4033 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
4034 wWidth
= rcBox
.right
- rcBox
.left
;
4035 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
4039 /* use current height and width */
4040 if (wHeight
== 0xffff)
4041 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4042 if (wWidth
== 0xffff)
4043 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
4046 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4048 else if (uView
== LVS_SMALLICON
)
4049 FIXME("uView == LVS_SMALLICON: not implemented\n");
4050 else if (uView
== LVS_ICON
)
4051 FIXME("uView == LVS_ICON: not implemented\n");
4059 * Create a drag image list for the specified item.
4062 * [I] infoPtr : valid pointer to the listview structure
4063 * [I] iItem : index of item
4064 * [O] lppt : Upperr-left corner of the image
4067 * Returns a handle to the image list if successful, NULL otherwise.
4069 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
4075 HBITMAP hbmp
, hOldbmp
;
4076 HIMAGELIST dragList
= 0;
4077 TRACE("iItem=%d Count=%d \n", iItem
, infoPtr
->nItemCount
);
4079 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
)
4082 rcItem
.left
= LVIR_BOUNDS
;
4083 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
4086 lppt
->x
= rcItem
.left
;
4087 lppt
->y
= rcItem
.top
;
4089 size
.cx
= rcItem
.right
- rcItem
.left
;
4090 size
.cy
= rcItem
.bottom
- rcItem
.top
;
4092 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
4093 hdc
= CreateCompatibleDC(hdcOrig
);
4094 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
4095 hOldbmp
= SelectObject(hdc
, hbmp
);
4097 rcItem
.left
= rcItem
.top
= 0;
4098 rcItem
.right
= size
.cx
;
4099 rcItem
.bottom
= size
.cy
;
4100 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
4103 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, 0, pos
, infoPtr
->cditemmode
))
4105 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
4106 SelectObject(hdc
, hOldbmp
);
4107 ImageList_Add(dragList
, hbmp
, 0);
4110 SelectObject(hdc
, hOldbmp
);
4114 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
4116 TRACE("ret=%p\n", dragList
);
4124 * Removes all listview items and subitems.
4127 * [I] infoPtr : valid pointer to the listview structure
4133 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
)
4136 HDPA hdpaSubItems
= NULL
;
4143 /* we do it directly, to avoid notifications */
4144 ranges_clear(infoPtr
->selectionRanges
);
4145 infoPtr
->nSelectionMark
= -1;
4146 infoPtr
->nFocusedItem
= -1;
4147 SetRectEmpty(&infoPtr
->rcFocus
);
4148 /* But we are supposed to leave nHotItem as is! */
4151 /* send LVN_DELETEALLITEMS notification */
4152 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4154 bSuppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
4156 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
4158 /* send LVN_DELETEITEM notification, if not suppressed */
4159 if (!bSuppress
) notify_deleteitem(infoPtr
, i
);
4160 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4162 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
4163 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
4165 hdrItem
= (ITEMHDR
*)DPA_GetPtr(hdpaSubItems
, j
);
4166 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4169 DPA_Destroy(hdpaSubItems
);
4170 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
4172 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
4173 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
4174 infoPtr
->nItemCount
--;
4177 LISTVIEW_UpdateScroll(infoPtr
);
4179 LISTVIEW_InvalidateList(infoPtr
);
4186 * Scrolls, and updates the columns, when a column is changing width.
4189 * [I] infoPtr : valid pointer to the listview structure
4190 * [I] nColumn : column to scroll
4191 * [I] dx : amount of scroll, in pixels
4196 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
4198 COLUMN_INFO
*lpColumnInfo
;
4202 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
4203 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
4204 rcCol
= lpColumnInfo
->rcHeader
;
4205 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
4206 rcCol
.left
= rcCol
.right
;
4208 /* ajust the other columns */
4209 for (nCol
= nColumn
; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
4211 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
4212 lpColumnInfo
->rcHeader
.left
+= dx
;
4213 lpColumnInfo
->rcHeader
.right
+= dx
;
4216 /* do not update screen if not in report mode */
4217 if (!is_redrawing(infoPtr
) || (infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return;
4219 /* if we have a focus, must first erase the focus rect */
4220 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
4222 /* Need to reset the item width when inserting a new column */
4223 infoPtr
->nItemWidth
+= dx
;
4225 LISTVIEW_UpdateScroll(infoPtr
);
4227 /* scroll to cover the deleted column, and invalidate for redraw */
4228 rcOld
= infoPtr
->rcList
;
4229 rcOld
.left
= rcCol
.left
;
4230 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4232 /* we can restore focus now */
4233 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
4238 * Removes a column from the listview control.
4241 * [I] infoPtr : valid pointer to the listview structure
4242 * [I] nColumn : column index
4248 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4252 TRACE("nColumn=%d\n", nColumn
);
4254 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0
4255 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4257 /* While the MSDN specifically says that column zero should not be deleted,
4258 what actually happens is that the column itself is deleted but no items or subitems
4262 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
4264 if (!Header_DeleteItem(infoPtr
->hwndHeader
, nColumn
))
4267 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
4268 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
4270 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
4272 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
4274 INT nItem
, nSubItem
, i
;
4276 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
4278 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
4281 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4283 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
4284 if (lpSubItem
->iSubItem
== nColumn
)
4287 lpDelItem
= lpSubItem
;
4289 else if (lpSubItem
->iSubItem
> nColumn
)
4291 lpSubItem
->iSubItem
--;
4295 /* if we found our subitem, zapp it */
4299 if (is_textW(lpDelItem
->hdr
.pszText
))
4300 Free(lpDelItem
->hdr
.pszText
);
4305 /* free dpa memory */
4306 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
4311 /* update the other column info */
4312 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
4313 LISTVIEW_InvalidateList(infoPtr
);
4315 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
4322 * Invalidates the listview after an item's insertion or deletion.
4325 * [I] infoPtr : valid pointer to the listview structure
4326 * [I] nItem : item index
4327 * [I] dir : -1 if deleting, 1 if inserting
4332 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
4334 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4335 INT nPerCol
, nItemCol
, nItemRow
;
4339 /* if we don't refresh, what's the point of scrolling? */
4340 if (!is_redrawing(infoPtr
)) return;
4342 assert (abs(dir
) == 1);
4344 /* arrange icons if autoarrange is on */
4345 if (is_autoarrange(infoPtr
))
4347 BOOL arrange
= TRUE
;
4348 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
4349 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
4350 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4353 /* scrollbars need updating */
4354 LISTVIEW_UpdateScroll(infoPtr
);
4356 /* figure out the item's position */
4357 if (uView
== LVS_REPORT
)
4358 nPerCol
= infoPtr
->nItemCount
+ 1;
4359 else if (uView
== LVS_LIST
)
4360 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
4361 else /* LVS_ICON, or LVS_SMALLICON */
4364 nItemCol
= nItem
/ nPerCol
;
4365 nItemRow
= nItem
% nPerCol
;
4366 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4368 /* move the items below up a slot */
4369 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
4370 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
4371 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
4372 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4373 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4374 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
4375 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4377 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll
), debugrect(&infoPtr
->rcList
));
4378 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4379 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4382 /* report has only that column, so we're done */
4383 if (uView
== LVS_REPORT
) return;
4385 /* now for LISTs, we have to deal with the columns to the right */
4386 rcScroll
.left
= (nItemCol
+ 1) * infoPtr
->nItemWidth
;
4388 rcScroll
.right
= (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
4389 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4390 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4391 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4392 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4393 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4398 * Removes an item from the listview control.
4401 * [I] infoPtr : valid pointer to the listview structure
4402 * [I] nItem : item index
4408 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
4410 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4413 TRACE("(nItem=%d)\n", nItem
);
4415 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
4417 /* remove selection, and focus */
4419 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4420 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
4422 /* send LVN_DELETEITEM notification. */
4423 notify_deleteitem(infoPtr
, nItem
);
4425 /* we need to do this here, because we'll be deleting stuff */
4426 if (uView
== LVS_SMALLICON
|| uView
== LVS_ICON
)
4427 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4429 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4435 hdpaSubItems
= (HDPA
)DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
4436 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4438 hdrItem
= (ITEMHDR
*)DPA_GetPtr(hdpaSubItems
, i
);
4439 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4442 DPA_Destroy(hdpaSubItems
);
4445 if (uView
== LVS_SMALLICON
|| uView
== LVS_ICON
)
4447 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
4448 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
4451 infoPtr
->nItemCount
--;
4452 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
4454 /* now is the invalidation fun */
4455 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
4462 * Callback implementation for editlabel control
4465 * [I] infoPtr : valid pointer to the listview structure
4466 * [I] pszText : modified text
4467 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4473 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, LPWSTR pszText
, BOOL isW
)
4475 NMLVDISPINFOW dispInfo
;
4477 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
4479 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4480 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4481 dispInfo
.item
.iItem
= infoPtr
->nEditLabelItem
;
4482 dispInfo
.item
.iSubItem
= 0;
4483 dispInfo
.item
.stateMask
= ~0;
4484 if (!LISTVIEW_GetItemW(infoPtr
, &dispInfo
.item
)) return FALSE
;
4485 /* add the text from the edit in */
4486 dispInfo
.item
.mask
|= LVIF_TEXT
;
4487 dispInfo
.item
.pszText
= pszText
;
4488 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4490 /* Do we need to update the Item Text */
4491 if (!notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
)) return FALSE
;
4492 if (!pszText
) return TRUE
;
4494 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4495 dispInfo
.item
.mask
= LVIF_TEXT
;
4496 dispInfo
.item
.iItem
= infoPtr
->nEditLabelItem
;
4497 dispInfo
.item
.iSubItem
= 0;
4498 dispInfo
.item
.pszText
= pszText
;
4499 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4500 return LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
4505 * Begin in place editing of specified list view item
4508 * [I] infoPtr : valid pointer to the listview structure
4509 * [I] nItem : item index
4510 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4516 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
4518 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
4519 NMLVDISPINFOW dispInfo
;
4522 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
4524 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
4525 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
4527 infoPtr
->nEditLabelItem
= nItem
;
4529 /* Is the EditBox still there, if so remove it */
4530 if(infoPtr
->hwndEdit
!= 0)
4532 SetFocus(infoPtr
->hwndSelf
);
4533 infoPtr
->hwndEdit
= 0;
4536 LISTVIEW_SetSelection(infoPtr
, nItem
);
4537 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
4538 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4540 rect
.left
= LVIR_LABEL
;
4541 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
4543 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4544 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4545 dispInfo
.item
.iItem
= nItem
;
4546 dispInfo
.item
.iSubItem
= 0;
4547 dispInfo
.item
.stateMask
= ~0;
4548 dispInfo
.item
.pszText
= szDispText
;
4549 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
4550 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
4552 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, WS_VISIBLE
,
4553 rect
.left
-2, rect
.top
-1, 0, rect
.bottom
- rect
.top
+2, isW
);
4554 if (!infoPtr
->hwndEdit
) return 0;
4556 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
4558 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
4559 infoPtr
->hwndEdit
= 0;
4563 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
4564 SetFocus(infoPtr
->hwndEdit
);
4565 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
4566 return infoPtr
->hwndEdit
;
4572 * Ensures the specified item is visible, scrolling into view if necessary.
4575 * [I] infoPtr : valid pointer to the listview structure
4576 * [I] nItem : item index
4577 * [I] bPartial : partially or entirely visible
4583 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
4585 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4586 INT nScrollPosHeight
= 0;
4587 INT nScrollPosWidth
= 0;
4588 INT nHorzAdjust
= 0;
4589 INT nVertAdjust
= 0;
4592 RECT rcItem
, rcTemp
;
4594 rcItem
.left
= LVIR_BOUNDS
;
4595 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
4597 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
4599 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
4601 /* scroll left/right, but in LVS_REPORT mode */
4602 if (uView
== LVS_LIST
)
4603 nScrollPosWidth
= infoPtr
->nItemWidth
;
4604 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
4605 nScrollPosWidth
= 1;
4607 if (rcItem
.left
< infoPtr
->rcList
.left
)
4610 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
4615 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
4619 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
4621 /* scroll up/down, but not in LVS_LIST mode */
4622 if (uView
== LVS_REPORT
)
4623 nScrollPosHeight
= infoPtr
->nItemHeight
;
4624 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
4625 nScrollPosHeight
= 1;
4627 if (rcItem
.top
< infoPtr
->rcList
.top
)
4630 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
4635 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
4639 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
4641 if (nScrollPosWidth
)
4643 INT diff
= nHorzDiff
/ nScrollPosWidth
;
4644 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
4645 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
4648 if (nScrollPosHeight
)
4650 INT diff
= nVertDiff
/ nScrollPosHeight
;
4651 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
4652 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
4660 * Searches for an item with specific characteristics.
4663 * [I] hwnd : window handle
4664 * [I] nStart : base item index
4665 * [I] lpFindInfo : item information to look for
4668 * SUCCESS : index of item
4671 static INT
LISTVIEW_FindItemW(LISTVIEW_INFO
*infoPtr
, INT nStart
,
4672 const LVFINDINFOW
*lpFindInfo
)
4674 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4675 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
4676 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
4677 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
4678 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
4679 POINT Position
, Destination
;
4682 if (!lpFindInfo
|| nItem
< 0) return -1;
4685 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
))
4687 lvItem
.mask
|= LVIF_TEXT
;
4688 lvItem
.pszText
= szDispText
;
4689 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4692 if (lpFindInfo
->flags
& LVFI_WRAP
)
4695 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
4696 (uView
== LVS_ICON
|| uView
==LVS_SMALLICON
))
4701 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4702 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
4703 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
4704 switch(lpFindInfo
->vkDirection
)
4706 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
4707 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
4708 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
4709 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
4710 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
4711 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
4712 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
4714 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
4715 Destination
.x
= rcArea
.right
;
4716 Destination
.y
= rcArea
.bottom
;
4718 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
4723 /* if LVFI_PARAM is specified, all other flags are ignored */
4724 if (lpFindInfo
->flags
& LVFI_PARAM
)
4726 lvItem
.mask
|= LVIF_PARAM
;
4728 lvItem
.mask
&= ~LVIF_TEXT
;
4732 for (; nItem
< nLast
; nItem
++)
4734 lvItem
.iItem
= nItem
;
4735 lvItem
.iSubItem
= 0;
4736 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
4738 if (lvItem
.mask
& LVIF_PARAM
)
4740 if (lpFindInfo
->lParam
== lvItem
.lParam
)
4746 if (lvItem
.mask
& LVIF_TEXT
)
4748 if (lpFindInfo
->flags
& LVFI_PARTIAL
)
4750 if (strstrW(lvItem
.pszText
, lpFindInfo
->psz
) == NULL
) continue;
4754 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
4758 if (!bNearest
) return nItem
;
4760 /* This is very inefficient. To do a good job here,
4761 * we need a sorted array of (x,y) item positions */
4762 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
4764 /* compute the distance^2 to the destination */
4765 xdist
= Destination
.x
- Position
.x
;
4766 ydist
= Destination
.y
- Position
.y
;
4767 dist
= xdist
* xdist
+ ydist
* ydist
;
4769 /* remember the distance, and item if it's closer */
4773 nNearestItem
= nItem
;
4780 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
4785 return nNearestItem
;
4790 * Searches for an item with specific characteristics.
4793 * [I] hwnd : window handle
4794 * [I] nStart : base item index
4795 * [I] lpFindInfo : item information to look for
4798 * SUCCESS : index of item
4801 static INT
LISTVIEW_FindItemA(LISTVIEW_INFO
*infoPtr
, INT nStart
,
4802 const LVFINDINFOA
*lpFindInfo
)
4804 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
);
4808 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
4809 if (hasText
) fiw
.psz
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
4810 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
4811 if (hasText
) textfreeT((LPWSTR
)fiw
.psz
, FALSE
);
4817 * Retrieves the background image of the listview control.
4820 * [I] infoPtr : valid pointer to the listview structure
4821 * [O] lpBkImage : background image attributes
4827 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4829 /* FIXME (listview, "empty stub!\n"); */
4835 * Retrieves column attributes.
4838 * [I] infoPtr : valid pointer to the listview structure
4839 * [I] nColumn : column index
4840 * [IO] lpColumn : column information
4841 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4842 * otherwise it is in fact a LPLVCOLUMNA
4848 static BOOL
LISTVIEW_GetColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
4850 COLUMN_INFO
*lpColumnInfo
;
4853 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4854 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
4856 /* initialize memory */
4857 ZeroMemory(&hdi
, sizeof(hdi
));
4859 if (lpColumn
->mask
& LVCF_TEXT
)
4861 hdi
.mask
|= HDI_TEXT
;
4862 hdi
.pszText
= lpColumn
->pszText
;
4863 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
4866 if (lpColumn
->mask
& LVCF_IMAGE
)
4867 hdi
.mask
|= HDI_IMAGE
;
4869 if (lpColumn
->mask
& LVCF_ORDER
)
4870 hdi
.mask
|= HDI_ORDER
;
4872 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
4874 if (lpColumn
->mask
& LVCF_FMT
)
4875 lpColumn
->fmt
= lpColumnInfo
->fmt
;
4877 if (lpColumn
->mask
& LVCF_WIDTH
)
4878 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
4880 if (lpColumn
->mask
& LVCF_IMAGE
)
4881 lpColumn
->iImage
= hdi
.iImage
;
4883 if (lpColumn
->mask
& LVCF_ORDER
)
4884 lpColumn
->iOrder
= hdi
.iOrder
;
4890 static BOOL
LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
4897 /* FIXME: little hack */
4898 for (i
= 0; i
< iCount
; i
++)
4906 * Retrieves the column width.
4909 * [I] infoPtr : valid pointer to the listview structure
4910 * [I] int : column index
4913 * SUCCESS : column width
4916 static INT
LISTVIEW_GetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4918 INT nColumnWidth
= 0;
4921 TRACE("nColumn=%d\n", nColumn
);
4923 /* we have a 'column' in LIST and REPORT mode only */
4924 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
)
4927 nColumnWidth
= infoPtr
->nItemWidth
;
4930 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
4931 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
4932 nColumnWidth
= rcHeader
.right
- rcHeader
.left
;
4936 TRACE("nColumnWidth=%d\n", nColumnWidth
);
4937 return nColumnWidth
;
4942 * In list or report display mode, retrieves the number of items that can fit
4943 * vertically in the visible area. In icon or small icon display mode,
4944 * retrieves the total number of visible items.
4947 * [I] infoPtr : valid pointer to the listview structure
4950 * Number of fully visible items.
4952 static INT
LISTVIEW_GetCountPerPage(LISTVIEW_INFO
*infoPtr
)
4954 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
4958 return infoPtr
->nItemCount
;
4960 return LISTVIEW_GetCountPerColumn(infoPtr
);
4962 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
4970 * Retrieves an image list handle.
4973 * [I] infoPtr : valid pointer to the listview structure
4974 * [I] nImageList : image list identifier
4977 * SUCCESS : image list handle
4980 static HIMAGELIST
LISTVIEW_GetImageList(LISTVIEW_INFO
*infoPtr
, INT nImageList
)
4984 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
4985 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
4986 case LVSIL_STATE
: return infoPtr
->himlState
;
4991 /* LISTVIEW_GetISearchString */
4995 * Retrieves item attributes.
4998 * [I] hwnd : window handle
4999 * [IO] lpLVItem : item info
5000 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5001 * if FALSE, the lpLVItem is a LPLVITEMA.
5004 * This is the internal 'GetItem' interface -- it tries to
5005 * be smart, and avoids text copies, if possible, by modifing
5006 * lpLVItem->pszText to point to the text string. Please note
5007 * that this is not always possible (e.g. OWNERDATA), so on
5008 * entry you *must* supply valid values for pszText, and cchTextMax.
5009 * The only difference to the documented interface is that upon
5010 * return, you should use *only* the lpLVItem->pszText, rather than
5011 * the buffer pointer you provided on input. Most code already does
5012 * that, so it's not a problem.
5013 * For the two cases when the text must be copied (that is,
5014 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5020 static BOOL
LISTVIEW_GetItemT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5022 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
5023 NMLVDISPINFOW dispInfo
;
5029 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
5031 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5034 if (lpLVItem
->mask
== 0) return TRUE
;
5036 /* make a local copy */
5037 isubitem
= lpLVItem
->iSubItem
;
5039 /* a quick optimization if all we're asked is the focus state
5040 * these queries are worth optimising since they are common,
5041 * and can be answered in constant time, without the heavy accesses */
5042 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
5043 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
5045 lpLVItem
->state
= 0;
5046 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5047 lpLVItem
->state
|= LVIS_FOCUSED
;
5051 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5053 /* if the app stores all the data, handle it separately */
5054 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5056 dispInfo
.item
.state
= 0;
5058 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5059 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) || infoPtr
->uCallbackMask
)
5061 /* NOTE: copy only fields which we _know_ are initialized, some apps
5062 * depend on the uninitialized fields being 0 */
5063 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
5064 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5065 dispInfo
.item
.iSubItem
= isubitem
;
5066 if (lpLVItem
->mask
& LVIF_TEXT
)
5068 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5069 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5071 if (lpLVItem
->mask
& LVIF_STATE
)
5072 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
5073 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5074 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
5075 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
5077 /* full size structure expected - _WIN32IE >= 0x560 */
5078 *lpLVItem
= dispInfo
.item
;
5080 else if (lpLVItem
->mask
& LVIF_INDENT
)
5082 /* indent member expected - _WIN32IE >= 0x300 */
5083 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
5087 /* minimal structure expected */
5088 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
5090 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
5093 /* make sure lParam is zeroed out */
5094 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
5096 /* we store only a little state, so if we're not asked, we're done */
5097 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
5099 /* if focus is handled by us, report it */
5100 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5102 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5103 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5104 lpLVItem
->state
|= LVIS_FOCUSED
;
5107 /* and do the same for selection, if we handle it */
5108 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5110 lpLVItem
->state
&= ~LVIS_SELECTED
;
5111 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5112 lpLVItem
->state
|= LVIS_SELECTED
;
5118 /* find the item and subitem structures before we proceed */
5119 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
5120 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
5125 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
5126 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
5129 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
5134 pItemHdr
= &lpItem
->hdr
;
5136 /* Do we need to query the state from the app? */
5137 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
5139 dispInfo
.item
.mask
|= LVIF_STATE
;
5140 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
5143 /* Do we need to enquire about the image? */
5144 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
5145 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
5147 dispInfo
.item
.mask
|= LVIF_IMAGE
;
5148 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
5151 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5152 if ((lpLVItem
->mask
& LVIF_TEXT
) && !is_textW(pItemHdr
->pszText
))
5154 dispInfo
.item
.mask
|= LVIF_TEXT
;
5155 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5156 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5157 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
5158 *dispInfo
.item
.pszText
= '\0';
5161 /* If we don't have all the requested info, query the application */
5162 if (dispInfo
.item
.mask
!= 0)
5164 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5165 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
5166 dispInfo
.item
.lParam
= lpItem
->lParam
;
5167 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5168 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
5171 /* we should not store values for subitems */
5172 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
5174 /* Now, handle the iImage field */
5175 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
5177 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
5178 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
5179 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
5181 else if (lpLVItem
->mask
& LVIF_IMAGE
)
5183 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
5184 lpLVItem
->iImage
= pItemHdr
->iImage
;
5186 lpLVItem
->iImage
= 0;
5189 /* The pszText field */
5190 if (dispInfo
.item
.mask
& LVIF_TEXT
)
5192 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
5193 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
5195 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
5197 else if (lpLVItem
->mask
& LVIF_TEXT
)
5199 if (isW
) lpLVItem
->pszText
= pItemHdr
->pszText
;
5200 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
5203 /* if this is a subitem, we're done */
5204 if (isubitem
) return TRUE
;
5206 /* Next is the lParam field */
5207 if (dispInfo
.item
.mask
& LVIF_PARAM
)
5209 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
5210 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
5211 lpItem
->lParam
= dispInfo
.item
.lParam
;
5213 else if (lpLVItem
->mask
& LVIF_PARAM
)
5214 lpLVItem
->lParam
= lpItem
->lParam
;
5216 /* ... the state field (this one is different due to uCallbackmask) */
5217 if (lpLVItem
->mask
& LVIF_STATE
)
5219 lpLVItem
->state
= lpItem
->state
;
5220 if (dispInfo
.item
.mask
& LVIF_STATE
)
5222 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
5223 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
5225 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5227 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5228 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5229 lpLVItem
->state
|= LVIS_FOCUSED
;
5231 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5233 lpLVItem
->state
&= ~LVIS_SELECTED
;
5234 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5235 lpLVItem
->state
|= LVIS_SELECTED
;
5239 /* and last, but not least, the indent field */
5240 if (lpLVItem
->mask
& LVIF_INDENT
)
5241 lpLVItem
->iIndent
= lpItem
->iIndent
;
5248 * Retrieves item attributes.
5251 * [I] hwnd : window handle
5252 * [IO] lpLVItem : item info
5253 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5254 * if FALSE, the lpLVItem is a LPLVITEMA.
5257 * This is the external 'GetItem' interface -- it properly copies
5258 * the text in the provided buffer.
5264 static BOOL
LISTVIEW_GetItemExtT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5269 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5272 pszText
= lpLVItem
->pszText
;
5273 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
5274 if (bResult
&& lpLVItem
->pszText
!= pszText
)
5275 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
5276 lpLVItem
->pszText
= pszText
;
5284 * Retrieves the position (upper-left) of the listview control item.
5285 * Note that for LVS_ICON style, the upper-left is that of the icon
5286 * and not the bounding box.
5289 * [I] infoPtr : valid pointer to the listview structure
5290 * [I] nItem : item index
5291 * [O] lpptPosition : coordinate information
5297 static BOOL
LISTVIEW_GetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
5299 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5302 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
5304 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5306 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5307 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
5309 if (uView
== LVS_ICON
)
5311 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
5312 lpptPosition
->y
+= ICON_TOP_PADDING
;
5314 lpptPosition
->x
+= Origin
.x
;
5315 lpptPosition
->y
+= Origin
.y
;
5317 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition
));
5324 * Retrieves the bounding rectangle for a listview control item.
5327 * [I] infoPtr : valid pointer to the listview structure
5328 * [I] nItem : item index
5329 * [IO] lprc : bounding rectangle coordinates
5330 * lprc->left specifies the portion of the item for which the bounding
5331 * rectangle will be retrieved.
5333 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5334 * including the icon and label.
5337 * * Experiment shows that native control returns:
5338 * * width = min (48, length of text line)
5339 * * .left = position.x - (width - iconsize.cx)/2
5340 * * .right = .left + width
5341 * * height = #lines of text * ntmHeight + icon height + 8
5342 * * .top = position.y - 2
5343 * * .bottom = .top + height
5344 * * separation between items .y = itemSpacing.cy - height
5345 * * .x = itemSpacing.cx - width
5346 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5349 * * Experiment shows that native control returns:
5350 * * width = iconSize.cx + 16
5351 * * .left = position.x - (width - iconsize.cx)/2
5352 * * .right = .left + width
5353 * * height = iconSize.cy + 4
5354 * * .top = position.y - 2
5355 * * .bottom = .top + height
5356 * * separation between items .y = itemSpacing.cy - height
5357 * * .x = itemSpacing.cx - width
5358 * LVIR_LABEL Returns the bounding rectangle of the item text.
5361 * * Experiment shows that native control returns:
5362 * * width = text length
5363 * * .left = position.x - width/2
5364 * * .right = .left + width
5365 * * height = ntmH * linecount + 2
5366 * * .top = position.y + iconSize.cy + 6
5367 * * .bottom = .top + height
5368 * * separation between items .y = itemSpacing.cy - height
5369 * * .x = itemSpacing.cx - width
5370 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5371 * rectangles, but excludes columns in report view.
5378 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5379 * upon whether the window has the focus currently and on whether the item
5380 * is the one with the focus. Ensure that the control's record of which
5381 * item has the focus agrees with the items' records.
5383 static BOOL
LISTVIEW_GetItemRect(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5385 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5386 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5387 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
5388 POINT Position
, Origin
;
5392 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
5394 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5396 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5397 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5399 /* Be smart and try to figure out the minimum we have to do */
5400 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
5401 if (uView
== LVS_REPORT
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
5402 if (uView
== LVS_ICON
&& lprc
->left
!= LVIR_ICON
&&
5403 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
5404 oversizedBox
= TRUE
;
5406 /* get what we need from the item before hand, so we make
5407 * only one request. This can speed up things, if data
5408 * is stored on the app side */
5410 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
5411 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
5412 lvItem
.iItem
= nItem
;
5413 lvItem
.iSubItem
= 0;
5414 lvItem
.pszText
= szDispText
;
5415 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5416 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5417 /* we got the state already up, simulate it here, to avoid a reget */
5418 if (uView
== LVS_ICON
&& (lprc
->left
!= LVIR_ICON
))
5420 lvItem
.mask
|= LVIF_STATE
;
5421 lvItem
.stateMask
= LVIS_FOCUSED
;
5422 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
5425 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
5426 lprc
->left
= LVIR_BOUNDS
;
5430 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
);
5434 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, lprc
);
5438 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
);
5441 case LVIR_SELECTBOUNDS
:
5442 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, &label_rect
);
5443 UnionRect(lprc
, lprc
, &label_rect
);
5447 WARN("Unknown value: %ld\n", lprc
->left
);
5451 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
5453 TRACE(" rect=%s\n", debugrect(lprc
));
5460 * Retrieves the spacing between listview control items.
5463 * [I] infoPtr : valid pointer to the listview structure
5464 * [IO] lprc : rectangle to receive the output
5465 * on input, lprc->top = nSubItem
5466 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5468 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5469 * not only those of the first column.
5470 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5476 static BOOL
LISTVIEW_GetSubItemRect(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5481 if (!lprc
) return FALSE
;
5483 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem
, lprc
->top
);
5484 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5486 return LISTVIEW_GetItemRect(infoPtr
, nItem
, lprc
);
5488 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return FALSE
;
5490 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &Position
)) return FALSE
;
5493 lvItem
.iItem
= nItem
;
5494 lvItem
.iSubItem
= lprc
->top
;
5496 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5500 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
);
5505 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
);
5509 ERR("Unknown bounds=%ld\n", lprc
->left
);
5513 OffsetRect(lprc
, Position
.x
, Position
.y
);
5520 * Retrieves the width of a label.
5523 * [I] infoPtr : valid pointer to the listview structure
5526 * SUCCESS : string width (in pixels)
5529 static INT
LISTVIEW_GetLabelWidth(LISTVIEW_INFO
*infoPtr
, INT nItem
)
5531 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5534 TRACE("(nItem=%d)\n", nItem
);
5536 lvItem
.mask
= LVIF_TEXT
;
5537 lvItem
.iItem
= nItem
;
5538 lvItem
.iSubItem
= 0;
5539 lvItem
.pszText
= szDispText
;
5540 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5541 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5543 return LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
5548 * Retrieves the spacing between listview control items.
5551 * [I] infoPtr : valid pointer to the listview structure
5552 * [I] bSmall : flag for small or large icon
5555 * Horizontal + vertical spacing
5557 static LONG
LISTVIEW_GetItemSpacing(LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
5563 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
5567 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
5568 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
5570 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
5577 * Retrieves the state of a listview control item.
5580 * [I] infoPtr : valid pointer to the listview structure
5581 * [I] nItem : item index
5582 * [I] uMask : state mask
5585 * State specified by the mask.
5587 static UINT
LISTVIEW_GetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
5591 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
5593 lvItem
.iItem
= nItem
;
5594 lvItem
.iSubItem
= 0;
5595 lvItem
.mask
= LVIF_STATE
;
5596 lvItem
.stateMask
= uMask
;
5597 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5599 return lvItem
.state
& uMask
;
5604 * Retrieves the text of a listview control item or subitem.
5607 * [I] hwnd : window handle
5608 * [I] nItem : item index
5609 * [IO] lpLVItem : item information
5610 * [I] isW : TRUE if lpLVItem is Unicode
5613 * SUCCESS : string length
5616 static INT
LISTVIEW_GetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
5618 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
5620 lpLVItem
->mask
= LVIF_TEXT
;
5621 lpLVItem
->iItem
= nItem
;
5622 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
5624 return textlenT(lpLVItem
->pszText
, isW
);
5629 * Searches for an item based on properties + relationships.
5632 * [I] infoPtr : valid pointer to the listview structure
5633 * [I] nItem : item index
5634 * [I] uFlags : relationship flag
5637 * SUCCESS : item index
5640 static INT
LISTVIEW_GetNextItem(LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
5642 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5644 LVFINDINFOW lvFindInfo
;
5645 INT nCountPerColumn
;
5649 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
5650 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
5652 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
5654 if (uFlags
& LVNI_CUT
)
5657 if (uFlags
& LVNI_DROPHILITED
)
5658 uMask
|= LVIS_DROPHILITED
;
5660 if (uFlags
& LVNI_FOCUSED
)
5661 uMask
|= LVIS_FOCUSED
;
5663 if (uFlags
& LVNI_SELECTED
)
5664 uMask
|= LVIS_SELECTED
;
5666 /* if we're asked for the focused item, that's only one,
5667 * so it's worth optimizing */
5668 if (uFlags
& LVNI_FOCUSED
)
5670 if (!(LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) == uMask
) return -1;
5671 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
5674 if (uFlags
& LVNI_ABOVE
)
5676 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
5681 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5687 /* Special case for autoarrange - move 'til the top of a list */
5688 if (is_autoarrange(infoPtr
))
5690 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5691 while (nItem
- nCountPerRow
>= 0)
5693 nItem
-= nCountPerRow
;
5694 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5699 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5700 lvFindInfo
.vkDirection
= VK_UP
;
5701 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5702 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5704 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5709 else if (uFlags
& LVNI_BELOW
)
5711 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
5713 while (nItem
< infoPtr
->nItemCount
)
5716 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5722 /* Special case for autoarrange - move 'til the bottom of a list */
5723 if (is_autoarrange(infoPtr
))
5725 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5726 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
5728 nItem
+= nCountPerRow
;
5729 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5734 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5735 lvFindInfo
.vkDirection
= VK_DOWN
;
5736 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5737 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5739 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5744 else if (uFlags
& LVNI_TOLEFT
)
5746 if (uView
== LVS_LIST
)
5748 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
5749 while (nItem
- nCountPerColumn
>= 0)
5751 nItem
-= nCountPerColumn
;
5752 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5756 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5758 /* Special case for autoarrange - move 'ti the beginning of a row */
5759 if (is_autoarrange(infoPtr
))
5761 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5762 while (nItem
% nCountPerRow
> 0)
5765 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5770 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5771 lvFindInfo
.vkDirection
= VK_LEFT
;
5772 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5773 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5775 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5780 else if (uFlags
& LVNI_TORIGHT
)
5782 if (uView
== LVS_LIST
)
5784 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
5785 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
5787 nItem
+= nCountPerColumn
;
5788 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5792 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5794 /* Special case for autoarrange - move 'til the end of a row */
5795 if (is_autoarrange(infoPtr
))
5797 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
5798 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
5801 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5806 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5807 lvFindInfo
.vkDirection
= VK_RIGHT
;
5808 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5809 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5811 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5820 /* search by index */
5821 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
5823 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
5831 /* LISTVIEW_GetNumberOfWorkAreas */
5835 * Retrieves the origin coordinates when in icon or small icon display mode.
5838 * [I] infoPtr : valid pointer to the listview structure
5839 * [O] lpptOrigin : coordinate information
5844 static void LISTVIEW_GetOrigin(LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
5846 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5847 INT nHorzPos
= 0, nVertPos
= 0;
5848 SCROLLINFO scrollInfo
;
5850 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
5851 scrollInfo
.fMask
= SIF_POS
;
5853 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
5854 nHorzPos
= scrollInfo
.nPos
;
5855 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
5856 nVertPos
= scrollInfo
.nPos
;
5858 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
5860 lpptOrigin
->x
= infoPtr
->rcList
.left
;
5861 lpptOrigin
->y
= infoPtr
->rcList
.top
;
5862 if (uView
== LVS_LIST
)
5863 nHorzPos
*= infoPtr
->nItemWidth
;
5864 else if (uView
== LVS_REPORT
)
5865 nVertPos
*= infoPtr
->nItemHeight
;
5867 lpptOrigin
->x
-= nHorzPos
;
5868 lpptOrigin
->y
-= nVertPos
;
5870 TRACE(" origin=%s\n", debugpoint(lpptOrigin
));
5875 * Retrieves the width of a string.
5878 * [I] hwnd : window handle
5879 * [I] lpszText : text string to process
5880 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5883 * SUCCESS : string width (in pixels)
5886 static INT
LISTVIEW_GetStringWidthT(LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
5891 if (is_textT(lpszText
, isW
))
5893 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
5894 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
5895 HFONT hOldFont
= SelectObject(hdc
, hFont
);
5898 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
5900 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
5901 SelectObject(hdc
, hOldFont
);
5902 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
5904 return stringSize
.cx
;
5909 * Determines which listview item is located at the specified position.
5912 * [I] infoPtr : valid pointer to the listview structure
5913 * [IO] lpht : hit test information
5914 * [I] subitem : fill out iSubItem.
5915 * [I] select : return the index only if the hit selects the item
5918 * (mm 20001022): We must not allow iSubItem to be touched, for
5919 * an app might pass only a structure with space up to iItem!
5920 * (MS Office 97 does that for instance in the file open dialog)
5923 * SUCCESS : item index
5926 static INT
LISTVIEW_HitTest(LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
5928 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5929 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5930 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
5931 POINT Origin
, Position
, opt
;
5936 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht
->pt
), subitem
, select
);
5940 if (subitem
) lpht
->iSubItem
= 0;
5942 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
5943 lpht
->flags
|= LVHT_TOLEFT
;
5944 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
5945 lpht
->flags
|= LVHT_TORIGHT
;
5947 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
5948 lpht
->flags
|= LVHT_ABOVE
;
5949 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
5950 lpht
->flags
|= LVHT_BELOW
;
5952 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
5953 if (lpht
->flags
) return -1;
5955 lpht
->flags
|= LVHT_NOWHERE
;
5957 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5959 /* first deal with the large items */
5960 rcSearch
.left
= lpht
->pt
.x
;
5961 rcSearch
.top
= lpht
->pt
.y
;
5962 rcSearch
.right
= rcSearch
.left
+ 1;
5963 rcSearch
.bottom
= rcSearch
.top
+ 1;
5965 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
5966 iterator_next(&i
); /* go to first item in the sequence */
5968 iterator_destroy(&i
);
5970 TRACE("lpht->iItem=%d\n", iItem
);
5971 if (iItem
== -1) return -1;
5973 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
5974 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
5975 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
5976 if (uView
== LVS_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
5977 lvItem
.iItem
= iItem
;
5978 lvItem
.iSubItem
= 0;
5979 lvItem
.pszText
= szDispText
;
5980 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5981 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
5982 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
5984 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcState
, &rcIcon
, &rcLabel
);
5985 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
5986 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
5987 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
5989 if (uView
== LVS_REPORT
)
5992 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
5993 TRACE("rcBounds=%s\n", debugrect(&rcBounds
));
5994 if (!PtInRect(&rcBounds
, opt
)) return -1;
5996 if (PtInRect(&rcIcon
, opt
))
5997 lpht
->flags
|= LVHT_ONITEMICON
;
5998 else if (PtInRect(&rcLabel
, opt
))
5999 lpht
->flags
|= LVHT_ONITEMLABEL
;
6000 else if (infoPtr
->himlState
&& ((lvItem
.state
& LVIS_STATEIMAGEMASK
) >> 12) && PtInRect(&rcState
, opt
))
6001 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
6002 if (lpht
->flags
& LVHT_ONITEM
)
6003 lpht
->flags
&= ~LVHT_NOWHERE
;
6005 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6006 if (uView
== LVS_REPORT
&& subitem
)
6010 rcBounds
.right
= rcBounds
.left
;
6011 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
6013 rcBounds
.left
= rcBounds
.right
;
6014 rcBounds
.right
+= LISTVIEW_GetColumnWidth(infoPtr
, j
);
6015 if (PtInRect(&rcBounds
, opt
))
6023 if (select
&& !(uView
== LVS_REPORT
&&
6024 ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) ||
6025 (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))))
6027 if (uView
== LVS_REPORT
)
6029 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6030 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6032 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
6034 return lpht
->iItem
= iItem
;
6038 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6039 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6040 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6041 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6042 their own sort proc. when sending LVM_SORTITEMS.
6045 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6047 LVS_SORTXXX must be specified,
6048 LVS_OWNERDRAW is not set,
6049 <item>.pszText is not LPSTR_TEXTCALLBACK.
6051 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6052 are sorted based on item text..."
6054 static INT WINAPI
LISTVIEW_InsertCompare( LPVOID first
, LPVOID second
, LPARAM lParam
)
6056 ITEM_INFO
* lv_first
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)first
, 0 );
6057 ITEM_INFO
* lv_second
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)second
, 0 );
6058 INT cmpv
= textcmpWT(lv_first
->hdr
.pszText
, lv_second
->hdr
.pszText
, TRUE
);
6060 /* if we're sorting descending, negate the return value */
6061 return (((LISTVIEW_INFO
*)lParam
)->dwStyle
& LVS_SORTDESCENDING
) ? -cmpv
: cmpv
;
6066 * Inserts a new item in the listview control.
6069 * [I] infoPtr : valid pointer to the listview structure
6070 * [I] lpLVItem : item information
6071 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6074 * SUCCESS : new item index
6077 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
6079 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6084 BOOL is_sorted
, has_changed
;
6087 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6089 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
6091 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6092 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iSubItem
) return -1;
6094 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
6096 if (!(lpItem
= (ITEM_INFO
*)Alloc(sizeof(ITEM_INFO
)))) return -1;
6098 /* insert item in listview control data structure */
6099 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
6100 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
6102 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
6103 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
6105 nItem
= is_sorted
? infoPtr
->nItemCount
: min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
6106 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
6107 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
6108 if (nItem
== -1) goto fail
;
6109 infoPtr
->nItemCount
++;
6111 /* shift indices first so they don't get tangled */
6112 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
6114 /* set the item attributes */
6115 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6117 /* full size structure expected - _WIN32IE >= 0x560 */
6120 else if (lpLVItem
->mask
& LVIF_INDENT
)
6122 /* indent member expected - _WIN32IE >= 0x300 */
6123 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
6127 /* minimal structure expected */
6128 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
6131 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) item
.state
&= ~LVIS_STATEIMAGEMASK
;
6132 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
6134 /* if we're sorted, sort the list, and update the index */
6137 DPA_Sort( infoPtr
->hdpaItems
, LISTVIEW_InsertCompare
, (LPARAM
)infoPtr
);
6138 nItem
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, hdpaSubItems
);
6139 assert(nItem
!= -1);
6142 /* make room for the position, if we are in the right mode */
6143 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6145 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
6147 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
6149 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
6154 /* send LVN_INSERTITEM notification */
6155 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
6157 nmlv
.lParam
= lpItem
->lParam
;
6158 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
6160 /* align items (set position of each item) */
6161 if ((uView
== LVS_SMALLICON
|| uView
== LVS_ICON
))
6165 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
6166 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
6168 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
6170 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
6173 /* now is the invalidation fun */
6174 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
6178 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
6179 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
6180 infoPtr
->nItemCount
--;
6182 DPA_DeletePtr(hdpaSubItems
, 0);
6183 DPA_Destroy (hdpaSubItems
);
6190 * Redraws a range of items.
6193 * [I] infoPtr : valid pointer to the listview structure
6194 * [I] nFirst : first item
6195 * [I] nLast : last item
6201 static BOOL
LISTVIEW_RedrawItems(LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
6205 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
6206 max(nFirst
, nLast
) >= infoPtr
->nItemCount
)
6209 for (i
= nFirst
; i
<= nLast
; i
++)
6210 LISTVIEW_InvalidateItem(infoPtr
, i
);
6217 * Scroll the content of a listview.
6220 * [I] infoPtr : valid pointer to the listview structure
6221 * [I] dx : horizontal scroll amount in pixels
6222 * [I] dy : vertical scroll amount in pixels
6229 * If the control is in report mode (LVS_REPORT) the control can
6230 * be scrolled only in line increments. "dy" will be rounded to the
6231 * nearest number of pixels that are a whole line. Ex: if line height
6232 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6233 * is passed the the scroll will be 0. (per MSDN 7/2002)
6235 * For: (per experimentaion with native control and CSpy ListView)
6236 * LVS_ICON dy=1 = 1 pixel (vertical only)
6238 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6240 * LVS_LIST dx=1 = 1 column (horizontal only)
6241 * but will only scroll 1 column per message
6242 * no matter what the value.
6243 * dy must be 0 or FALSE returned.
6244 * LVS_REPORT dx=1 = 1 pixel
6248 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
6250 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
) {
6252 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
6253 dy
/= infoPtr
->nItemHeight
;
6256 if (dy
!= 0) return FALSE
;
6263 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
, 0);
6264 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
, 0);
6271 * Sets the background color.
6274 * [I] infoPtr : valid pointer to the listview structure
6275 * [I] clrBk : background color
6281 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrBk
)
6283 TRACE("(clrBk=%lx)\n", clrBk
);
6285 if(infoPtr
->clrBk
!= clrBk
) {
6286 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
6287 infoPtr
->clrBk
= clrBk
;
6288 if (clrBk
== CLR_NONE
)
6289 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
6291 infoPtr
->hBkBrush
= CreateSolidBrush(clrBk
);
6292 LISTVIEW_InvalidateList(infoPtr
);
6298 /* LISTVIEW_SetBkImage */
6300 /*** Helper for {Insert,Set}ColumnT *only* */
6301 static void column_fill_hditem(LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
, const LVCOLUMNW
*lpColumn
, BOOL isW
)
6303 if (lpColumn
->mask
& LVCF_FMT
)
6305 /* format member is valid */
6306 lphdi
->mask
|= HDI_FORMAT
;
6308 /* set text alignment (leftmost column must be left-aligned) */
6309 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
6310 lphdi
->fmt
|= HDF_LEFT
;
6311 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
6312 lphdi
->fmt
|= HDF_RIGHT
;
6313 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
6314 lphdi
->fmt
|= HDF_CENTER
;
6316 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6317 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
6319 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6321 lphdi
->fmt
|= HDF_IMAGE
;
6322 lphdi
->iImage
= I_IMAGECALLBACK
;
6326 if (lpColumn
->mask
& LVCF_WIDTH
)
6328 lphdi
->mask
|= HDI_WIDTH
;
6329 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
6331 /* make it fill the remainder of the controls width */
6335 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
6337 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
6338 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
6341 /* retrieve the layout of the header */
6342 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
6343 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, debugrect(&rcHeader
));
6345 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
6348 lphdi
->cxy
= lpColumn
->cx
;
6351 if (lpColumn
->mask
& LVCF_TEXT
)
6353 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
6354 lphdi
->fmt
|= HDF_STRING
;
6355 lphdi
->pszText
= lpColumn
->pszText
;
6356 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
6359 if (lpColumn
->mask
& LVCF_IMAGE
)
6361 lphdi
->mask
|= HDI_IMAGE
;
6362 lphdi
->iImage
= lpColumn
->iImage
;
6365 if (lpColumn
->mask
& LVCF_ORDER
)
6367 lphdi
->mask
|= HDI_ORDER
;
6368 lphdi
->iOrder
= lpColumn
->iOrder
;
6375 * Inserts a new column.
6378 * [I] infoPtr : valid pointer to the listview structure
6379 * [I] nColumn : column index
6380 * [I] lpColumn : column information
6381 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6384 * SUCCESS : new column index
6387 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6388 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6390 COLUMN_INFO
*lpColumnInfo
;
6394 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6396 if (!lpColumn
|| nColumn
< 0) return -1;
6397 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
6399 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6400 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6402 /* insert item in header control */
6403 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
6404 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
6405 (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6406 if (nNewColumn
== -1) return -1;
6407 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
6409 /* create our own column info */
6410 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
6411 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
6413 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
6414 if (!Header_GetItemRect(infoPtr
->hwndHeader
, nNewColumn
, &lpColumnInfo
->rcHeader
)) goto fail
;
6416 /* now we have to actually adjust the data */
6417 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
6419 SUBITEM_INFO
*lpSubItem
;
6423 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
6425 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
6426 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
6428 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
6429 if (lpSubItem
->iSubItem
>= nNewColumn
)
6430 lpSubItem
->iSubItem
++;
6435 /* make space for the new column */
6436 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
6441 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
6444 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
6452 * Sets the attributes of a header item.
6455 * [I] infoPtr : valid pointer to the listview structure
6456 * [I] nColumn : column index
6457 * [I] lpColumn : column attributes
6458 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6464 static BOOL
LISTVIEW_SetColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6465 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6467 HDITEMW hdi
, hdiget
;
6470 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6472 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6474 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6475 if (lpColumn
->mask
& LVCF_FMT
)
6477 hdi
.mask
|= HDI_FORMAT
;
6478 hdiget
.mask
= HDI_FORMAT
;
6479 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdiget
))
6480 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
6482 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6484 /* set header item attributes */
6485 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6486 if (!bResult
) return FALSE
;
6488 if (lpColumn
->mask
& LVCF_FMT
)
6490 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
6491 int oldFmt
= lpColumnInfo
->fmt
;
6493 lpColumnInfo
->fmt
= lpColumn
->fmt
;
6494 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
6496 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6497 if (uView
== LVS_REPORT
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
6506 * Sets the column order array
6509 * [I] infoPtr : valid pointer to the listview structure
6510 * [I] iCount : number of elements in column order array
6511 * [I] lpiArray : pointer to column order array
6517 static BOOL
LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
6519 FIXME("iCount %d lpiArray %p\n", iCount
, lpiArray
);
6530 * Sets the width of a column
6533 * [I] infoPtr : valid pointer to the listview structure
6534 * [I] nColumn : column index
6535 * [I] cx : column width
6541 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
6543 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6544 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
6548 TRACE("(nColumn=%d, cx=%d\n", nColumn
, cx
);
6550 /* set column width only if in report or list mode */
6551 if (uView
!= LVS_REPORT
&& uView
!= LVS_LIST
) return FALSE
;
6553 /* take care of invalid cx values */
6554 if(uView
== LVS_REPORT
&& cx
< -2) cx
= LVSCW_AUTOSIZE
;
6555 else if (uView
== LVS_LIST
&& cx
< 1) return FALSE
;
6557 /* resize all columns if in LVS_LIST mode */
6558 if(uView
== LVS_LIST
)
6560 infoPtr
->nItemWidth
= cx
;
6561 LISTVIEW_InvalidateList(infoPtr
);
6565 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6567 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
6572 lvItem
.mask
= LVIF_TEXT
;
6574 lvItem
.iSubItem
= nColumn
;
6575 lvItem
.pszText
= szDispText
;
6576 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6577 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
6579 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
6580 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
6581 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
6583 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
6584 max_cx
+= infoPtr
->iconSize
.cx
;
6585 max_cx
+= TRAILING_LABEL_PADDING
;
6588 /* autosize based on listview items width */
6589 if(cx
== LVSCW_AUTOSIZE
)
6591 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
6593 /* if iCol is the last column make it fill the remainder of the controls width */
6594 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
6599 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6600 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
6602 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
6606 /* Despite what the MS docs say, if this is not the last
6607 column, then MS resizes the column to the width of the
6608 largest text string in the column, including headers
6609 and items. This is different from LVSCW_AUTOSIZE in that
6610 LVSCW_AUTOSIZE ignores the header string length. */
6613 /* retrieve header text */
6614 hdi
.mask
= HDI_TEXT
;
6615 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
6616 hdi
.pszText
= szDispText
;
6617 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, (LPARAM
)&hdi
))
6619 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
6620 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
6623 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
6624 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
6625 /* FIXME: Take into account the header image, if one is present */
6626 SelectObject(hdc
, old_font
);
6627 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6629 cx
= max (cx
, max_cx
);
6633 if (cx
< 0) return FALSE
;
6635 /* call header to update the column change */
6636 hdi
.mask
= HDI_WIDTH
;
6638 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
6639 return Header_SetItemW(infoPtr
->hwndHeader
, nColumn
, (LPARAM
)&hdi
);
6643 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6646 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO
*infoPtr
)
6649 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
6651 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
6652 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
6655 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
6656 ILC_COLOR
| ILC_MASK
, 2, 2);
6657 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
6658 hdc
= CreateCompatibleDC(hdc_wnd
);
6659 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
6660 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
6661 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
6663 rc
.left
= rc
.top
= 0;
6664 rc
.right
= GetSystemMetrics(SM_CXSMICON
);
6665 rc
.bottom
= GetSystemMetrics(SM_CYSMICON
);
6667 hbm_orig
= SelectObject(hdc
, hbm_mask
);
6668 FillRect(hdc
, &rc
, hbr_white
);
6669 InflateRect(&rc
, -3, -3);
6670 FillRect(hdc
, &rc
, hbr_black
);
6672 SelectObject(hdc
, hbm_im
);
6673 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
6674 SelectObject(hdc
, hbm_orig
);
6675 ImageList_Add(himl
, hbm_im
, hbm_mask
);
6677 SelectObject(hdc
, hbm_im
);
6678 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
6679 SelectObject(hdc
, hbm_orig
);
6680 ImageList_Add(himl
, hbm_im
, hbm_mask
);
6682 DeleteObject(hbm_mask
);
6683 DeleteObject(hbm_im
);
6691 * Sets the extended listview style.
6694 * [I] infoPtr : valid pointer to the listview structure
6696 * [I] dwStyle : style
6699 * SUCCESS : previous style
6702 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD dwMask
, DWORD dwStyle
)
6704 DWORD dwOldStyle
= infoPtr
->dwLvExStyle
;
6708 infoPtr
->dwLvExStyle
= (dwOldStyle
& ~dwMask
) | (dwStyle
& dwMask
);
6710 infoPtr
->dwLvExStyle
= dwStyle
;
6712 if((infoPtr
->dwLvExStyle
^ dwOldStyle
) & LVS_EX_CHECKBOXES
)
6714 HIMAGELIST himl
= 0;
6715 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
6716 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
6717 LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
6725 * Sets the new hot cursor used during hot tracking and hover selection.
6728 * [I] infoPtr : valid pointer to the listview structure
6729 * [I} hCurosr : the new hot cursor handle
6732 * Returns the previous hot cursor
6734 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
6736 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
6738 infoPtr
->hHotCursor
= hCursor
;
6746 * Sets the hot item index.
6749 * [I] infoPtr : valid pointer to the listview structure
6750 * [I] iIndex : index
6753 * SUCCESS : previous hot item index
6754 * FAILURE : -1 (no hot item)
6756 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
6758 INT iOldIndex
= infoPtr
->nHotItem
;
6760 infoPtr
->nHotItem
= iIndex
;
6768 * Sets the amount of time the cursor must hover over an item before it is selected.
6771 * [I] infoPtr : valid pointer to the listview structure
6772 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6775 * Returns the previous hover time
6777 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
6779 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
6781 infoPtr
->dwHoverTime
= dwHoverTime
;
6783 return oldHoverTime
;
6788 * Sets spacing for icons of LVS_ICON style.
6791 * [I] infoPtr : valid pointer to the listview structure
6792 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6793 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6796 * MAKELONG(oldcx, oldcy)
6798 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
6800 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
6801 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6803 TRACE("requested=(%d,%d)\n", cx
, cy
);
6805 /* this is supported only for LVS_ICON style */
6806 if (uView
!= LVS_ICON
) return oldspacing
;
6808 /* set to defaults, if instructed to */
6809 if (cx
== -1) cx
= GetSystemMetrics(SM_CXICONSPACING
);
6810 if (cy
== -1) cy
= GetSystemMetrics(SM_CYICONSPACING
);
6812 /* if 0 then compute width
6813 * FIXME: Should scan each item and determine max width of
6814 * icon or label, then make that the width */
6816 cx
= infoPtr
->iconSpacing
.cx
;
6818 /* if 0 then compute height */
6820 cy
= infoPtr
->iconSize
.cy
+ 2 * infoPtr
->ntmHeight
+
6821 ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_PADDING
;
6824 infoPtr
->iconSpacing
.cx
= cx
;
6825 infoPtr
->iconSpacing
.cy
= cy
;
6827 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6828 LOWORD(oldspacing
), HIWORD(oldspacing
), cx
, cy
,
6829 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
6830 infoPtr
->ntmHeight
);
6832 /* these depend on the iconSpacing */
6833 LISTVIEW_UpdateItemSize(infoPtr
);
6838 inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL small
)
6842 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
6849 size
->cx
= GetSystemMetrics(small
? SM_CXSMICON
: SM_CXICON
);
6850 size
->cy
= GetSystemMetrics(small
? SM_CYSMICON
: SM_CYICON
);
6859 * [I] infoPtr : valid pointer to the listview structure
6860 * [I] nType : image list type
6861 * [I] himl : image list handle
6864 * SUCCESS : old image list
6867 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
6869 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6870 INT oldHeight
= infoPtr
->nItemHeight
;
6871 HIMAGELIST himlOld
= 0;
6873 TRACE("(nType=%d, himl=%p\n", nType
, himl
);
6878 himlOld
= infoPtr
->himlNormal
;
6879 infoPtr
->himlNormal
= himl
;
6880 if (uView
== LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
6881 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
6885 himlOld
= infoPtr
->himlSmall
;
6886 infoPtr
->himlSmall
= himl
;
6887 if (uView
!= LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
6891 himlOld
= infoPtr
->himlState
;
6892 infoPtr
->himlState
= himl
;
6893 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
6894 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
6898 ERR("Unknown icon type=%d\n", nType
);
6902 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
6903 if (infoPtr
->nItemHeight
!= oldHeight
)
6904 LISTVIEW_UpdateScroll(infoPtr
);
6911 * Preallocates memory (does *not* set the actual count of items !)
6914 * [I] infoPtr : valid pointer to the listview structure
6915 * [I] nItems : item count (projected number of items to allocate)
6916 * [I] dwFlags : update flags
6922 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
6924 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems
, dwFlags
);
6926 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
6928 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6929 INT nOldCount
= infoPtr
->nItemCount
;
6931 if (nItems
< nOldCount
)
6933 RANGE range
= { nItems
, nOldCount
};
6934 ranges_del(infoPtr
->selectionRanges
, range
);
6935 if (infoPtr
->nFocusedItem
>= nItems
)
6937 infoPtr
->nFocusedItem
= -1;
6938 SetRectEmpty(&infoPtr
->rcFocus
);
6942 infoPtr
->nItemCount
= nItems
;
6943 LISTVIEW_UpdateScroll(infoPtr
);
6945 /* the flags are valid only in ownerdata report and list modes */
6946 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
) dwFlags
= 0;
6948 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
6949 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
6951 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
6952 LISTVIEW_InvalidateList(infoPtr
);
6959 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6960 nFrom
= min(nOldCount
, nItems
);
6961 nTo
= max(nOldCount
, nItems
);
6963 if (uView
== LVS_REPORT
)
6966 rcErase
.top
= nFrom
* infoPtr
->nItemHeight
;
6967 rcErase
.right
= infoPtr
->nItemWidth
;
6968 rcErase
.bottom
= nTo
* infoPtr
->nItemHeight
;
6969 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
6970 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
6971 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
6975 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
6977 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
6978 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
6979 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
6980 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
6981 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
6982 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
6983 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
6985 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
6987 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
6988 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
6989 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
6990 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
6991 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
6997 /* According to MSDN for non-LVS_OWNERDATA this is just
6998 * a performance issue. The control allocates its internal
6999 * data structures for the number of items specified. It
7000 * cuts down on the number of memory allocations. Therefore
7001 * we will just issue a WARN here
7003 WARN("for non-ownerdata performance option not implemented.\n");
7011 * Sets the position of an item.
7014 * [I] infoPtr : valid pointer to the listview structure
7015 * [I] nItem : item index
7016 * [I] pt : coordinate
7022 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, POINT pt
)
7024 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7027 TRACE("(nItem=%d, &pt=%s\n", nItem
, debugpoint(&pt
));
7029 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
7030 !(uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)) return FALSE
;
7032 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7034 /* This point value seems to be an undocumented feature.
7035 * The best guess is that it means either at the origin,
7036 * or at true beginning of the list. I will assume the origin. */
7037 if ((pt
.x
== -1) && (pt
.y
== -1))
7040 if (uView
== LVS_ICON
)
7042 pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
7043 pt
.y
-= ICON_TOP_PADDING
;
7048 infoPtr
->bAutoarrange
= FALSE
;
7050 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, FALSE
);
7055 * Sets the state of one or many items.
7058 * [I] infoPtr : valid pointer to the listview structure
7059 * [I] nItem : item index
7060 * [I] lpLVItem : item or subitem info
7066 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
)
7068 BOOL bResult
= TRUE
;
7071 lvItem
.iItem
= nItem
;
7072 lvItem
.iSubItem
= 0;
7073 lvItem
.mask
= LVIF_STATE
;
7074 lvItem
.state
= lpLVItem
->state
;
7075 lvItem
.stateMask
= lpLVItem
->stateMask
;
7076 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
7080 /* apply to all items */
7081 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7082 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) bResult
= FALSE
;
7085 bResult
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
7088 * Update selection mark
7090 * Investigation on windows 2k showed that selection mark was updated
7091 * whenever a new selection was made, but if the selected item was
7092 * unselected it was not updated.
7094 * we are probably still not 100% accurate, but this at least sets the
7095 * proper selection mark when it is needed
7098 if (bResult
&& (lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) &&
7099 ((infoPtr
->nSelectionMark
== -1) || (lvItem
.iItem
<= infoPtr
->nSelectionMark
)))
7102 infoPtr
->nSelectionMark
= -1;
7103 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
7105 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
7107 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
7109 infoPtr
->nSelectionMark
= i
;
7113 else if (ranges_contain(infoPtr
->selectionRanges
, i
))
7115 infoPtr
->nSelectionMark
= i
;
7126 * Sets the text of an item or subitem.
7129 * [I] hwnd : window handle
7130 * [I] nItem : item index
7131 * [I] lpLVItem : item or subitem info
7132 * [I] isW : TRUE if input is Unicode
7138 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
7142 if (nItem
< 0 && nItem
>= infoPtr
->nItemCount
) return FALSE
;
7144 lvItem
.iItem
= nItem
;
7145 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
7146 lvItem
.mask
= LVIF_TEXT
;
7147 lvItem
.pszText
= lpLVItem
->pszText
;
7148 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
7150 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
7152 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
7157 * Set item index that marks the start of a multiple selection.
7160 * [I] infoPtr : valid pointer to the listview structure
7161 * [I] nIndex : index
7164 * Index number or -1 if there is no selection mark.
7166 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
7168 INT nOldIndex
= infoPtr
->nSelectionMark
;
7170 TRACE("(nIndex=%d)\n", nIndex
);
7172 infoPtr
->nSelectionMark
= nIndex
;
7179 * Sets the text background color.
7182 * [I] infoPtr : valid pointer to the listview structure
7183 * [I] clrTextBk : text background color
7189 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrTextBk
)
7191 TRACE("(clrTextBk=%lx)\n", clrTextBk
);
7193 if (infoPtr
->clrTextBk
!= clrTextBk
)
7195 infoPtr
->clrTextBk
= clrTextBk
;
7196 LISTVIEW_InvalidateList(infoPtr
);
7204 * Sets the text foreground color.
7207 * [I] infoPtr : valid pointer to the listview structure
7208 * [I] clrText : text color
7214 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF clrText
)
7216 TRACE("(clrText=%lx)\n", clrText
);
7218 if (infoPtr
->clrText
!= clrText
)
7220 infoPtr
->clrText
= clrText
;
7221 LISTVIEW_InvalidateList(infoPtr
);
7229 * Determines which listview item is located at the specified position.
7232 * [I] infoPtr : valid pointer to the listview structure
7233 * [I] hwndNewToolTip : handle to new ToolTip
7238 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
7240 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
7241 infoPtr
->hwndToolTip
= hwndNewToolTip
;
7242 return hwndOldToolTip
;
7245 /* LISTVIEW_SetUnicodeFormat */
7246 /* LISTVIEW_SetWorkAreas */
7250 * Callback internally used by LISTVIEW_SortItems()
7253 * [I] first : pointer to first ITEM_INFO to compare
7254 * [I] second : pointer to second ITEM_INFO to compare
7255 * [I] lParam : HWND of control
7258 * if first comes before second : negative
7259 * if first comes after second : positive
7260 * if first and second are equivalent : zero
7262 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
7264 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
7265 ITEM_INFO
* lv_first
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)first
, 0 );
7266 ITEM_INFO
* lv_second
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)second
, 0 );
7267 PFNLVCOMPARE CompareFunction
= infoPtr
->pfnCompare
;
7269 /* Forward the call to the client defined callback */
7271 return (CompareFunction
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
7276 * Sorts the listview items.
7279 * [I] infoPtr : valid pointer to the listview structure
7280 * [I] pfnCompare : application-defined value
7281 * [I] lParamSort : pointer to comparision callback
7287 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
, LPARAM lParamSort
)
7289 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7292 LPVOID selectionMarkItem
;
7296 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
7298 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
7300 if (!pfnCompare
) return FALSE
;
7301 if (!infoPtr
->hdpaItems
) return FALSE
;
7303 /* if there are 0 or 1 items, there is no need to sort */
7304 if (infoPtr
->nItemCount
< 2) return TRUE
;
7306 if (infoPtr
->nFocusedItem
>= 0)
7308 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
7309 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
7310 if (lpItem
) lpItem
->state
|= LVIS_FOCUSED
;
7312 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7313 /* clear the lpItem->state for non-selected ones */
7314 /* remove the selection ranges */
7316 infoPtr
->pfnCompare
= pfnCompare
;
7317 infoPtr
->lParamSort
= lParamSort
;
7318 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
7320 /* Adjust selections and indices so that they are the way they should
7321 * be after the sort (otherwise, the list items move around, but
7322 * whatever is at the item's previous original position will be
7325 selectionMarkItem
=(infoPtr
->nSelectionMark
>=0)?DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
):NULL
;
7326 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
7328 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
7329 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
7331 if (lpItem
->state
& LVIS_SELECTED
)
7333 item
.state
= LVIS_SELECTED
;
7334 item
.stateMask
= LVIS_SELECTED
;
7335 LISTVIEW_SetItemState(infoPtr
, i
, &item
);
7337 if (lpItem
->state
& LVIS_FOCUSED
)
7339 infoPtr
->nFocusedItem
= i
;
7340 lpItem
->state
&= ~LVIS_FOCUSED
;
7343 if (selectionMarkItem
!= NULL
)
7344 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
7345 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7347 /* refresh the display */
7348 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
)
7349 LISTVIEW_InvalidateList(infoPtr
);
7356 * Updates an items or rearranges the listview control.
7359 * [I] infoPtr : valid pointer to the listview structure
7360 * [I] nItem : item index
7366 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
7368 TRACE("(nItem=%d)\n", nItem
);
7370 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
7372 /* rearrange with default alignment style */
7373 if (is_autoarrange(infoPtr
))
7374 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
7376 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
7384 * Creates the listview control.
7387 * [I] hwnd : window handle
7388 * [I] lpcs : the create parameters
7394 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
7396 LISTVIEW_INFO
*infoPtr
;
7397 UINT uView
= lpcs
->style
& LVS_TYPEMASK
;
7400 TRACE("(lpcs=%p)\n", lpcs
);
7402 /* initialize info pointer */
7403 infoPtr
= (LISTVIEW_INFO
*)Alloc(sizeof(LISTVIEW_INFO
));
7404 if (!infoPtr
) return -1;
7406 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
7408 infoPtr
->hwndSelf
= hwnd
;
7409 infoPtr
->dwStyle
= lpcs
->style
;
7410 /* determine the type of structures to use */
7411 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
7412 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
7413 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
7415 /* initialize color information */
7416 infoPtr
->clrBk
= CLR_NONE
;
7417 infoPtr
->clrText
= comctl32_color
.clrWindowText
;
7418 infoPtr
->clrTextBk
= CLR_DEFAULT
;
7419 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
7421 /* set default values */
7422 infoPtr
->nFocusedItem
= -1;
7423 infoPtr
->nSelectionMark
= -1;
7424 infoPtr
->nHotItem
= -1;
7425 infoPtr
->bRedraw
= TRUE
;
7426 infoPtr
->bNoItemMetrics
= TRUE
;
7427 infoPtr
->bDoChangeNotify
= TRUE
;
7428 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
7429 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
7430 infoPtr
->nEditLabelItem
= -1;
7431 infoPtr
->dwHoverTime
= -1; /* default system hover time */
7433 /* get default font (icon title) */
7434 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
7435 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
7436 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
7437 LISTVIEW_SaveTextMetrics(infoPtr
);
7440 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
,
7441 WS_CHILD
| HDS_HORZ
| (DWORD
)((LVS_NOSORTHEADER
& lpcs
->style
)?0:HDS_BUTTONS
),
7442 0, 0, 0, 0, hwnd
, NULL
,
7443 lpcs
->hInstance
, NULL
);
7444 if (!infoPtr
->hwndHeader
) goto fail
;
7446 /* set header unicode format */
7447 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, (WPARAM
)TRUE
, (LPARAM
)NULL
);
7449 /* set header font */
7450 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, (LPARAM
)TRUE
);
7452 /* allocate memory for the data structure */
7453 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
7454 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
7455 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
7456 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
7457 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
7459 /* initialize the icon sizes */
7460 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, uView
!= LVS_ICON
);
7461 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
7463 /* init item size to avoid division by 0 */
7464 LISTVIEW_UpdateItemSize (infoPtr
);
7466 if (uView
== LVS_REPORT
)
7468 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
))
7470 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
7474 /* set HDS_HIDDEN flag to hide the header bar */
7475 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
,
7476 GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
) | HDS_HIDDEN
);
7483 DestroyWindow(infoPtr
->hwndHeader
);
7484 ranges_destroy(infoPtr
->selectionRanges
);
7485 DPA_Destroy(infoPtr
->hdpaItems
);
7486 DPA_Destroy(infoPtr
->hdpaPosX
);
7487 DPA_Destroy(infoPtr
->hdpaPosY
);
7488 DPA_Destroy(infoPtr
->hdpaColumns
);
7495 * Erases the background of the listview control.
7498 * [I] infoPtr : valid pointer to the listview structure
7499 * [I] hdc : device context handle
7505 static inline BOOL
LISTVIEW_EraseBkgnd(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
7509 TRACE("(hdc=%p)\n", hdc
);
7511 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
7513 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
7519 * Helper function for LISTVIEW_[HV]Scroll *only*.
7520 * Performs vertical/horizontal scrolling by a give amount.
7523 * [I] infoPtr : valid pointer to the listview structure
7524 * [I] dx : amount of horizontal scroll
7525 * [I] dy : amount of vertical scroll
7527 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
7529 /* now we can scroll the list */
7530 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
7531 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
7532 /* if we have focus, adjust rect */
7533 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
7534 UpdateWindow(infoPtr
->hwndSelf
);
7539 * Performs vertical scrolling.
7542 * [I] infoPtr : valid pointer to the listview structure
7543 * [I] nScrollCode : scroll code
7544 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7545 * [I] hScrollWnd : scrollbar control window handle
7551 * SB_LINEUP/SB_LINEDOWN:
7552 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7553 * for LVS_REPORT is 1 line
7554 * for LVS_LIST cannot occur
7557 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
7558 INT nScrollDiff
, HWND hScrollWnd
)
7560 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7561 INT nOldScrollPos
, nNewScrollPos
;
7562 SCROLLINFO scrollInfo
;
7565 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
7566 debugscrollcode(nScrollCode
), nScrollDiff
);
7568 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
7570 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7571 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
7573 is_an_icon
= ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
));
7575 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
7577 nOldScrollPos
= scrollInfo
.nPos
;
7578 switch (nScrollCode
)
7584 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
7588 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
7592 nScrollDiff
= -scrollInfo
.nPage
;
7596 nScrollDiff
= scrollInfo
.nPage
;
7599 case SB_THUMBPOSITION
:
7601 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
7608 /* quit right away if pos isn't changing */
7609 if (nScrollDiff
== 0) return 0;
7611 /* calculate new position, and handle overflows */
7612 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
7613 if (nScrollDiff
> 0) {
7614 if (nNewScrollPos
< nOldScrollPos
||
7615 nNewScrollPos
> scrollInfo
.nMax
)
7616 nNewScrollPos
= scrollInfo
.nMax
;
7618 if (nNewScrollPos
> nOldScrollPos
||
7619 nNewScrollPos
< scrollInfo
.nMin
)
7620 nNewScrollPos
= scrollInfo
.nMin
;
7623 /* set the new position, and reread in case it changed */
7624 scrollInfo
.fMask
= SIF_POS
;
7625 scrollInfo
.nPos
= nNewScrollPos
;
7626 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
7628 /* carry on only if it really changed */
7629 if (nNewScrollPos
== nOldScrollPos
) return 0;
7631 /* now adjust to client coordinates */
7632 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
7633 if (uView
== LVS_REPORT
) nScrollDiff
*= infoPtr
->nItemHeight
;
7635 /* and scroll the window */
7636 scroll_list(infoPtr
, 0, nScrollDiff
);
7643 * Performs horizontal scrolling.
7646 * [I] infoPtr : valid pointer to the listview structure
7647 * [I] nScrollCode : scroll code
7648 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7649 * [I] hScrollWnd : scrollbar control window handle
7655 * SB_LINELEFT/SB_LINERIGHT:
7656 * for LVS_ICON, LVS_SMALLICON 1 pixel
7657 * for LVS_REPORT is 1 pixel
7658 * for LVS_LIST is 1 column --> which is a 1 because the
7659 * scroll is based on columns not pixels
7662 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
7663 INT nScrollDiff
, HWND hScrollWnd
)
7665 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7666 INT nOldScrollPos
, nNewScrollPos
;
7667 SCROLLINFO scrollInfo
;
7669 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
7670 debugscrollcode(nScrollCode
), nScrollDiff
);
7672 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
7674 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7675 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
7677 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
7679 nOldScrollPos
= scrollInfo
.nPos
;
7681 switch (nScrollCode
)
7695 nScrollDiff
= -scrollInfo
.nPage
;
7699 nScrollDiff
= scrollInfo
.nPage
;
7702 case SB_THUMBPOSITION
:
7704 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
7711 /* quit right away if pos isn't changing */
7712 if (nScrollDiff
== 0) return 0;
7714 /* calculate new position, and handle overflows */
7715 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
7716 if (nScrollDiff
> 0) {
7717 if (nNewScrollPos
< nOldScrollPos
||
7718 nNewScrollPos
> scrollInfo
.nMax
)
7719 nNewScrollPos
= scrollInfo
.nMax
;
7721 if (nNewScrollPos
> nOldScrollPos
||
7722 nNewScrollPos
< scrollInfo
.nMin
)
7723 nNewScrollPos
= scrollInfo
.nMin
;
7726 /* set the new position, and reread in case it changed */
7727 scrollInfo
.fMask
= SIF_POS
;
7728 scrollInfo
.nPos
= nNewScrollPos
;
7729 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
7731 /* carry on only if it really changed */
7732 if (nNewScrollPos
== nOldScrollPos
) return 0;
7734 if(uView
== LVS_REPORT
)
7735 LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
7737 /* now adjust to client coordinates */
7738 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
7739 if (uView
== LVS_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
7741 /* and scroll the window */
7742 scroll_list(infoPtr
, nScrollDiff
, 0);
7747 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
7749 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7750 INT gcWheelDelta
= 0;
7751 INT pulScrollLines
= 3;
7752 SCROLLINFO scrollInfo
;
7754 TRACE("(wheelDelta=%d)\n", wheelDelta
);
7756 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
7757 gcWheelDelta
-= wheelDelta
;
7759 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7760 scrollInfo
.fMask
= SIF_POS
;
7767 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7768 * should be fixed in the future.
7770 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (gcWheelDelta
< 0) ?
7771 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
, 0);
7775 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
7777 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
7778 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
7779 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, cLineScroll
, 0);
7784 LISTVIEW_HScroll(infoPtr
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0, 0);
7795 * [I] infoPtr : valid pointer to the listview structure
7796 * [I] nVirtualKey : virtual key
7797 * [I] lKeyData : key data
7802 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
7804 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7806 NMLVKEYDOWN nmKeyDown
;
7808 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey
, lKeyData
);
7810 /* send LVN_KEYDOWN notification */
7811 nmKeyDown
.wVKey
= nVirtualKey
;
7812 nmKeyDown
.flags
= 0;
7813 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
7815 switch (nVirtualKey
)
7818 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
7820 notify(infoPtr
, NM_RETURN
);
7821 notify(infoPtr
, LVN_ITEMACTIVATE
);
7826 if (infoPtr
->nItemCount
> 0)
7831 if (infoPtr
->nItemCount
> 0)
7832 nItem
= infoPtr
->nItemCount
- 1;
7836 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
7840 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
7844 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
7848 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
7852 if (uView
== LVS_REPORT
)
7853 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
);
7855 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
7856 * LISTVIEW_GetCountPerRow(infoPtr
);
7857 if(nItem
< 0) nItem
= 0;
7861 if (uView
== LVS_REPORT
)
7862 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
);
7864 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
7865 * LISTVIEW_GetCountPerRow(infoPtr
);
7866 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
7870 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
))
7871 LISTVIEW_KeySelection(infoPtr
, nItem
);
7881 * [I] infoPtr : valid pointer to the listview structure
7886 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
7890 /* if we did not have the focus, there's nothing to do */
7891 if (!infoPtr
->bFocus
) return 0;
7893 /* send NM_KILLFOCUS notification */
7894 notify(infoPtr
, NM_KILLFOCUS
);
7896 /* if we have a focus rectagle, get rid of it */
7897 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
7899 /* set window focus flag */
7900 infoPtr
->bFocus
= FALSE
;
7902 /* invalidate the selected items before reseting focus flag */
7903 LISTVIEW_InvalidateSelectedItems(infoPtr
);
7911 * Track mouse/dragging
7914 * [I] infoPtr : valid pointer to the listview structure
7915 * [I] pt : mouse coordinate
7920 static LRESULT
LISTVIEW_TrackMouse(LISTVIEW_INFO
*infoPtr
, POINT pt
)
7922 INT cxDrag
= GetSystemMetrics(SM_CXDRAG
);
7923 INT cyDrag
= GetSystemMetrics(SM_CYDRAG
);
7929 r
.top
= pt
.y
- cyDrag
;
7930 r
.left
= pt
.x
- cxDrag
;
7931 r
.bottom
= pt
.y
+ cyDrag
;
7932 r
.right
= pt
.x
+ cxDrag
;
7934 SetCapture(infoPtr
->hwndSelf
);
7938 if (PeekMessageW(&msg
, 0, 0, 0, PM_REMOVE
| PM_NOYIELD
))
7940 if (msg
.message
== WM_MOUSEMOVE
)
7942 pt
.x
= (short)LOWORD(msg
.lParam
);
7943 pt
.y
= (short)HIWORD(msg
.lParam
);
7944 if (PtInRect(&r
, pt
))
7952 else if (msg
.message
>= WM_LBUTTONDOWN
&&
7953 msg
.message
<= WM_RBUTTONDBLCLK
)
7958 DispatchMessageW(&msg
);
7961 if (GetCapture() != infoPtr
->hwndSelf
)
7972 * Processes double click messages (left mouse button).
7975 * [I] infoPtr : valid pointer to the listview structure
7976 * [I] wKey : key flag
7977 * [I] x,y : mouse coordinate
7982 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
7984 LVHITTESTINFO htInfo
;
7986 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
7988 /* send NM_RELEASEDCAPTURE notification */
7989 notify(infoPtr
, NM_RELEASEDCAPTURE
);
7994 /* send NM_DBLCLK notification */
7995 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
7996 notify_click(infoPtr
, NM_DBLCLK
, &htInfo
);
7998 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7999 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
8006 * Processes mouse down messages (left mouse button).
8009 * [I] infoPtr : valid pointer to the listview structure
8010 * [I] wKey : key flag
8011 * [I] x,y : mouse coordinate
8016 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8018 LVHITTESTINFO lvHitTestInfo
;
8019 static BOOL bGroupSelect
= TRUE
;
8020 POINT pt
= { x
, y
};
8023 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8025 /* send NM_RELEASEDCAPTURE notification */
8026 notify(infoPtr
, NM_RELEASEDCAPTURE
);
8028 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
8030 /* set left button down flag */
8031 infoPtr
->bLButtonDown
= TRUE
;
8033 lvHitTestInfo
.pt
.x
= x
;
8034 lvHitTestInfo
.pt
.y
= y
;
8036 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8037 TRACE("at %s, nItem=%d\n", debugpoint(&pt
), nItem
);
8038 infoPtr
->nEditLabelItem
= -1;
8039 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8041 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
8043 DWORD state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_STATEIMAGEMASK
) >> 12;
8044 if(state
== 1 || state
== 2)
8048 lvitem
.state
= state
<< 12;
8049 lvitem
.stateMask
= LVIS_STATEIMAGEMASK
;
8050 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvitem
);
8054 if (LISTVIEW_TrackMouse(infoPtr
, lvHitTestInfo
.pt
))
8058 ZeroMemory(&nmlv
, sizeof(nmlv
));
8060 nmlv
.ptAction
.x
= lvHitTestInfo
.pt
.x
;
8061 nmlv
.ptAction
.y
= lvHitTestInfo
.pt
.y
;
8063 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
8068 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
8070 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8071 infoPtr
->nEditLabelItem
= nItem
;
8073 LISTVIEW_SetSelection(infoPtr
, nItem
);
8077 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
8081 LISTVIEW_AddGroupSelection(infoPtr
, nItem
);
8082 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8083 infoPtr
->nSelectionMark
= nItem
;
8089 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
8090 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8092 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
8093 infoPtr
->nSelectionMark
= nItem
;
8096 else if (wKey
& MK_CONTROL
)
8100 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
8102 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
8103 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8104 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
8105 infoPtr
->nSelectionMark
= nItem
;
8107 else if (wKey
& MK_SHIFT
)
8109 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
8113 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8114 infoPtr
->nEditLabelItem
= nItem
;
8116 /* set selection (clears other pre-existing selections) */
8117 LISTVIEW_SetSelection(infoPtr
, nItem
);
8123 /* remove all selections */
8124 LISTVIEW_DeselectAll(infoPtr
);
8133 * Processes mouse up messages (left mouse button).
8136 * [I] infoPtr : valid pointer to the listview structure
8137 * [I] wKey : key flag
8138 * [I] x,y : mouse coordinate
8143 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8145 LVHITTESTINFO lvHitTestInfo
;
8147 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8149 if (!infoPtr
->bLButtonDown
) return 0;
8151 lvHitTestInfo
.pt
.x
= x
;
8152 lvHitTestInfo
.pt
.y
= y
;
8154 /* send NM_CLICK notification */
8155 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8156 notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
);
8158 /* set left button flag */
8159 infoPtr
->bLButtonDown
= FALSE
;
8161 /* if we clicked on a selected item, edit the label */
8162 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
8163 LISTVIEW_EditLabelT(infoPtr
, lvHitTestInfo
.iItem
, TRUE
);
8170 * Destroys the listview control (called after WM_DESTROY).
8173 * [I] infoPtr : valid pointer to the listview structure
8178 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
8182 /* delete all items */
8183 LISTVIEW_DeleteAllItems(infoPtr
);
8185 /* destroy data structure */
8186 DPA_Destroy(infoPtr
->hdpaItems
);
8187 DPA_Destroy(infoPtr
->hdpaPosX
);
8188 DPA_Destroy(infoPtr
->hdpaPosY
);
8189 DPA_Destroy(infoPtr
->hdpaColumns
);
8190 ranges_destroy(infoPtr
->selectionRanges
);
8192 /* destroy image lists */
8193 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
8195 if (infoPtr
->himlNormal
)
8196 ImageList_Destroy(infoPtr
->himlNormal
);
8197 if (infoPtr
->himlSmall
)
8198 ImageList_Destroy(infoPtr
->himlSmall
);
8199 if (infoPtr
->himlState
)
8200 ImageList_Destroy(infoPtr
->himlState
);
8203 /* destroy font, bkgnd brush */
8205 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
8206 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
8208 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
8210 /* free listview info pointer*/
8218 * Handles notifications from header.
8221 * [I] infoPtr : valid pointer to the listview structure
8222 * [I] nCtrlId : control identifier
8223 * [I] lpnmh : notification information
8228 static LRESULT
LISTVIEW_HeaderNotification(LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
8230 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8232 TRACE("(lpnmh=%p)\n", lpnmh
);
8234 if (!lpnmh
|| lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
8236 switch (lpnmh
->hdr
.code
)
8240 case HDN_ITEMCHANGEDW
:
8241 case HDN_ITEMCHANGEDA
:
8243 COLUMN_INFO
*lpColumnInfo
;
8246 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8250 hdi
.mask
= HDI_WIDTH
;
8251 if (!Header_GetItemW(infoPtr
->hwndHeader
, lpnmh
->iItem
, (LPARAM
)&hdi
)) return 0;
8255 cxy
= lpnmh
->pitem
->cxy
;
8257 /* determine how much we change since the last know position */
8258 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8259 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
8262 RECT rcCol
= lpColumnInfo
->rcHeader
;
8264 lpColumnInfo
->rcHeader
.right
+= dx
;
8265 LISTVIEW_ScrollColumns(infoPtr
, lpnmh
->iItem
+ 1, dx
);
8266 LISTVIEW_UpdateItemSize(infoPtr
);
8267 if (uView
== LVS_REPORT
&& is_redrawing(infoPtr
))
8269 /* this trick works for left aligned columns only */
8270 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
8272 rcCol
.right
= min (rcCol
.right
, lpColumnInfo
->rcHeader
.right
);
8273 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- 3 * infoPtr
->ntmAveCharWidth
);
8275 rcCol
.top
= infoPtr
->rcList
.top
;
8276 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
8277 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
8283 case HDN_ITEMCLICKW
:
8284 case HDN_ITEMCLICKA
:
8286 /* Handle sorting by Header Column */
8289 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
8291 nmlv
.iSubItem
= lpnmh
->iItem
;
8292 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
8302 * Determines the type of structure to use.
8305 * [I] infoPtr : valid pointer to the listview structureof the sender
8306 * [I] hwndFrom : listview window handle
8307 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8312 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
8314 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
8316 if (nCommand
!= NF_REQUERY
) return 0;
8318 infoPtr
->notifyFormat
= SendMessageW(hwndFrom
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
8325 * Paints/Repaints the listview control.
8328 * [I] infoPtr : valid pointer to the listview structure
8329 * [I] hdc : device context handle
8334 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
8336 TRACE("(hdc=%p)\n", hdc
);
8338 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
8340 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8342 infoPtr
->bNoItemMetrics
= FALSE
;
8343 LISTVIEW_UpdateItemSize(infoPtr
);
8344 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
8345 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8346 LISTVIEW_UpdateScroll(infoPtr
);
8349 LISTVIEW_Refresh(infoPtr
, hdc
);
8354 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
8356 if (ps
.fErase
) LISTVIEW_FillBkgnd(infoPtr
, hdc
, &ps
.rcPaint
);
8357 LISTVIEW_Refresh(infoPtr
, hdc
);
8358 EndPaint(infoPtr
->hwndSelf
, &ps
);
8367 * Paints/Repaints the listview control.
8370 * [I] infoPtr : valid pointer to the listview structure
8371 * [I] hdc : device context handle
8372 * [I] options : drawing options
8377 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
8379 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc
, options
);
8381 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
8384 if (options
& PRF_ERASEBKGND
)
8385 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
8387 if (options
& PRF_CLIENT
)
8388 LISTVIEW_Paint(infoPtr
, hdc
);
8396 * Processes double click messages (right mouse button).
8399 * [I] infoPtr : valid pointer to the listview structure
8400 * [I] wKey : key flag
8401 * [I] x,y : mouse coordinate
8406 static LRESULT
LISTVIEW_RButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8408 LVHITTESTINFO lvHitTestInfo
;
8410 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
8412 /* send NM_RELEASEDCAPTURE notification */
8413 notify(infoPtr
, NM_RELEASEDCAPTURE
);
8415 /* send NM_RDBLCLK notification */
8416 lvHitTestInfo
.pt
.x
= x
;
8417 lvHitTestInfo
.pt
.y
= y
;
8418 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8419 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
8426 * Processes mouse down messages (right mouse button).
8429 * [I] infoPtr : valid pointer to the listview structure
8430 * [I] wKey : key flag
8431 * [I] x,y : mouse coordinate
8436 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8438 LVHITTESTINFO lvHitTestInfo
;
8441 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
8443 /* send NM_RELEASEDCAPTURE notification */
8444 notify(infoPtr
, NM_RELEASEDCAPTURE
);
8446 /* make sure the listview control window has the focus */
8447 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
8449 /* set right button down flag */
8450 infoPtr
->bRButtonDown
= TRUE
;
8452 /* determine the index of the selected item */
8453 lvHitTestInfo
.pt
.x
= x
;
8454 lvHitTestInfo
.pt
.y
= y
;
8455 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8457 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8459 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8460 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
8461 !LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8462 LISTVIEW_SetSelection(infoPtr
, nItem
);
8466 LISTVIEW_DeselectAll(infoPtr
);
8474 * Processes mouse up messages (right mouse button).
8477 * [I] infoPtr : valid pointer to the listview structure
8478 * [I] wKey : key flag
8479 * [I] x,y : mouse coordinate
8484 static LRESULT
LISTVIEW_RButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8486 LVHITTESTINFO lvHitTestInfo
;
8489 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
8491 if (!infoPtr
->bRButtonDown
) return 0;
8493 /* set button flag */
8494 infoPtr
->bRButtonDown
= FALSE
;
8496 /* Send NM_RClICK notification */
8497 lvHitTestInfo
.pt
.x
= x
;
8498 lvHitTestInfo
.pt
.y
= y
;
8499 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8500 notify_click(infoPtr
, NM_RCLICK
, &lvHitTestInfo
);
8502 /* Change to screen coordinate for WM_CONTEXTMENU */
8503 pt
= lvHitTestInfo
.pt
;
8504 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
8506 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8507 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
8508 (WPARAM
)infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
8519 * [I] infoPtr : valid pointer to the listview structure
8520 * [I] hwnd : window handle of window containing the cursor
8521 * [I] nHittest : hit-test code
8522 * [I] wMouseMsg : ideintifier of the mouse message
8525 * TRUE if cursor is set
8528 static BOOL
LISTVIEW_SetCursor(LISTVIEW_INFO
*infoPtr
, HWND hwnd
, UINT nHittest
, UINT wMouseMsg
)
8530 LVHITTESTINFO lvHitTestInfo
;
8532 if(!(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)) return FALSE
;
8534 if(!infoPtr
->hHotCursor
) return FALSE
;
8536 GetCursorPos(&lvHitTestInfo
.pt
);
8537 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) return FALSE
;
8539 SetCursor(infoPtr
->hHotCursor
);
8549 * [I] infoPtr : valid pointer to the listview structure
8550 * [I] hwndLoseFocus : handle of previously focused window
8555 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
8557 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
8559 /* if we have the focus already, there's nothing to do */
8560 if (infoPtr
->bFocus
) return 0;
8562 /* send NM_SETFOCUS notification */
8563 notify(infoPtr
, NM_SETFOCUS
);
8565 /* set window focus flag */
8566 infoPtr
->bFocus
= TRUE
;
8568 /* put the focus rect back on */
8569 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
8571 /* redraw all visible selected items */
8572 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8582 * [I] infoPtr : valid pointer to the listview structure
8583 * [I] fRedraw : font handle
8584 * [I] fRedraw : redraw flag
8589 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
8591 HFONT oldFont
= infoPtr
->hFont
;
8593 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
8595 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
8596 if (infoPtr
->hFont
== oldFont
) return 0;
8598 LISTVIEW_SaveTextMetrics(infoPtr
);
8600 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
)
8601 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
8603 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
8610 * Message handling for WM_SETREDRAW.
8611 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8614 * [I] infoPtr : valid pointer to the listview structure
8615 * [I] bRedraw: state of redraw flag
8618 * DefWinProc return value
8620 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
8622 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr
->bRedraw
, bRedraw
);
8624 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8625 if ((infoPtr
->bRedraw
&& bRedraw
) || (!infoPtr
->bRedraw
&& !bRedraw
)) return 0;
8627 infoPtr
->bRedraw
= bRedraw
;
8629 if(!bRedraw
) return 0;
8631 if (is_autoarrange(infoPtr
))
8632 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8633 LISTVIEW_UpdateScroll(infoPtr
);
8635 /* despite what the WM_SETREDRAW docs says, apps expect us
8636 * to invalidate the listview here... stupid! */
8637 LISTVIEW_InvalidateList(infoPtr
);
8644 * Resizes the listview control. This function processes WM_SIZE
8645 * messages. At this time, the width and height are not used.
8648 * [I] infoPtr : valid pointer to the listview structure
8649 * [I] Width : new width
8650 * [I] Height : new height
8655 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
8657 RECT rcOld
= infoPtr
->rcList
;
8659 TRACE("(width=%d, height=%d)\n", Width
, Height
);
8661 LISTVIEW_UpdateSize(infoPtr
);
8662 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
8664 /* do not bother with display related stuff if we're not redrawing */
8665 if (!is_redrawing(infoPtr
)) return 0;
8667 if (is_autoarrange(infoPtr
))
8668 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8670 LISTVIEW_UpdateScroll(infoPtr
);
8672 /* refresh all only for lists whose height changed significantly */
8673 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
&&
8674 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
8675 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
8676 LISTVIEW_InvalidateList(infoPtr
);
8683 * Sets the size information.
8686 * [I] infoPtr : valid pointer to the listview structure
8691 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
8693 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8695 TRACE("uView=%d, rcList(old)=%s\n", uView
, debugrect(&infoPtr
->rcList
));
8697 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
8699 if (uView
== LVS_LIST
)
8701 /* Apparently the "LIST" style is supposed to have the same
8702 * number of items in a column even if there is no scroll bar.
8703 * Since if a scroll bar already exists then the bottom is already
8704 * reduced, only reduce if the scroll bar does not currently exist.
8705 * The "2" is there to mimic the native control. I think it may be
8706 * related to either padding or edges. (GLA 7/2002)
8708 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_HSCROLL
))
8709 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
8710 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
8712 else if (uView
== LVS_REPORT
&& !(infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
))
8717 hl
.prc
= &infoPtr
->rcList
;
8719 Header_Layout(infoPtr
->hwndHeader
, &hl
);
8721 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
8723 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
8726 TRACE(" rcList=%s\n", debugrect(&infoPtr
->rcList
));
8731 * Processes WM_STYLECHANGED messages.
8734 * [I] infoPtr : valid pointer to the listview structure
8735 * [I] wStyleType : window style type (normal or extended)
8736 * [I] lpss : window style information
8741 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
8742 const STYLESTRUCT
*lpss
)
8744 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
8745 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
8747 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8748 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
8750 if (wStyleType
!= GWL_STYLE
) return 0;
8752 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8753 /* what if LVS_OWNERDATA changed? */
8754 /* or LVS_SINGLESEL */
8755 /* or LVS_SORT{AS,DES}CENDING */
8757 infoPtr
->dwStyle
= lpss
->styleNew
;
8759 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
8760 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
8761 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
8763 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
8764 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
8765 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
8767 if (uNewView
!= uOldView
)
8769 SIZE oldIconSize
= infoPtr
->iconSize
;
8772 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8773 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
8775 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
8776 SetRectEmpty(&infoPtr
->rcFocus
);
8778 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
8779 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
8781 if (uNewView
== LVS_ICON
)
8783 if ((infoPtr
->iconSize
.cx
!= oldIconSize
.cx
) || (infoPtr
->iconSize
.cy
!= oldIconSize
.cy
))
8785 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8786 oldIconSize
.cx
, oldIconSize
.cy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
8787 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
8790 else if (uNewView
== LVS_REPORT
)
8795 hl
.prc
= &infoPtr
->rcList
;
8797 Header_Layout(infoPtr
->hwndHeader
, &hl
);
8798 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
8801 LISTVIEW_UpdateItemSize(infoPtr
);
8804 if (uNewView
== LVS_REPORT
)
8805 ShowWindow(infoPtr
->hwndHeader
, (lpss
->styleNew
& LVS_NOCOLUMNHEADER
) ? SW_HIDE
: SW_SHOWNORMAL
);
8807 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
8808 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
8809 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8811 /* update the size of the client area */
8812 LISTVIEW_UpdateSize(infoPtr
);
8814 /* add scrollbars if needed */
8815 LISTVIEW_UpdateScroll(infoPtr
);
8817 /* invalidate client area + erase background */
8818 LISTVIEW_InvalidateList(infoPtr
);
8825 * Window procedure of the listview control.
8828 static LRESULT WINAPI
8829 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
8831 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
8833 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg
, wParam
, lParam
);
8835 if (!infoPtr
&& (uMsg
!= WM_CREATE
))
8836 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
8840 case LVM_APPROXIMATEVIEWRECT
:
8841 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
8842 LOWORD(lParam
), HIWORD(lParam
));
8844 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
8846 /* case LVM_CANCELEDITLABEL: */
8848 case LVM_CREATEDRAGIMAGE
:
8849 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
8851 case LVM_DELETEALLITEMS
:
8852 return LISTVIEW_DeleteAllItems(infoPtr
);
8854 case LVM_DELETECOLUMN
:
8855 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
8857 case LVM_DELETEITEM
:
8858 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
8860 case LVM_EDITLABELW
:
8861 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, TRUE
);
8863 case LVM_EDITLABELA
:
8864 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, FALSE
);
8866 /* case LVM_ENABLEGROUPVIEW: */
8868 case LVM_ENSUREVISIBLE
:
8869 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
8872 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
8875 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
8877 case LVM_GETBKCOLOR
:
8878 return infoPtr
->clrBk
;
8880 /* case LVM_GETBKIMAGE: */
8882 case LVM_GETCALLBACKMASK
:
8883 return infoPtr
->uCallbackMask
;
8885 case LVM_GETCOLUMNA
:
8886 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
8888 case LVM_GETCOLUMNW
:
8889 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
8891 case LVM_GETCOLUMNORDERARRAY
:
8892 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
8894 case LVM_GETCOLUMNWIDTH
:
8895 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
8897 case LVM_GETCOUNTPERPAGE
:
8898 return LISTVIEW_GetCountPerPage(infoPtr
);
8900 case LVM_GETEDITCONTROL
:
8901 return (LRESULT
)infoPtr
->hwndEdit
;
8903 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
8904 return infoPtr
->dwLvExStyle
;
8906 /* case LVM_GETGROUPINFO: */
8908 /* case LVM_GETGROUPMETRICS: */
8911 return (LRESULT
)infoPtr
->hwndHeader
;
8913 case LVM_GETHOTCURSOR
:
8914 return (LRESULT
)infoPtr
->hHotCursor
;
8916 case LVM_GETHOTITEM
:
8917 return infoPtr
->nHotItem
;
8919 case LVM_GETHOVERTIME
:
8920 return infoPtr
->dwHoverTime
;
8922 case LVM_GETIMAGELIST
:
8923 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
8925 /* case LVM_GETINSERTMARK: */
8927 /* case LVM_GETINSERTMARKCOLOR: */
8929 /* case LVM_GETINSERTMARKRECT: */
8931 case LVM_GETISEARCHSTRINGA
:
8932 case LVM_GETISEARCHSTRINGW
:
8933 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8937 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
8940 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
8942 case LVM_GETITEMCOUNT
:
8943 return infoPtr
->nItemCount
;
8945 case LVM_GETITEMPOSITION
:
8946 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
8948 case LVM_GETITEMRECT
:
8949 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
8951 case LVM_GETITEMSPACING
:
8952 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
8954 case LVM_GETITEMSTATE
:
8955 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
8957 case LVM_GETITEMTEXTA
:
8958 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
8960 case LVM_GETITEMTEXTW
:
8961 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
8963 case LVM_GETNEXTITEM
:
8964 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
8966 case LVM_GETNUMBEROFWORKAREAS
:
8967 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8971 if (!lParam
) return FALSE
;
8972 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
8975 /* case LVM_GETOUTLINECOLOR: */
8977 /* case LVM_GETSELECTEDCOLUMN: */
8979 case LVM_GETSELECTEDCOUNT
:
8980 return LISTVIEW_GetSelectedCount(infoPtr
);
8982 case LVM_GETSELECTIONMARK
:
8983 return infoPtr
->nSelectionMark
;
8985 case LVM_GETSTRINGWIDTHA
:
8986 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, FALSE
);
8988 case LVM_GETSTRINGWIDTHW
:
8989 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, TRUE
);
8991 case LVM_GETSUBITEMRECT
:
8992 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
8994 case LVM_GETTEXTBKCOLOR
:
8995 return infoPtr
->clrTextBk
;
8997 case LVM_GETTEXTCOLOR
:
8998 return infoPtr
->clrText
;
9000 /* case LVM_GETTILEINFO: */
9002 /* case LVM_GETTILEVIEWINFO: */
9004 case LVM_GETTOOLTIPS
:
9005 if( !infoPtr
->hwndToolTip
)
9006 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
9007 return (LRESULT
)infoPtr
->hwndToolTip
;
9009 case LVM_GETTOPINDEX
:
9010 return LISTVIEW_GetTopIndex(infoPtr
);
9012 /*case LVM_GETUNICODEFORMAT:
9013 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9016 /* case LVM_GETVIEW: */
9018 case LVM_GETVIEWRECT
:
9019 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
9021 case LVM_GETWORKAREAS
:
9022 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9025 /* case LVM_HASGROUP: */
9028 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, FALSE
);
9030 case LVM_INSERTCOLUMNA
:
9031 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9033 case LVM_INSERTCOLUMNW
:
9034 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9036 /* case LVM_INSERTGROUP: */
9038 /* case LVM_INSERTGROUPSORTED: */
9040 case LVM_INSERTITEMA
:
9041 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9043 case LVM_INSERTITEMW
:
9044 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9046 /* case LVM_INSERTMARKHITTEST: */
9048 /* case LVM_ISGROUPVIEWENABLED: */
9050 /* case LVM_MAPIDTOINDEX: */
9052 /* case LVM_MAPINDEXTOID: */
9054 /* case LVM_MOVEGROUP: */
9056 /* case LVM_MOVEITEMTOGROUP: */
9058 case LVM_REDRAWITEMS
:
9059 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9061 /* case LVM_REMOVEALLGROUPS: */
9063 /* case LVM_REMOVEGROUP: */
9066 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9068 case LVM_SETBKCOLOR
:
9069 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
9071 /* case LVM_SETBKIMAGE: */
9073 case LVM_SETCALLBACKMASK
:
9074 infoPtr
->uCallbackMask
= (UINT
)wParam
;
9077 case LVM_SETCOLUMNA
:
9078 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9080 case LVM_SETCOLUMNW
:
9081 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9083 case LVM_SETCOLUMNORDERARRAY
:
9084 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9086 case LVM_SETCOLUMNWIDTH
:
9087 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
9089 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
9090 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
9092 /* case LVM_SETGROUPINFO: */
9094 /* case LVM_SETGROUPMETRICS: */
9096 case LVM_SETHOTCURSOR
:
9097 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
9099 case LVM_SETHOTITEM
:
9100 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
9102 case LVM_SETHOVERTIME
:
9103 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)wParam
);
9105 case LVM_SETICONSPACING
:
9106 return LISTVIEW_SetIconSpacing(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
9108 case LVM_SETIMAGELIST
:
9109 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
9111 /* case LVM_SETINFOTIP: */
9113 /* case LVM_SETINSERTMARK: */
9115 /* case LVM_SETINSERTMARKCOLOR: */
9118 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9121 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9123 case LVM_SETITEMCOUNT
:
9124 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
9126 case LVM_SETITEMPOSITION
:
9129 pt
.x
= (short)LOWORD(lParam
);
9130 pt
.y
= (short)HIWORD(lParam
);
9131 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, pt
);
9134 case LVM_SETITEMPOSITION32
:
9135 if (lParam
== 0) return FALSE
;
9136 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, *((POINT
*)lParam
));
9138 case LVM_SETITEMSTATE
:
9139 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
9141 case LVM_SETITEMTEXTA
:
9142 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9144 case LVM_SETITEMTEXTW
:
9145 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9147 /* case LVM_SETOUTLINECOLOR: */
9149 /* case LVM_SETSELECTEDCOLUMN: */
9151 case LVM_SETSELECTIONMARK
:
9152 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
9154 case LVM_SETTEXTBKCOLOR
:
9155 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
9157 case LVM_SETTEXTCOLOR
:
9158 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
9160 /* case LVM_SETTILEINFO: */
9162 /* case LVM_SETTILEVIEWINFO: */
9164 /* case LVM_SETTILEWIDTH: */
9166 case LVM_SETTOOLTIPS
:
9167 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
9169 /* case LVM_SETUNICODEFORMAT: */
9171 /* case LVM_SETVIEW: */
9173 /* case LVM_SETWORKAREAS: */
9175 /* case LVM_SORTGROUPS: */
9178 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
);
9180 /* LVM_SORTITEMSEX: */
9182 case LVM_SUBITEMHITTEST
:
9183 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
9186 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
9189 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
9192 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
9195 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
9198 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
9201 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
9204 return (LRESULT
)infoPtr
->hFont
;
9207 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
9210 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
9213 return LISTVIEW_KillFocus(infoPtr
);
9215 case WM_LBUTTONDBLCLK
:
9216 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9218 case WM_LBUTTONDOWN
:
9219 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9222 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9225 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9228 return LISTVIEW_MouseHover(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9231 return LISTVIEW_NCDestroy(infoPtr
);
9234 if (lParam
&& ((LPNMHDR
)lParam
)->hwndFrom
== infoPtr
->hwndHeader
)
9235 return LISTVIEW_HeaderNotification(infoPtr
, (LPNMHEADERW
)lParam
);
9238 case WM_NOTIFYFORMAT
:
9239 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
9241 case WM_PRINTCLIENT
:
9242 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
9245 return LISTVIEW_Paint(infoPtr
, (HDC
)wParam
);
9247 case WM_RBUTTONDBLCLK
:
9248 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9250 case WM_RBUTTONDOWN
:
9251 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9254 return LISTVIEW_RButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9257 if(LISTVIEW_SetCursor(infoPtr
, (HWND
)wParam
, LOWORD(lParam
), HIWORD(lParam
)))
9262 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
9265 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
9268 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
9271 return LISTVIEW_Size(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
9273 case WM_STYLECHANGED
:
9274 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
9276 case WM_SYSCOLORCHANGE
:
9277 COMCTL32_RefreshSysColors();
9280 /* case WM_TIMER: */
9283 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
9286 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
9287 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9288 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
9290 case WM_WINDOWPOSCHANGED
:
9291 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
9293 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
9294 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
9295 LISTVIEW_UpdateSize(infoPtr
);
9296 LISTVIEW_UpdateScroll(infoPtr
);
9298 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9300 /* case WM_WININICHANGE: */
9303 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
))
9304 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg
, wParam
, lParam
);
9307 /* call default window procedure */
9308 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9315 * Registers the window class.
9323 void LISTVIEW_Register(void)
9327 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
9328 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
9329 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
9330 wndClass
.cbClsExtra
= 0;
9331 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
9332 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
9333 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
9334 wndClass
.lpszClassName
= WC_LISTVIEWW
;
9335 RegisterClassW(&wndClass
);
9340 * Unregisters the window class.
9348 void LISTVIEW_Unregister(void)
9350 UnregisterClassW(WC_LISTVIEWW
, NULL
);
9355 * Handle any WM_COMMAND messages
9358 * [I] infoPtr : valid pointer to the listview structure
9359 * [I] wParam : the first message parameter
9360 * [I] lParam : the second message parameter
9365 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
9367 switch (HIWORD(wParam
))
9372 * Adjust the edit window size
9375 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
9376 HFONT hFont
, hOldFont
= 0;
9381 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
9382 len
= GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
9383 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
9385 /* Select font to get the right dimension of the string */
9386 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
9389 hOldFont
= SelectObject(hdc
, hFont
);
9392 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
9394 TEXTMETRICW textMetric
;
9396 /* Add Extra spacing for the next character */
9397 GetTextMetricsW(hdc
, &textMetric
);
9398 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
9406 rect
.bottom
- rect
.top
,
9407 SWP_DRAWFRAME
|SWP_NOMOVE
);
9410 SelectObject(hdc
, hOldFont
);
9412 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
9418 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);
9427 * Subclassed edit control windproc function
9430 * [I] hwnd : the edit window handle
9431 * [I] uMsg : the message that is to be processed
9432 * [I] wParam : first message parameter
9433 * [I] lParam : second message parameter
9434 * [I] isW : TRUE if input is Unicode
9439 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
9441 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
9442 BOOL cancel
= FALSE
;
9444 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9445 hwnd
, uMsg
, wParam
, lParam
, isW
);
9450 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
9457 WNDPROC editProc
= infoPtr
->EditWndProc
;
9458 infoPtr
->EditWndProc
= 0;
9459 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
9460 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
9464 if (VK_ESCAPE
== (INT
)wParam
)
9469 else if (VK_RETURN
== (INT
)wParam
)
9473 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
9477 if (infoPtr
->hwndEdit
)
9479 LPWSTR buffer
= NULL
;
9481 infoPtr
->hwndEdit
= 0;
9484 DWORD len
= isW
? GetWindowTextLengthW(hwnd
) : GetWindowTextLengthA(hwnd
);
9488 if ( (buffer
= Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))) )
9490 if (isW
) GetWindowTextW(hwnd
, buffer
, len
+1);
9491 else GetWindowTextA(hwnd
, (CHAR
*)buffer
, len
+1);
9495 LISTVIEW_EndEditLabelT(infoPtr
, buffer
, isW
);
9497 if (buffer
) Free(buffer
);
9501 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
9507 * Subclassed edit control Unicode windproc function
9510 * [I] hwnd : the edit window handle
9511 * [I] uMsg : the message that is to be processed
9512 * [I] wParam : first message parameter
9513 * [I] lParam : second message parameter
9517 LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9519 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
9524 * Subclassed edit control ANSI windproc function
9527 * [I] hwnd : the edit window handle
9528 * [I] uMsg : the message that is to be processed
9529 * [I] wParam : first message parameter
9530 * [I] lParam : second message parameter
9534 LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9536 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
9541 * Creates a subclassed edit cotrol
9544 * [I] infoPtr : valid pointer to the listview structure
9545 * [I] text : initial text for the edit
9546 * [I] style : the window style
9547 * [I] isW : TRUE if input is Unicode
9551 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, DWORD style
,
9552 INT x
, INT y
, INT width
, INT height
, BOOL isW
)
9554 WCHAR editName
[5] = { 'E', 'd', 'i', 't', '\0' };
9559 TEXTMETRICW textMetric
;
9560 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
9562 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text
, isW
), isW
);
9564 style
|= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
;
9565 hdc
= GetDC(infoPtr
->hwndSelf
);
9567 /* Select the font to get appropriate metric dimensions */
9568 if(infoPtr
->hFont
!= 0)
9569 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
9571 /*Get String Length in pixels */
9572 if(!GetTextExtentPoint32W(hdc
, text
, lstrlenW(text
), &sz
))
9575 /*Add Extra spacing for the next character */
9576 GetTextMetricsW(hdc
, &textMetric
);
9577 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
9579 if(infoPtr
->hFont
!= 0)
9580 SelectObject(hdc
, hOldFont
);
9582 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
9584 hedit
= CreateWindowW(editName
, text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
9586 hedit
= CreateWindowA("Edit", (LPCSTR
)text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
9588 if (!hedit
) return 0;
9590 infoPtr
->EditWndProc
= (WNDPROC
)
9591 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
9592 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
9594 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);