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
25 * Listview control implementation.
28 * -- Drawing optimizations.
29 * -- Hot item handling.
32 * LISTVIEW_Notify : most notifications from children (editbox and header)
35 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
37 * Advanced functionality:
38 * LISTVIEW_GetNumberOfWorkAreas : not implemented
39 * LISTVIEW_GetISearchString : not implemented
40 * LISTVIEW_GetBkImage : not implemented
41 * LISTVIEW_SetBkImage : not implemented
42 * LISTVIEW_GetColumnOrderArray : simple hack only
43 * LISTVIEW_SetColumnOrderArray : simple hack only
44 * LISTVIEW_Arrange : empty stub
45 * LISTVIEW_ApproximateViewRect : incomplete
46 * LISTVIEW_Update : not completed
48 * Known differences in message stream from native control (not known if
49 * these differences cause problems):
50 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
51 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
52 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
53 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
54 * does *not* invoke DefWindowProc
55 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
56 * processing for "USEDOUBLECLICKTIME".
61 #include "wine/port.h"
73 #include "wine/debug.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
77 /* Some definitions for inline edit control */
79 typedef struct tagCOLUMNCACHE
83 } COLUMNCACHE
, *LPCOLUMNCACHE
;
85 typedef struct tagITEMHDR
89 } ITEMHDR
, *LPITEMHDR
;
91 typedef struct tagLISTVIEW_SUBITEM
97 typedef struct tagLISTVIEW_ITEM
106 typedef struct tagRANGE
112 typedef struct tagLISTVIEW_INFO
119 HIMAGELIST himlNormal
;
120 HIMAGELIST himlSmall
;
121 HIMAGELIST himlState
;
126 HDPA hdpaSelectionRanges
;
128 BOOL bRemovingAllSelections
;
131 RECT rcList
; /* This rectangle is really the window
132 * client rectangle possibly reduced by the
133 * horizontal scroll bar and/or header - see
134 * LISTVIEW_UpdateSize. This rectangle offset
135 * by the LISTVIEW_GetOrigin value is in
136 * client coordinates */
137 RECT rcView
; /* This rectangle contains all items -
138 * contructed in LISTVIEW_AlignTop and
139 * LISTVIEW_AlignLeft */
148 INT ntmHeight
; /* from GetTextMetrics from above font */
149 INT ntmAveCharWidth
; /* from GetTextMetrics from above font */
153 DWORD dwStyle
; /* the cached window GWL_STYLE */
154 DWORD dwLvExStyle
; /* extended listview style */
157 HDPA hdpaPosX
; /* maintains the (X, Y) coordinates of the */
158 HDPA hdpaPosY
; /* items in LVS_ICON, and LVS_SMALLICON modes */
159 PFNLVCOMPARE pfnCompare
;
167 DWORD lastKeyPressTimestamp
;
169 INT nSearchParamLength
;
170 WCHAR szSearchParam
[ MAX_PATH
];
174 DEFINE_COMMON_NOTIFICATIONS(LISTVIEW_INFO
, hwndSelf
);
179 /* How many we debug buffer to allocate */
180 #define DEBUG_BUFFERS 20
181 /* The size of a single debug bbuffer */
182 #define DEBUG_BUFFER_SIZE 256
184 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
185 #define SB_INTERNAL -1
187 /* maximum size of a label */
188 #define DISP_TEXT_SIZE 512
190 /* padding for items in list and small icon display modes */
191 #define WIDTH_PADDING 12
193 /* padding for items in list, report and small icon display modes */
194 #define HEIGHT_PADDING 1
196 /* offset of items in report display mode */
197 #define REPORT_MARGINX 2
199 /* padding for icon in large icon display mode
200 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
201 * that HITTEST will see.
202 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
203 * ICON_TOP_PADDING - sum of the two above.
204 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
205 * LABEL_VERT_PADDING - between bottom of text and end of box
207 * ICON_LR_PADDING - additional width above icon size.
208 * ICON_LR_HALF - half of the above value
210 #define ICON_TOP_PADDING_NOTHITABLE 2
211 #define ICON_TOP_PADDING_HITABLE 2
212 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
213 #define ICON_BOTTOM_PADDING 4
214 #define LABEL_VERT_PADDING 7
215 #define ICON_LR_PADDING 16
216 #define ICON_LR_HALF (ICON_LR_PADDING/2)
218 /* default label width for items in list and small icon display modes */
219 #define DEFAULT_LABEL_WIDTH 40
221 /* default column width for items in list display mode */
222 #define DEFAULT_COLUMN_WIDTH 128
224 /* Size of "line" scroll for V & H scrolls */
225 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
227 /* Padding betwen image and label */
228 #define IMAGE_PADDING 2
230 /* Padding behind the label */
231 #define TRAILING_PADDING 5
233 /* Border for the icon caption */
234 #define CAPTION_BORDER 2
236 /* Standard DrawText flags for LISTVIEW_UpdateLargeItemLabelRect and LISTVIEW_DrawLargeItem */
237 #define LISTVIEW_DTFLAGS DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL
239 /* Dump the LISTVIEW_INFO structure to the debug channel */
240 #define LISTVIEW_DUMP(iP) do { \
241 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
242 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
243 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
244 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
245 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
246 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
247 (iP->bFocus) ? "true" : "false"); \
248 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
249 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
250 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
251 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
253 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
254 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
259 * forward declarations
261 static BOOL
LISTVIEW_GetItemT(LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
262 static INT
LISTVIEW_SuperHitTestItem(LISTVIEW_INFO
*, LPLVHITTESTINFO
, BOOL
, BOOL
);
263 static void LISTVIEW_AlignLeft(LISTVIEW_INFO
*);
264 static void LISTVIEW_AlignTop(LISTVIEW_INFO
*);
265 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*, INT
);
266 static INT
LISTVIEW_GetItemHeight(LISTVIEW_INFO
*);
267 static BOOL
LISTVIEW_GetItemPosition(LISTVIEW_INFO
*, INT
, LPPOINT
);
268 static BOOL
LISTVIEW_GetItemRect(LISTVIEW_INFO
*, INT
, LPRECT
);
269 static INT
LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO
*);
270 static BOOL
LISTVIEW_GetSubItemRect(LISTVIEW_INFO
*, INT
, LPRECT
);
271 static INT
LISTVIEW_GetLabelWidth(LISTVIEW_INFO
*, INT
);
272 static LRESULT
LISTVIEW_GetColumnWidth(LISTVIEW_INFO
*, INT
);
273 static BOOL
LISTVIEW_GetOrigin(LISTVIEW_INFO
*, LPPOINT
);
274 static BOOL
LISTVIEW_GetViewRect(LISTVIEW_INFO
*, LPRECT
);
275 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*, INT
);
276 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
277 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*, INT
, POINT
);
278 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO
*);
279 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*, INT
);
280 static BOOL
LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
281 static void LISTVIEW_UnsupportedStyles(LONG
);
282 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*, INT
, BOOL
);
283 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*, WPARAM
, LPARAM
);
284 static LRESULT
LISTVIEW_SortItems(LISTVIEW_INFO
*, PFNLVCOMPARE
, LPARAM
);
285 static LRESULT
LISTVIEW_GetStringWidthT(LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
286 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*, WPARAM
, LPARAM
);
287 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
);
288 static LRESULT
LISTVIEW_GetItemState(LISTVIEW_INFO
*, INT
, UINT
);
289 static LRESULT
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, LPLVITEMW
);
290 static BOOL
LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO
*, int, RECT
*);
291 static LRESULT
LISTVIEW_GetColumnT(LISTVIEW_INFO
*, INT
, LPLVCOLUMNW
, BOOL
);
292 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
293 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
294 static INT
LISTVIEW_GetTopIndex(LISTVIEW_INFO
*);
295 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
296 static HWND
CreateEditLabelT(LISTVIEW_INFO
*, LPCWSTR
, DWORD
, INT
, INT
, INT
, INT
, BOOL
);
298 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
299 #define KEY_DELAY 450
301 /******** Text handling functions *************************************/
303 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
304 * text string. The string may be ANSI or Unicode, in which case
305 * the boolean isW tells us the type of the string.
307 * The name of the function tell what type of strings it expects:
308 * W: Unicode, T: ANSI/Unicode - function of isW
311 static inline BOOL
is_textW(LPCWSTR text
)
313 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
316 static inline BOOL
is_textT(LPCWSTR text
, BOOL isW
)
318 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
319 return is_textW(text
);
322 static inline int textlenT(LPCWSTR text
, BOOL isW
)
324 return !is_textT(text
, isW
) ? 0 :
325 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
328 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
331 if (isSrcW
) lstrcpynW(dest
, src
, max
);
332 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
334 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
335 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
338 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
340 LPWSTR wstr
= (LPWSTR
)text
;
342 if (!isW
&& is_textT(text
, isW
))
344 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
345 wstr
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
346 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
348 TRACE(" wstr=%s\n", text
== LPSTR_TEXTCALLBACKW
? "(callback)" : debugstr_w(wstr
));
352 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
354 if (!isW
&& is_textT(wstr
, isW
)) HeapFree(GetProcessHeap(), 0, wstr
);
358 * dest is a pointer to a Unicode string
359 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
361 static BOOL
textsetptrT(LPWSTR
*dest
, LPWSTR src
, BOOL isW
)
365 if (src
== LPSTR_TEXTCALLBACKW
)
367 if (is_textW(*dest
)) COMCTL32_Free(*dest
);
368 *dest
= LPSTR_TEXTCALLBACKW
;
372 LPWSTR pszText
= textdupTtoW(src
, isW
);
373 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
374 bResult
= Str_SetPtrW(dest
, pszText
);
375 textfreeT(pszText
, isW
);
381 * compares a Unicode to a Unicode/ANSI text string
383 static inline int textcmpWT(LPWSTR aw
, LPWSTR bt
, BOOL isW
)
385 if (!aw
) return bt
? -1 : 0;
386 if (!bt
) return aw
? 1 : 0;
387 if (aw
== LPSTR_TEXTCALLBACKW
)
388 return bt
== LPSTR_TEXTCALLBACKW
? 0 : -1;
389 if (bt
!= LPSTR_TEXTCALLBACKW
)
391 LPWSTR bw
= textdupTtoW(bt
, isW
);
392 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
400 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
404 n
= min(min(n
, strlenW(s1
)), strlenW(s2
));
405 res
= CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
);
406 return res
? res
- sizeof(WCHAR
) : res
;
409 /******** Debugging functions *****************************************/
411 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
413 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
414 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
417 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
419 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
420 n
= min(textlenT(text
, isW
), n
);
421 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
424 static char* debug_getbuf()
426 static int index
= 0;
427 static char buffers
[DEBUG_BUFFERS
][DEBUG_BUFFER_SIZE
];
428 return buffers
[index
++ % DEBUG_BUFFERS
];
431 static inline char* debugpoint(const POINT
* lppt
)
435 char* buf
= debug_getbuf();
436 snprintf(buf
, DEBUG_BUFFER_SIZE
, "(%ld, %ld)", lppt
->x
, lppt
->y
);
438 } else return "(null)";
441 static inline char* debugrect(const RECT
* rect
)
445 char* buf
= debug_getbuf();
446 snprintf(buf
, DEBUG_BUFFER_SIZE
, "[(%d, %d);(%d, %d)]",
447 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
449 } else return "(null)";
452 static char* debuglvitem_t(LPLVITEMW lpLVItem
, BOOL isW
)
454 char* buf
= debug_getbuf(), *text
= buf
;
455 int len
, size
= DEBUG_BUFFER_SIZE
;
457 if (lpLVItem
== NULL
) return "(null)";
458 len
= snprintf(buf
, size
, "{iItem=%d, iSubItem=%d, ", lpLVItem
->iItem
, lpLVItem
->iSubItem
);
459 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
460 if (lpLVItem
->mask
& LVIF_STATE
)
461 len
= snprintf(buf
, size
, "state=%x, stateMask=%x, ", lpLVItem
->state
, lpLVItem
->stateMask
);
463 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
464 if (lpLVItem
->mask
& LVIF_TEXT
)
465 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem
->pszText
, isW
, 80), lpLVItem
->cchTextMax
);
467 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
468 if (lpLVItem
->mask
& LVIF_IMAGE
)
469 len
= snprintf(buf
, size
, "iImage=%d, ", lpLVItem
->iImage
);
471 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
472 if (lpLVItem
->mask
& LVIF_PARAM
)
473 len
= snprintf(buf
, size
, "lParam=%lx, ", lpLVItem
->lParam
);
475 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
476 if (lpLVItem
->mask
& LVIF_INDENT
)
477 len
= snprintf(buf
, size
, "iIndent=%d, ", lpLVItem
->iIndent
);
479 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
482 buf
= text
+ strlen(text
);
484 if (buf
- text
> 2) buf
[-2] = '}'; buf
[-1] = 0;
488 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn
, BOOL isW
)
490 char* buf
= debug_getbuf(), *text
= buf
;
491 int len
, size
= DEBUG_BUFFER_SIZE
;
493 if (lpColumn
== NULL
) return "(null)";
494 len
= snprintf(buf
, size
, "{");
495 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
496 if (lpColumn
->mask
& LVCF_SUBITEM
)
497 len
= snprintf(buf
, size
, "iSubItem=%d, ", lpColumn
->iSubItem
);
499 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
500 if (lpColumn
->mask
& LVCF_FMT
)
501 len
= snprintf(buf
, size
, "fmt=%x, ", lpColumn
->fmt
);
503 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
504 if (lpColumn
->mask
& LVCF_WIDTH
)
505 len
= snprintf(buf
, size
, "cx=%d, ", lpColumn
->cx
);
507 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
508 if (lpColumn
->mask
& LVCF_TEXT
)
509 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn
->pszText
, isW
, 80), lpColumn
->cchTextMax
);
511 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
512 if (lpColumn
->mask
& LVCF_IMAGE
)
513 len
= snprintf(buf
, size
, "iImage=%d, ", lpColumn
->iImage
);
515 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
516 if (lpColumn
->mask
& LVCF_ORDER
)
517 len
= snprintf(buf
, size
, "iOrder=%d, ", lpColumn
->iOrder
);
519 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
522 buf
= text
+ strlen(text
);
524 if (buf
- text
> 2) buf
[-2] = '}'; buf
[-1] = 0;
529 /******** Notification functions i************************************/
531 static inline BOOL
notify(LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
533 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
534 pnmh
->idFrom
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_ID
);
536 return (BOOL
)SendMessageW(GetParent(infoPtr
->hwndSelf
), WM_NOTIFY
,
537 (WPARAM
)pnmh
->idFrom
, (LPARAM
)pnmh
);
540 static inline void notify_itemactivate(LISTVIEW_INFO
*infoPtr
)
543 notify(infoPtr
, LVN_ITEMACTIVATE
, &nmh
);
546 static inline BOOL
notify_listview(LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
548 return notify(infoPtr
, code
, (LPNMHDR
)plvnm
);
551 static int get_ansi_notification(INT unicodeNotificationCode
)
553 switch (unicodeNotificationCode
)
555 case LVN_BEGINLABELEDITW
: return LVN_BEGINLABELEDITA
;
556 case LVN_ENDLABELEDITW
: return LVN_ENDLABELEDITA
;
557 case LVN_GETDISPINFOW
: return LVN_GETDISPINFOA
;
558 case LVN_SETDISPINFOW
: return LVN_SETDISPINFOA
;
559 case LVN_ODFINDITEMW
: return LVN_ODFINDITEMA
;
560 case LVN_GETINFOTIPW
: return LVN_GETINFOTIPA
;
562 ERR("unknown notification %x\n", unicodeNotificationCode
);
563 return unicodeNotificationCode
;
567 Send notification. depends on dispinfoW having same
568 structure as dispinfoA.
569 infoPtr : listview struct
570 notificationCode : *Unicode* notification code
571 pdi : dispinfo structure (can be unicode or ansi)
572 isW : TRUE if dispinfo is Unicode
574 static BOOL
notify_dispinfoT(LISTVIEW_INFO
*infoPtr
, INT notificationCode
, LPNMLVDISPINFOW pdi
, BOOL isW
)
576 BOOL bResult
= FALSE
;
577 BOOL convertToAnsi
= FALSE
, convertToUnicode
= FALSE
;
579 INT cchTempBufMax
= 0, savCchTextMax
= 0;
580 LPWSTR pszTempBuf
= NULL
, savPszText
= NULL
;
582 if ((pdi
->item
.mask
& LVIF_TEXT
) && is_textT(pdi
->item
.pszText
, isW
))
584 convertToAnsi
= (isW
&& infoPtr
->notifyFormat
== NFR_ANSI
);
585 convertToUnicode
= (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
);
588 if (convertToAnsi
|| convertToUnicode
)
590 if (notificationCode
!= LVN_GETDISPINFOW
)
592 cchTempBufMax
= convertToUnicode
?
593 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
594 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
598 cchTempBufMax
= pdi
->item
.cchTextMax
;
599 *pdi
->item
.pszText
= 0; /* make sure we don't process garbage */
602 pszTempBuf
= HeapAlloc(GetProcessHeap(), 0,
603 (convertToUnicode
? sizeof(WCHAR
) : sizeof(CHAR
)) * cchTempBufMax
);
604 if (!pszTempBuf
) return FALSE
;
605 if (convertToUnicode
)
606 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
607 pszTempBuf
, cchTempBufMax
);
609 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) pszTempBuf
,
610 cchTempBufMax
, NULL
, NULL
);
611 savCchTextMax
= pdi
->item
.cchTextMax
;
612 savPszText
= pdi
->item
.pszText
;
613 pdi
->item
.pszText
= pszTempBuf
;
614 pdi
->item
.cchTextMax
= cchTempBufMax
;
617 if (infoPtr
->notifyFormat
== NFR_ANSI
)
618 realNotifCode
= get_ansi_notification(notificationCode
);
620 realNotifCode
= notificationCode
;
621 bResult
= notify(infoPtr
, realNotifCode
, (LPNMHDR
)pdi
);
623 if (convertToUnicode
|| convertToAnsi
)
625 if (convertToUnicode
) /* note : pointer can be changed by app ! */
626 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) savPszText
,
627 savCchTextMax
, NULL
, NULL
);
629 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
630 savPszText
, savCchTextMax
);
631 pdi
->item
.pszText
= savPszText
; /* restores our buffer */
632 pdi
->item
.cchTextMax
= savCchTextMax
;
633 HeapFree(GetProcessHeap(), 0, pszTempBuf
);
638 static inline void notify_odcachehint(LISTVIEW_INFO
*infoPtr
, INT iFrom
, INT iTo
)
644 notify(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
647 static BOOL
notify_customdraw (LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, HDC hdc
, RECT rc
)
649 NMLVCUSTOMDRAW nmlvcd
;
651 TRACE("(dwDrawStage=%lx, hdc=%x, rc=?)\n", dwDrawStage
, hdc
);
653 nmlvcd
.nmcd
.dwDrawStage
= dwDrawStage
;
654 nmlvcd
.nmcd
.hdc
= hdc
;
656 nmlvcd
.nmcd
.dwItemSpec
= 0;
657 nmlvcd
.nmcd
.uItemState
= 0;
658 nmlvcd
.nmcd
.lItemlParam
= 0;
659 nmlvcd
.clrText
= infoPtr
->clrText
;
660 nmlvcd
.clrTextBk
= infoPtr
->clrBk
;
662 return (BOOL
)notify(infoPtr
, NM_CUSTOMDRAW
, &nmlvcd
.nmcd
.hdr
);
665 /* FIXME: we should inline this where it's called somehow
666 * I think we need to pass in the structure
668 static BOOL
notify_customdrawitem (LISTVIEW_INFO
*infoPtr
, HDC hdc
, UINT iItem
, UINT iSubItem
, UINT uItemDrawState
)
670 NMLVCUSTOMDRAW nmlvcd
;
678 item
.mask
= LVIF_PARAM
;
679 if (!LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) return FALSE
;
683 if (LISTVIEW_GetItemState(infoPtr
, iItem
, LVIS_SELECTED
)) uItemState
|= CDIS_SELECTED
;
684 if (LISTVIEW_GetItemState(infoPtr
, iItem
, LVIS_FOCUSED
)) uItemState
|= CDIS_FOCUS
;
685 if (iItem
== infoPtr
->nHotItem
) uItemState
|= CDIS_HOT
;
687 itemRect
.left
= LVIR_BOUNDS
;
688 LISTVIEW_GetItemRect(infoPtr
, iItem
, &itemRect
);
690 nmlvcd
.nmcd
.dwDrawStage
= CDDS_ITEM
| uItemDrawState
;
691 nmlvcd
.nmcd
.hdc
= hdc
;
692 nmlvcd
.nmcd
.rc
= itemRect
;
693 nmlvcd
.nmcd
.dwItemSpec
= iItem
;
694 nmlvcd
.nmcd
.uItemState
= uItemState
;
695 nmlvcd
.nmcd
.lItemlParam
= item
.lParam
;
696 nmlvcd
.clrText
= infoPtr
->clrText
;
697 nmlvcd
.clrTextBk
= infoPtr
->clrBk
;
698 nmlvcd
.iSubItem
= iSubItem
;
700 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
701 nmlvcd
.nmcd
.dwDrawStage
, nmlvcd
.nmcd
.hdc
, nmlvcd
.nmcd
.dwItemSpec
,
702 nmlvcd
.nmcd
.uItemState
, nmlvcd
.nmcd
.lItemlParam
);
704 bReturn
= notify(infoPtr
, NM_CUSTOMDRAW
, &nmlvcd
.nmcd
.hdr
);
706 infoPtr
->clrText
= nmlvcd
.clrText
;
707 infoPtr
->clrBk
= nmlvcd
.clrTextBk
;
712 /******** Misc helper functions ************************************/
714 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
715 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
717 if (isW
) return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
718 else return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
721 /******** Internal API functions ************************************/
723 /* The Invalidate* are macros, so we preserve the caller location */
724 #define LISTVIEW_InvalidateRect(infoPtr, rect) do { \
725 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
726 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
729 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
731 rcItem.left = LVIR_BOUNDS; \
732 if(LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) \
733 LISTVIEW_InvalidateRect(infoPtr, &rcItem); \
736 #define LISTVIEW_InvalidateList(infoPtr)\
737 LISTVIEW_InvalidateRect(infoPtr, NULL)
739 static inline BOOL
LISTVIEW_GetItemW(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
741 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
744 static inline int LISTVIEW_GetType(LISTVIEW_INFO
*infoPtr
)
746 return infoPtr
->dwStyle
& LVS_TYPEMASK
;
751 * Retrieves the number of items that can fit vertically in the client area.
754 * [I] infoPtr : valid pointer to the listview structure
757 * Number of items per row.
759 static inline INT
LISTVIEW_GetCountPerRow(LISTVIEW_INFO
*infoPtr
)
761 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
763 return max(nListWidth
/infoPtr
->nItemWidth
, 1);
768 * Retrieves the number of items that can fit horizontally in the client
772 * [I] infoPtr : valid pointer to the listview structure
775 * Number of items per column.
777 static inline INT
LISTVIEW_GetCountPerColumn(LISTVIEW_INFO
*infoPtr
)
779 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
781 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
786 * Retrieves the range of visible items. Note that the upper limit
787 * may be a bit larger than the actual last visible item.
790 * [I] infoPtr : valid pointer to the listview structure
793 * maximum range of visible items
795 static RANGE
LISTVIEW_GetVisibleRange(LISTVIEW_INFO
*infoPtr
)
797 UINT uView
= LISTVIEW_GetType(infoPtr
);
798 INT nPerCol
, nPerRow
;
801 visrange
.lower
= LISTVIEW_GetTopIndex(infoPtr
);
803 if (uView
== LVS_REPORT
)
805 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
808 else if (uView
== LVS_LIST
)
810 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
811 nPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
815 /* FIXME: this is correct only in autoarrange mode */
816 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
817 nPerRow
= LISTVIEW_GetCountPerRow(infoPtr
) + 1;
820 visrange
.upper
= visrange
.lower
+ nPerCol
* nPerRow
;
821 if (visrange
.upper
> infoPtr
->nItemCount
)
822 visrange
.upper
= infoPtr
->nItemCount
;
824 TRACE("range=(%d, %d)\n", visrange
.lower
, visrange
.upper
);
830 /*************************************************************************
831 * LISTVIEW_ProcessLetterKeys
833 * Processes keyboard messages generated by pressing the letter keys
835 * What this does is perform a case insensitive search from the
836 * current position with the following quirks:
837 * - If two chars or more are pressed in quick succession we search
838 * for the corresponding string (e.g. 'abc').
839 * - If there is a delay we wipe away the current search string and
840 * restart with just that char.
841 * - If the user keeps pressing the same character, whether slowly or
842 * fast, so that the search string is entirely composed of this
843 * character ('aaaaa' for instance), then we search for first item
844 * that starting with that character.
845 * - If the user types the above character in quick succession, then
846 * we must also search for the corresponding string ('aaaaa'), and
847 * go to that string if there is a match.
850 * [I] hwnd : handle to the window
851 * [I] charCode : the character code, the actual character
852 * [I] keyData : key data
860 * - The current implementation has a list of characters it will
861 * accept and it ignores averything else. In particular it will
862 * ignore accentuated characters which seems to match what
863 * Windows does. But I'm not sure it makes sense to follow
865 * - We don't sound a beep when the search fails.
869 * TREEVIEW_ProcessLetterKeys
871 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
876 WCHAR buffer
[MAX_PATH
];
877 DWORD timestamp
,elapsed
;
879 /* simple parameter checking */
880 if (!charCode
|| !keyData
) return 0;
882 /* only allow the valid WM_CHARs through */
883 if (!isalnum(charCode
) &&
884 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
885 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
886 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
887 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
888 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
889 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
890 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
891 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
892 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
895 /* if there's one item or less, there is no where to go */
896 if (infoPtr
->nItemCount
<= 1) return 0;
898 /* compute how much time elapsed since last keypress */
899 timestamp
=GetTickCount();
900 if (timestamp
> infoPtr
->lastKeyPressTimestamp
) {
901 elapsed
=timestamp
-infoPtr
->lastKeyPressTimestamp
;
903 elapsed
=infoPtr
->lastKeyPressTimestamp
-timestamp
;
906 /* update the search parameters */
907 infoPtr
->lastKeyPressTimestamp
=timestamp
;
908 if (elapsed
< KEY_DELAY
) {
909 if (infoPtr
->nSearchParamLength
< MAX_PATH
) {
910 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++]=charCode
;
912 if (infoPtr
->charCode
!= charCode
) {
913 infoPtr
->charCode
=charCode
=0;
916 infoPtr
->charCode
=charCode
;
917 infoPtr
->szSearchParam
[0]=charCode
;
918 infoPtr
->nSearchParamLength
=1;
919 /* Redundant with the 1 char string */
923 /* and search from the current position */
925 if (infoPtr
->nFocusedItem
>= 0) {
926 endidx
=infoPtr
->nFocusedItem
;
928 /* if looking for single character match,
929 * then we must always move forward
931 if (infoPtr
->nSearchParamLength
== 1)
934 endidx
=infoPtr
->nItemCount
;
938 if (idx
== infoPtr
->nItemCount
) {
939 if (endidx
== infoPtr
->nItemCount
|| endidx
== 0)
945 item
.mask
= LVIF_TEXT
;
948 item
.pszText
= buffer
;
949 item
.cchTextMax
= MAX_PATH
;
950 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
952 /* check for a match */
953 if (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,infoPtr
->nSearchParamLength
) == 0) {
956 } else if ( (charCode
!= 0) && (nItem
== -1) && (nItem
!= infoPtr
->nFocusedItem
) &&
957 (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,1) == 0) ) {
958 /* This would work but we must keep looking for a longer match */
962 } while (idx
!= endidx
);
965 LISTVIEW_KeySelection(infoPtr
, nItem
);
970 /*************************************************************************
971 * LISTVIEW_UpdateHeaderSize [Internal]
973 * Function to resize the header control
976 * hwnd [I] handle to a window
977 * nNewScrollPos [I] Scroll Pos to Set
984 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
989 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
991 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
992 point
[0].x
= winRect
.left
;
993 point
[0].y
= winRect
.top
;
994 point
[1].x
= winRect
.right
;
995 point
[1].y
= winRect
.bottom
;
997 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
998 point
[0].x
= -nNewScrollPos
;
999 point
[1].x
+= nNewScrollPos
;
1001 SetWindowPos(infoPtr
->hwndHeader
,0,
1002 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
1003 SWP_NOZORDER
| SWP_NOACTIVATE
);
1008 * Update the scrollbars. This functions should be called whenever
1009 * the content, size or view changes.
1012 * [I] infoPtr : valid pointer to the listview structure
1017 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO
*infoPtr
)
1019 LONG lStyle
= infoPtr
->dwStyle
;
1020 UINT uView
= lStyle
& LVS_TYPEMASK
;
1021 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1022 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1023 SCROLLINFO scrollInfo
;
1025 if (lStyle
& LVS_NOSCROLL
) return;
1027 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
1029 if (uView
== LVS_LIST
)
1031 /* update horizontal scrollbar */
1032 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
1033 INT nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
1035 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1036 infoPtr
->nItemCount
, nCountPerColumn
, nCountPerRow
);
1038 scrollInfo
.nMin
= 0;
1039 scrollInfo
.nMax
= infoPtr
->nItemCount
/ nCountPerColumn
;
1040 if((infoPtr
->nItemCount
% nCountPerColumn
) == 0)
1042 if (scrollInfo
.nMax
< 0) scrollInfo
.nMax
= 0;
1043 scrollInfo
.nPos
= ListView_GetTopIndex(infoPtr
->hwndSelf
) / nCountPerColumn
;
1044 scrollInfo
.nPage
= nCountPerRow
;
1045 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
1046 SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
1047 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
1049 else if (uView
== LVS_REPORT
)
1053 /* update vertical scrollbar */
1054 scrollInfo
.nMin
= 0;
1055 scrollInfo
.nMax
= infoPtr
->nItemCount
- 1;
1056 scrollInfo
.nPos
= ListView_GetTopIndex(infoPtr
->hwndSelf
);
1057 scrollInfo
.nPage
= LISTVIEW_GetCountPerColumn(infoPtr
);
1058 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
1059 test
= (scrollInfo
.nMin
>= scrollInfo
.nMax
- max((INT
)scrollInfo
.nPage
- 1, 0));
1060 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1061 scrollInfo
.nMax
, scrollInfo
.nPage
, test
);
1062 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
1063 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, (test
) ? FALSE
: TRUE
);
1065 /* update horizontal scrollbar */
1066 nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1067 scrollInfo
.fMask
= SIF_POS
;
1068 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)
1069 || infoPtr
->nItemCount
== 0)
1071 scrollInfo
.nPos
= 0;
1073 scrollInfo
.nMin
= 0;
1074 scrollInfo
.nMax
= max(infoPtr
->nItemWidth
, 0)-1;
1075 scrollInfo
.nPage
= nListWidth
;
1076 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
1077 test
= (scrollInfo
.nMin
>= scrollInfo
.nMax
- max((INT
)scrollInfo
.nPage
- 1, 0));
1078 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1079 scrollInfo
.nMax
, scrollInfo
.nPage
, test
);
1080 SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
1081 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, (test
) ? FALSE
: TRUE
);
1083 /* Update the Header Control */
1084 scrollInfo
.fMask
= SIF_POS
;
1085 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
);
1086 LISTVIEW_UpdateHeaderSize(infoPtr
, scrollInfo
.nPos
);
1093 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
))
1095 INT nViewWidth
= rcView
.right
- rcView
.left
;
1096 INT nViewHeight
= rcView
.bottom
- rcView
.top
;
1098 /* Update Horizontal Scrollbar */
1099 scrollInfo
.fMask
= SIF_POS
;
1100 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)
1101 || infoPtr
->nItemCount
== 0)
1103 scrollInfo
.nPos
= 0;
1105 scrollInfo
.nMin
= 0;
1106 scrollInfo
.nMax
= max(nViewWidth
, 0)-1;
1107 scrollInfo
.nPage
= nListWidth
;
1108 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
1109 TRACE("LVS_ICON/SMALLICON Horz.\n");
1110 SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
1112 /* Update Vertical Scrollbar */
1113 nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1114 scrollInfo
.fMask
= SIF_POS
;
1115 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)
1116 || infoPtr
->nItemCount
== 0)
1118 scrollInfo
.nPos
= 0;
1120 scrollInfo
.nMin
= 0;
1121 scrollInfo
.nMax
= max(nViewHeight
,0)-1;
1122 scrollInfo
.nPage
= nListHeight
;
1123 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
1124 TRACE("LVS_ICON/SMALLICON Vert.\n");
1125 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
1133 * Shows/hides the focus rectangle.
1136 * [I] infoPtr : valid pointer to the listview structure
1137 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1142 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL fShow
)
1146 TRACE("fShow=%d, nItem=%d\n", fShow
, nItem
);
1148 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return;
1150 rcItem
.left
= LVIR_BOUNDS
;
1152 if ( (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
&&
1153 !(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) &&
1154 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))
1156 /* this little optimization eliminates some nasty flicker */
1157 if (!LISTVIEW_GetSubItemRect(infoPtr
, nItem
, &rcItem
)) return;
1161 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return;
1164 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
1172 item
.mask
= LVIF_PARAM
;
1173 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto invalidate
;
1175 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) goto invalidate
;
1176 ZeroMemory(&dis
, sizeof(dis
));
1177 dis
.CtlType
= ODT_LISTVIEW
;
1178 dis
.CtlID
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_ID
);
1180 dis
.itemAction
= ODA_FOCUS
;
1181 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
1182 dis
.hwndItem
= infoPtr
->hwndSelf
;
1184 dis
.rcItem
= rcItem
;
1185 dis
.itemData
= item
.lParam
;
1187 SendMessageW(GetParent(infoPtr
->hwndSelf
), WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
1188 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1193 /* Here we are inneficient. We could, in theory, simply DrawFocusRect
1194 * to erase/show the focus, without all this heavy duty redraw.
1195 * Note that there are cases where we can not do that: when the list
1196 * is in ICON mode, and the item is large, we must to invalidate it.
1197 * Moreover, in the vast majority of cases, the selection status of
1198 * the item changes anyway, and so the item is invalidated already,
1199 * so not too much harm is done. If we do notice any flicker, we should
1200 * refine this method. */
1202 LISTVIEW_InvalidateRect(infoPtr
, &rcItem
);
1207 * Invalidates all visible selected items.
1209 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO
*infoPtr
)
1214 visrange
= LISTVIEW_GetVisibleRange(infoPtr
);
1215 for (i
= visrange
.lower
; i
<= visrange
.upper
; i
++)
1217 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
1218 LISTVIEW_InvalidateItem(infoPtr
, i
);
1225 * Prints a message for unsupported window styles.
1226 * A kind of TODO list for window styles.
1229 * [I] LONG : window style
1234 static void LISTVIEW_UnsupportedStyles(LONG lStyle
)
1236 if ((LVS_TYPESTYLEMASK
& lStyle
) == LVS_NOSCROLL
)
1237 FIXME(" LVS_NOSCROLL\n");
1239 if (lStyle
& LVS_NOLABELWRAP
)
1240 FIXME(" LVS_NOLABELWRAP\n");
1242 if (lStyle
& LVS_SORTASCENDING
)
1243 FIXME(" LVS_SORTASCENDING\n");
1245 if (lStyle
& LVS_SORTDESCENDING
)
1246 FIXME(" LVS_SORTDESCENDING\n");
1250 * DESCRIPTION: [INTERNAL]
1251 * Update the bounding rectangle around the text under a large icon.
1252 * This depends on whether it has the focus or not.
1253 * On entry the rectangle's top, left and right should be set.
1254 * On return the bottom will also be set and the width may have been
1258 * [I] infoPtr : pointer to the listview structure
1259 * [I] nItem : the item for which we are calculating this
1260 * [I/O] rect : the rectangle to be updated
1262 * This appears to be weird, even in the Microsoft implementation.
1264 static BOOL
LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO
*infoPtr
, int nItem
, RECT
*rect
)
1266 HDC hdc
= GetDC (infoPtr
->hwndSelf
);
1267 HFONT hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
1268 UINT uFormat
= LISTVIEW_DTFLAGS
| DT_CALCRECT
;
1269 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
1270 RECT rcText
= *rect
;
1271 RECT rcBack
= *rect
;
1272 BOOL focused
, selected
;
1273 int dx
, dy
, old_wid
, new_wid
;
1276 TRACE("%s, focus item=%d, cur item=%d\n",
1277 (infoPtr
->bFocus
) ? "Window has focus" : "Window not focused",
1278 infoPtr
->nFocusedItem
, nItem
);
1281 focused
= infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
);
1282 selected
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
1284 uFormat
|= (focused
) ? DT_NOCLIP
: DT_WORD_ELLIPSIS
| DT_END_ELLIPSIS
;
1286 /* We (aim to) display the full text. In Windows 95 it appears to
1287 * calculate the size assuming the specified font and then it draws
1288 * the text in that region with the specified font except scaled to
1289 * 10 point (or the height of the system font or ...). Thus if the
1290 * window has 24 point Helvetica the highlit rectangle will be
1291 * taller than the text and if it is 7 point Helvetica then the text
1293 * For now we will simply say that it is the correct size to display
1294 * the text in the specified font.
1296 lvItem
.mask
= LVIF_TEXT
;
1297 lvItem
.iItem
= nItem
;
1298 lvItem
.iSubItem
= 0;
1299 lvItem
.pszText
= szDispText
;
1300 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
1301 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
1303 InflateRect(&rcText
, -2, 0);
1304 DrawTextW (hdc
, lvItem
.pszText
, -1, &rcText
, uFormat
);
1305 /* Microsoft, in their great wisdom, have decided that the rectangle
1306 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
1307 * not the location. So we have to do the centring ourselves (and take
1308 * responsibility for agreeing off-by-one consistency with them).
1311 old_wid
= rcText
.right
- rcText
.left
;
1312 new_wid
= rcBack
.right
- rcBack
.left
;
1313 dx
= rcBack
.left
- rcText
.left
+ (new_wid
-old_wid
)/2;
1314 dy
= rcBack
.top
- rcText
.top
;
1315 OffsetRect (&rcText
, dx
, dy
);
1320 InflateRect(&rcText
, 2, 0);
1322 else /* not focused, may or may not be selected */
1325 * We need to have the bottom to be an intergal number of
1326 * text lines (ntmHeight) below text top that is less than
1327 * or equal to the nItemHeight.
1329 INT lh
= infoPtr
->nItemHeight
- infoPtr
->iconSize
.cy
-
1330 ICON_TOP_PADDING
- ICON_BOTTOM_PADDING
;
1331 INT ih
= (lh
/ infoPtr
->ntmHeight
) * infoPtr
->ntmHeight
;
1332 rcText
.bottom
= min(rcText
.bottom
, rcText
.top
+ ih
);
1337 TRACE("%s and %s, bounding rect=(%d,%d)-(%d,%d)\n",
1338 (focused
) ? "focused(full text)" : "not focused",
1339 (selected
) ? "selected" : "not selected",
1340 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
1342 SelectObject (hdc
, hOldFont
);
1343 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
1349 * DESCRIPTION: [INTERNAL]
1350 * Compute the rectangles of an item. This is to localize all
1351 * the computations in one place. If you are not interested in some
1352 * of these values, simply pass in a NULL -- the fucntion is smart
1353 * enough to compute only what's necessary. The function computes
1354 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1355 * one, the BOX rectangle. This rectangle is very cheap to compute,
1356 * and is guaranteed to contain all the other rectangles. Computing
1357 * the ICON rect is also cheap, but all the others are potentaily
1358 * expensive. This gives an easy and effective optimization when
1359 * searching (like point inclusion, or rectangle intersection):
1360 * first test against the BOX, and if TRUE, test agains the desired
1361 * rectangle. These optimizations are coded in:
1362 * LISTVIEW_PtInRect, and LISTVIEW_IntersectRect
1363 * use them wherever is appropriate.
1366 * [I] infoPtr : valid pointer to the listview structure
1367 * [I] nItem : item number
1368 * [O] lprcBox : ptr to Box rectangle
1369 * The internal LVIR_BOX rectangle
1370 * [O] lprcBounds : ptr to Bounds rectangle
1371 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1372 * [O] lprcIcon : ptr to Icon rectangle
1373 * Same as LVM_GETITEMRECT with LVIR_ICON
1374 * [O] lprcLabel : ptr to Label rectangle
1375 * Same as LVM_GETITEMRECT with LVIR_LABEL
1378 * TRUE if computations OK
1381 static BOOL
LISTVIEW_GetItemMeasures(LISTVIEW_INFO
*infoPtr
, INT nItem
,
1382 LPRECT lprcBox
, LPRECT lprcBounds
,
1383 LPRECT lprcIcon
, LPRECT lprcLabel
)
1385 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1386 BOOL doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
1387 RECT Box
, Icon
, Label
;
1390 /* This should be very cheap to compute */
1391 if (!LISTVIEW_GetOrigin(infoPtr
, &Origin
)) return FALSE
;
1393 /* Be smart and try to figure out the minimum we have to do */
1396 if (uView
== LVS_REPORT
) doIcon
= TRUE
;
1397 else doLabel
= TRUE
;
1399 if (uView
== LVS_ICON
&& infoPtr
->bFocus
&&
1400 LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
1401 oversizedBox
= doLabel
= TRUE
;
1402 if (lprcLabel
) doLabel
= TRUE
;
1403 if (doLabel
|| lprcIcon
) doIcon
= TRUE
;
1405 /************************************************************/
1406 /* compute the box rectangle (it should be cheap to do) */
1407 /************************************************************/
1408 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1410 Box
.left
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1411 Box
.top
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1413 else if (uView
== LVS_LIST
)
1415 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
1416 Box
.left
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
1417 Box
.top
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
1419 else /* LVS_REPORT */
1421 Box
.left
= REPORT_MARGINX
;
1422 Box
.top
= nItem
* infoPtr
->nItemHeight
;
1424 Box
.left
+= Origin
.x
;
1425 Box
.top
+= Origin
.y
;
1426 Box
.right
= Box
.left
+ infoPtr
->nItemWidth
;
1427 Box
.bottom
= Box
.top
+ infoPtr
->nItemHeight
;
1429 /************************************************************/
1430 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1431 /************************************************************/
1434 if (uView
== LVS_ICON
)
1436 Icon
.left
= Box
.left
;
1437 if (infoPtr
->himlNormal
)
1438 Icon
.left
+= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2 - ICON_LR_HALF
;
1440 Icon
.right
= Icon
.left
;
1441 Icon
.bottom
= Icon
.top
;
1442 if (infoPtr
->himlNormal
)
1444 Icon
.right
+= infoPtr
->iconSize
.cx
+ ICON_LR_PADDING
;
1445 Icon
.bottom
+= infoPtr
->iconSize
.cy
+ ICON_TOP_PADDING
;
1448 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1451 Icon
.left
= Box
.left
;
1452 if (uView
== LVS_REPORT
)
1456 lvItem
.mask
= LVIF_INDENT
;
1457 lvItem
.iItem
= nItem
;
1458 lvItem
.iSubItem
= 0;
1459 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
1460 Icon
.left
+= infoPtr
->iconSize
.cx
* lvItem
.iIndent
;
1462 if (infoPtr
->himlState
) Icon
.left
+= infoPtr
->iconStateSize
.cx
;
1464 Icon
.right
= Icon
.left
;
1465 if (infoPtr
->himlSmall
) Icon
.right
+= infoPtr
->iconSize
.cx
;
1466 Icon
.bottom
= Icon
.top
+ infoPtr
->nItemHeight
;
1468 if(lprcIcon
) *lprcIcon
= Icon
;
1469 TRACE("hwnd=%x, item=%d, icon=%s\n", infoPtr
->hwndSelf
, nItem
, debugrect(&Icon
));
1472 /************************************************************/
1473 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1474 /************************************************************/
1477 if (uView
== LVS_ICON
)
1481 Label
.left
= Box
.left
;
1482 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
1483 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
1485 nLabelWidth
= LISTVIEW_GetLabelWidth(infoPtr
, nItem
);
1486 if (infoPtr
->iconSpacing
.cx
- nLabelWidth
> 1)
1488 Label
.left
+= (infoPtr
->iconSpacing
.cx
- nLabelWidth
) / 2;
1489 Label
.right
= Label
.left
+ nLabelWidth
;
1490 Label
.bottom
= Label
.top
+ infoPtr
->ntmHeight
+ 1;
1491 Label
.bottom
+= HEIGHT_PADDING
;
1495 Label
.right
= Label
.left
+ infoPtr
->nItemWidth
;
1496 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
+ HEIGHT_PADDING
;
1497 LISTVIEW_UpdateLargeItemLabelRect (infoPtr
, nItem
, &Label
);
1500 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1504 Label
.left
= Icon
.right
;
1505 Label
.top
= Box
.top
;
1506 nLabelWidth
= LISTVIEW_GetLabelWidth(infoPtr
, nItem
);
1507 nLabelWidth
+= TRAILING_PADDING
;
1508 if (infoPtr
->himlSmall
) nLabelWidth
+= IMAGE_PADDING
;
1509 Label
.right
= min(Label
.left
+ nLabelWidth
, Box
.right
);
1510 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
1513 if (lprcLabel
) *lprcLabel
= Label
;
1514 TRACE("hwnd=%x, item=%d, label=%s\n", infoPtr
->hwndSelf
, nItem
, debugrect(&Label
));
1517 /***********************************************************/
1518 /* compute bounds box for the item (ala LVM_GETITEMRECT) */
1519 /***********************************************************/
1522 if (uView
== LVS_REPORT
)
1524 lprcBounds
->left
= infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
? Box
.left
: Icon
.left
;
1525 lprcBounds
->top
= Box
.top
;
1526 lprcBounds
->right
= min(lprcBounds
->left
+ infoPtr
->nItemWidth
, Box
.right
) - REPORT_MARGINX
;
1527 if (lprcBounds
->right
< lprcBounds
->left
) lprcBounds
->right
= lprcBounds
->left
;
1528 lprcBounds
->bottom
= lprcBounds
->top
+ infoPtr
->nItemHeight
;
1531 UnionRect(lprcBounds
, &Icon
, &Label
);
1532 TRACE("hwnd=%x, item=%d, bounds=%s\n", infoPtr
->hwndSelf
, nItem
, debugrect(lprcBounds
));
1535 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
1536 else if (lprcBox
) *lprcBox
= Box
;
1537 TRACE("hwnd=%x, item=%d, box=%s\n", infoPtr
->hwndSelf
, nItem
, debugrect(&Box
));
1544 * Aligns the items with the top edge of the window.
1547 * [I] infoPtr : valid pointer to the listview structure
1552 static void LISTVIEW_AlignTop(LISTVIEW_INFO
*infoPtr
)
1554 UINT uView
= LISTVIEW_GetType(infoPtr
);
1555 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1558 INT i
, off_x
=0, off_y
=0;
1560 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1562 /* Since SetItemPosition uses upper-left of icon, and for
1563 style=LVS_ICON the icon is not left adjusted, get the offset */
1564 if (uView
== LVS_ICON
)
1566 off_y
= ICON_TOP_PADDING
;
1567 off_x
= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2;
1571 ZeroMemory(&rcView
, sizeof(RECT
));
1572 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1574 infoPtr
->rcList
.left
, infoPtr
->rcList
.right
);
1576 if (nListWidth
> infoPtr
->nItemWidth
)
1578 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
1580 if ((ptItem
.x
-off_x
) + infoPtr
->nItemWidth
> nListWidth
)
1583 ptItem
.y
+= infoPtr
->nItemHeight
;
1586 LISTVIEW_SetItemPosition(infoPtr
, i
, ptItem
);
1587 ptItem
.x
+= infoPtr
->nItemWidth
;
1588 rcView
.right
= max(rcView
.right
, ptItem
.x
);
1591 rcView
.right
-= off_x
;
1592 rcView
.bottom
= (ptItem
.y
-off_y
) + infoPtr
->nItemHeight
;
1596 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
1598 LISTVIEW_SetItemPosition(infoPtr
, i
, ptItem
);
1599 ptItem
.y
+= infoPtr
->nItemHeight
;
1602 rcView
.right
= infoPtr
->nItemWidth
;
1603 rcView
.bottom
= ptItem
.y
-off_y
;
1606 infoPtr
->rcView
= rcView
;
1612 * Aligns the items with the left edge of the window.
1615 * [I] infoPtr : valid pointer to the listview structure
1620 static void LISTVIEW_AlignLeft(LISTVIEW_INFO
*infoPtr
)
1622 UINT uView
= LISTVIEW_GetType(infoPtr
);
1623 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1626 INT i
, off_x
=0, off_y
=0;
1628 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1630 /* Since SetItemPosition uses upper-left of icon, and for
1631 style=LVS_ICON the icon is not left adjusted, get the offset */
1632 if (uView
== LVS_ICON
)
1634 off_y
= ICON_TOP_PADDING
;
1635 off_x
= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2;
1639 ZeroMemory(&rcView
, sizeof(RECT
));
1640 TRACE("Icon off.x=%d, off.y=%d\n", off_x
, off_y
);
1642 if (nListHeight
> infoPtr
->nItemHeight
)
1644 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
1646 if (ptItem
.y
+ infoPtr
->nItemHeight
> nListHeight
)
1649 ptItem
.x
+= infoPtr
->nItemWidth
;
1652 LISTVIEW_SetItemPosition(infoPtr
, i
, ptItem
);
1653 ptItem
.y
+= infoPtr
->nItemHeight
;
1654 rcView
.bottom
= max(rcView
.bottom
, ptItem
.y
);
1657 rcView
.right
= ptItem
.x
+ infoPtr
->nItemWidth
;
1661 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
1663 LISTVIEW_SetItemPosition(infoPtr
, i
, ptItem
);
1664 ptItem
.x
+= infoPtr
->nItemWidth
;
1667 rcView
.bottom
= infoPtr
->nItemHeight
;
1668 rcView
.right
= ptItem
.x
;
1671 infoPtr
->rcView
= rcView
;
1678 * Retrieves the bounding rectangle of all the items.
1681 * [I] infoPtr : valid pointer to the listview structure
1682 * [O] lprcView : bounding rectangle
1688 static BOOL
LISTVIEW_GetViewRect(LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
1692 TRACE("(lprcView=%p)\n", lprcView
);
1694 if (!lprcView
) return FALSE
;
1696 if (!LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
)) return FALSE
;
1698 *lprcView
= infoPtr
->rcView
;
1699 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
1701 TRACE("lprcView=%s\n", debugrect(lprcView
));
1708 * Retrieves the subitem pointer associated with the subitem index.
1711 * [I] HDPA : DPA handle for a specific item
1712 * [I] INT : index of subitem
1715 * SUCCESS : subitem pointer
1718 static LISTVIEW_SUBITEM
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
,
1721 LISTVIEW_SUBITEM
*lpSubItem
;
1724 /* we should binary search here if need be */
1725 for (i
= 1; i
< hdpaSubItems
->nItemCount
; i
++)
1727 lpSubItem
= (LISTVIEW_SUBITEM
*) DPA_GetPtr(hdpaSubItems
, i
);
1728 if (lpSubItem
&& (lpSubItem
->iSubItem
== nSubItem
))
1738 * Calculates the width of a specific item.
1741 * [I] infoPtr : valid pointer to the listview structure
1742 * [I] nItem : item to calculate width, or -1 for max of all
1745 * Returns the width of an item width an item.
1747 static INT
LISTVIEW_CalculateItemWidth(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1749 UINT uView
= LISTVIEW_GetType(infoPtr
);
1750 INT nItemWidth
= 0, i
;
1752 if (uView
== LVS_ICON
)
1753 nItemWidth
= infoPtr
->iconSpacing
.cx
;
1754 else if (uView
== LVS_REPORT
)
1756 INT nHeaderItemCount
;
1759 /* calculate width of header */
1760 nHeaderItemCount
= Header_GetItemCount(infoPtr
->hwndHeader
);
1761 for (i
= 0; i
< nHeaderItemCount
; i
++)
1762 if (Header_GetItemRect(infoPtr
->hwndHeader
, i
, &rcHeaderItem
))
1763 nItemWidth
+= (rcHeaderItem
.right
- rcHeaderItem
.left
);
1769 if (infoPtr
->nItemCount
== 0) return DEFAULT_COLUMN_WIDTH
;
1771 /* get width of string */
1774 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
1776 nLabelWidth
= LISTVIEW_GetLabelWidth(infoPtr
, i
);
1777 nItemWidth
= max(nItemWidth
, nLabelWidth
);
1781 nItemWidth
= LISTVIEW_GetLabelWidth(infoPtr
, nItem
);
1782 if (!nItemWidth
) return DEFAULT_COLUMN_WIDTH
;
1783 nItemWidth
+= WIDTH_PADDING
;
1784 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
1785 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
1786 if (nItem
== -1) nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
);
1789 return max(nItemWidth
, 1);
1794 * Calculates the max width of any item in the list.
1797 * [I] infoPtr : valid pointer to the listview structure
1798 * [I] LONG : window style
1801 * Returns item width.
1803 static inline INT
LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO
*infoPtr
)
1805 return LISTVIEW_CalculateItemWidth(infoPtr
, -1);
1810 * Retrieves and saves important text metrics info for the current
1814 * [I] infoPtr : valid pointer to the listview structure
1817 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
1820 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
1821 HFONT hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
1822 INT oldHeight
, oldACW
;
1824 GetTextMetricsW(hdc
, &tm
);
1826 oldHeight
= infoPtr
->ntmHeight
;
1827 oldACW
= infoPtr
->ntmAveCharWidth
;
1828 infoPtr
->ntmHeight
= tm
.tmHeight
;
1829 infoPtr
->ntmAveCharWidth
= tm
.tmAveCharWidth
;
1831 SelectObject(hdc
, hOldFont
);
1832 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1833 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1834 oldHeight
, infoPtr
->ntmHeight
, oldACW
, infoPtr
->ntmAveCharWidth
);
1840 * Calculates the height of an item.
1843 * [I] infoPtr : valid pointer to the listview structure
1846 * Returns item height.
1848 static INT
LISTVIEW_GetItemHeight(LISTVIEW_INFO
*infoPtr
)
1852 if (LISTVIEW_GetType(infoPtr
) == LVS_ICON
)
1853 nItemHeight
= infoPtr
->iconSpacing
.cy
;
1856 nItemHeight
= infoPtr
->ntmHeight
;
1857 if (infoPtr
->himlState
)
1858 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
1859 if (infoPtr
->himlSmall
)
1860 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
1861 if (infoPtr
->himlState
|| infoPtr
->himlSmall
)
1862 nItemHeight
+= HEIGHT_PADDING
;
1868 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO
*infoPtr
)
1872 ERR("Selections are:\n");
1873 for (i
= 0; i
< infoPtr
->hdpaSelectionRanges
->nItemCount
; i
++)
1875 RANGE
*selection
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
, i
);
1876 ERR(" [%d - %d]\n", selection
->lower
, selection
->upper
);
1883 * A compare function for selection ranges
1886 * [I] range1 : pointer to selection range 1;
1887 * [I] range2 : pointer to selection range 2;
1891 * >0 : if Item 1 > Item 2
1892 * <0 : if Item 2 > Item 1
1893 * 0 : if Item 1 == Item 2
1895 static INT CALLBACK
LISTVIEW_CompareSelectionRanges(LPVOID range1
, LPVOID range2
, LPARAM flags
)
1897 if (((RANGE
*)range1
)->upper
< ((RANGE
*)range2
)->lower
)
1899 if (((RANGE
*)range2
)->upper
< ((RANGE
*)range1
)->lower
)
1905 * Helper function for LISTVIEW_AddSelectionRange, and LISTVIEW_SetItem.
1907 static BOOL
add_selection_range(LISTVIEW_INFO
*infoPtr
, INT lower
, INT upper
, BOOL adj_sel_only
)
1913 TRACE("range (%i - %i)\n", lower
, upper
);
1915 /* try find overlapping selections first */
1916 selection
.lower
= lower
- 1;
1917 selection
.upper
= upper
+ 1;
1918 index
= DPA_Search(infoPtr
->hdpaSelectionRanges
, &selection
, 0,
1919 LISTVIEW_CompareSelectionRanges
, 0, 0);
1925 /* create the brand new selection to insert */
1926 newsel
= (RANGE
*)COMCTL32_Alloc(sizeof(RANGE
));
1927 if(!newsel
) return FALSE
;
1928 newsel
->lower
= lower
;
1929 newsel
->upper
= upper
;
1931 /* figure out where to insert it */
1932 index
= DPA_Search(infoPtr
->hdpaSelectionRanges
, newsel
, 0,
1933 LISTVIEW_CompareSelectionRanges
, 0, DPAS_INSERTAFTER
);
1934 if (index
== -1) index
= 0;
1936 /* and get it over with */
1937 DPA_InsertPtr(infoPtr
->hdpaSelectionRanges
, index
, newsel
);
1941 RANGE
*chksel
, *mrgsel
;
1942 INT fromindex
, mergeindex
;
1944 chksel
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
, index
);
1945 if (!chksel
) return FALSE
;
1946 TRACE("Merge with index %i (%d - %d)\n",
1947 index
, chksel
->lower
, chksel
->upper
);
1949 chksel
->lower
= min(lower
, chksel
->lower
);
1950 chksel
->upper
= max(upper
, chksel
->upper
);
1952 TRACE("New range %i (%d - %d)\n",
1953 index
, chksel
->lower
, chksel
->upper
);
1955 /* merge now common selection ranges */
1957 selection
.lower
= chksel
->lower
- 1;
1958 selection
.upper
= chksel
->upper
+ 1;
1962 mergeindex
= DPA_Search(infoPtr
->hdpaSelectionRanges
, &selection
, fromindex
,
1963 LISTVIEW_CompareSelectionRanges
, 0, 0);
1964 if (mergeindex
== -1) break;
1965 if (mergeindex
== index
)
1967 fromindex
= index
+ 1;
1971 TRACE("Merge with index %i\n", mergeindex
);
1973 mrgsel
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
, mergeindex
);
1974 if (!mrgsel
) return FALSE
;
1976 chksel
->lower
= min(chksel
->lower
, mrgsel
->lower
);
1977 chksel
->upper
= max(chksel
->upper
, mrgsel
->upper
);
1978 COMCTL32_Free(mrgsel
);
1979 DPA_DeletePtr(infoPtr
->hdpaSelectionRanges
, mergeindex
);
1980 if (mergeindex
< index
) index
--;
1984 /*DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);*/
1986 if (adj_sel_only
) return TRUE
;
1988 /* set the selection on items */
1989 lvItem
.state
= LVIS_SELECTED
;
1990 lvItem
.stateMask
= LVIS_SELECTED
;
1991 for(i
= lower
; i
<= upper
; i
++)
1992 LISTVIEW_SetItemState(infoPtr
, i
, &lvItem
);
1998 * Helper function for LISTVIEW_RemoveSelectionRange, and LISTVIEW_SetItem.
2000 static BOOL
remove_selection_range(LISTVIEW_INFO
*infoPtr
, INT lower
, INT upper
, BOOL adj_sel_only
)
2002 RANGE remsel
, tmpsel
, *chksel
;
2008 lvItem
.stateMask
= LVIS_SELECTED
;
2010 remsel
.lower
= lower
;
2011 remsel
.upper
= upper
;
2013 TRACE("range: (%d - %d)\n", remsel
.lower
, remsel
.upper
);
2017 index
= DPA_Search(infoPtr
->hdpaSelectionRanges
, &remsel
, 0,
2018 LISTVIEW_CompareSelectionRanges
, 0, 0);
2019 if (index
== -1) return TRUE
;
2021 chksel
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
, index
);
2022 if (!chksel
) return FALSE
;
2024 TRACE("Matches range index %i (%d - %d)\n",
2025 index
, chksel
->lower
, chksel
->upper
);
2027 /* case 1: Same range */
2028 if ( (chksel
->upper
== remsel
.upper
) &&
2029 (chksel
->lower
== remsel
.lower
) )
2031 DPA_DeletePtr(infoPtr
->hdpaSelectionRanges
, index
);
2034 /* case 2: engulf */
2035 else if ( (chksel
->upper
<= remsel
.upper
) &&
2036 (chksel
->lower
>= remsel
.lower
) )
2038 DPA_DeletePtr(infoPtr
->hdpaSelectionRanges
, index
);
2040 /* case 3: overlap upper */
2041 else if ( (chksel
->upper
< remsel
.upper
) &&
2042 (chksel
->lower
< remsel
.lower
) )
2044 chksel
->upper
= remsel
.lower
- 1;
2046 /* case 4: overlap lower */
2047 else if ( (chksel
->upper
> remsel
.upper
) &&
2048 (chksel
->lower
> remsel
.lower
) )
2050 chksel
->lower
= remsel
.upper
+ 1;
2052 /* case 5: fully internal */
2056 (RANGE
*)COMCTL32_Alloc(sizeof(RANGE
));
2057 if (!newsel
) return FALSE
;
2059 newsel
->lower
= chksel
->lower
;
2060 newsel
->upper
= remsel
.lower
- 1;
2061 chksel
->lower
= remsel
.upper
+ 1;
2062 DPA_InsertPtr(infoPtr
->hdpaSelectionRanges
, index
, newsel
);
2063 /*DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);*/
2067 if (adj_sel_only
) continue;
2069 /* here, chksel holds the selection to delete */
2070 for (i
= chksel
->lower
; i
<= chksel
->upper
; i
++)
2071 LISTVIEW_SetItemState(infoPtr
, i
, &lvItem
);
2080 * Adds a selection range.
2083 * [I] infoPtr : valid pointer to the listview structure
2084 * [I] lower : lower item index
2085 * [I] upper : upper item index
2091 static inline BOOL
LISTVIEW_AddSelectionRange(LISTVIEW_INFO
*infoPtr
, INT lower
, INT upper
)
2093 return add_selection_range(infoPtr
, lower
, upper
, FALSE
);
2098 * Removes a range selections.
2101 * [I] infoPtr : valid pointer to the listview structure
2102 * [I] lower : lower item index
2103 * [I] upper : upper item index
2109 static inline BOOL
LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO
*infoPtr
, INT lower
, INT upper
)
2111 return remove_selection_range(infoPtr
, lower
, upper
, FALSE
);
2116 * Removes all selection ranges
2119 * [I] infoPtr : valid pointer to the listview structure
2125 static LRESULT
LISTVIEW_RemoveAllSelections(LISTVIEW_INFO
*infoPtr
)
2129 if (infoPtr
->bRemovingAllSelections
) return TRUE
;
2131 infoPtr
->bRemovingAllSelections
= TRUE
;
2137 sel
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
, 0);
2138 if (sel
) LISTVIEW_RemoveSelectionRange(infoPtr
, sel
->lower
, sel
->upper
);
2140 while (infoPtr
->hdpaSelectionRanges
->nItemCount
> 0);
2142 infoPtr
->bRemovingAllSelections
= FALSE
;
2149 * Retrieves the number of items that are marked as selected.
2152 * [I] infoPtr : valid pointer to the listview structure
2155 * Number of items selected.
2157 static INT
LISTVIEW_GetSelectedCount(LISTVIEW_INFO
*infoPtr
)
2159 INT i
, nSelectedCount
= 0;
2161 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
2164 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2166 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
2172 for (i
= 0; i
< infoPtr
->hdpaSelectionRanges
->nItemCount
; i
++)
2174 RANGE
*sel
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
, i
);
2175 nSelectedCount
+= sel
->upper
- sel
->lower
+ 1;
2179 TRACE("nSelectedCount=%d\n", nSelectedCount
);
2180 return nSelectedCount
;
2185 * Manages the item focus.
2188 * [I] infoPtr : valid pointer to the listview structure
2189 * [I] INT : item index
2192 * TRUE : focused item changed
2193 * FALSE : focused item has NOT changed
2195 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2197 INT oldFocus
= infoPtr
->nFocusedItem
;
2200 lvItem
.state
= LVIS_FOCUSED
;
2201 lvItem
.stateMask
= LVIS_FOCUSED
;
2202 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
2204 return oldFocus
!= infoPtr
->nFocusedItem
;
2209 * Updates the various indices after an item has been inserted or deleted.
2212 * [I] infoPtr : valid pointer to the listview structure
2213 * [I] nItem : item index
2214 * [I] direction : Direction of shift, +1 or -1.
2219 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
2221 RANGE selection
,*checkselection
;
2224 TRACE("Shifting %iu, %i steps\n",nItem
,direction
);
2226 selection
.upper
= nItem
;
2227 selection
.lower
= nItem
;
2229 index
= DPA_Search(infoPtr
->hdpaSelectionRanges
, &selection
, 0,
2230 LISTVIEW_CompareSelectionRanges
,
2231 0,DPAS_SORTED
|DPAS_INSERTAFTER
);
2233 while ((index
< infoPtr
->hdpaSelectionRanges
->nItemCount
)&&(index
!= -1))
2235 checkselection
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
,index
);
2236 if ((checkselection
->lower
>= nItem
)&&
2237 ((int)(checkselection
->lower
+ direction
) >= 0))
2238 checkselection
->lower
+= direction
;
2239 if ((checkselection
->upper
>= nItem
)&&
2240 ((int)(checkselection
->upper
+ direction
) >= 0))
2241 checkselection
->upper
+= direction
;
2245 /* Note that the following will fail if direction != +1 and -1 */
2246 if (infoPtr
->nSelectionMark
> nItem
)
2247 infoPtr
->nSelectionMark
+= direction
;
2248 else if (infoPtr
->nSelectionMark
== nItem
)
2251 infoPtr
->nSelectionMark
+= direction
;
2252 else if (infoPtr
->nSelectionMark
>= infoPtr
->nItemCount
)
2253 infoPtr
->nSelectionMark
= infoPtr
->nItemCount
- 1;
2256 if (infoPtr
->nFocusedItem
> nItem
)
2257 infoPtr
->nFocusedItem
+= direction
;
2258 else if (infoPtr
->nFocusedItem
== nItem
)
2261 infoPtr
->nFocusedItem
+= direction
;
2264 if (infoPtr
->nFocusedItem
>= infoPtr
->nItemCount
)
2265 infoPtr
->nFocusedItem
= infoPtr
->nItemCount
- 1;
2266 if (infoPtr
->nFocusedItem
>= 0)
2267 LISTVIEW_SetItemFocus(infoPtr
, infoPtr
->nFocusedItem
);
2270 /* But we are not supposed to modify nHotItem! */
2276 * Adds a block of selections.
2279 * [I] infoPtr : valid pointer to the listview structure
2280 * [I] INT : item index
2285 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2287 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
2288 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
2295 item
.state
= LVIS_SELECTED
;
2296 item
.stateMask
= LVIS_SELECTED
;
2298 /* FIXME: this is not correct LVS_OWNERDATA
2299 * See docu for LVN_ITEMCHANGED. Is there something similar for
2300 * RemoveGroupSelection (is there such a thing?)?
2302 for (i
= nFirst
; i
<= nLast
; i
++)
2303 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
2305 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
2306 infoPtr
->nSelectionMark
= nItem
;
2312 * Sets a single group selection.
2315 * [I] infoPtr : valid pointer to the listview structure
2316 * [I] INT : item index
2321 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2323 UINT uView
= LISTVIEW_GetType(infoPtr
);
2329 LISTVIEW_RemoveAllSelections(infoPtr
);
2331 item
.state
= LVIS_SELECTED
;
2332 item
.stateMask
= LVIS_SELECTED
;
2334 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
2338 if (infoPtr
->nSelectionMark
== -1)
2339 infoPtr
->nSelectionMark
= nFirst
= nLast
= nItem
;
2342 nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
2343 nLast
= max(infoPtr
->nSelectionMark
, nItem
);
2345 for (i
= nFirst
; i
<= nLast
; i
++)
2346 LISTVIEW_SetItemState(infoPtr
, i
, &item
);
2350 RECT rcItem
, rcSelMark
;
2352 rcItem
.left
= LVIR_BOUNDS
;
2353 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return;
2354 rcSelMark
.left
= LVIR_BOUNDS
;
2355 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) return;
2356 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
2357 for (i
= 0; i
<= infoPtr
->nItemCount
; i
++)
2359 LISTVIEW_GetItemPosition(infoPtr
, i
, &ptItem
);
2360 if (PtInRect(&rcSel
, ptItem
))
2361 LISTVIEW_SetItemState(infoPtr
, i
, &item
);
2365 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
2370 * Sets a single selection.
2373 * [I] infoPtr : valid pointer to the listview structure
2374 * [I] INT : item index
2379 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2383 TRACE("nItem=%d\n", nItem
);
2385 LISTVIEW_RemoveAllSelections(infoPtr
);
2387 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
2388 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
2389 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
2391 infoPtr
->nSelectionMark
= nItem
;
2396 * Set selection(s) with keyboard.
2399 * [I] infoPtr : valid pointer to the listview structure
2400 * [I] INT : item index
2403 * SUCCESS : TRUE (needs to be repainted)
2404 * FAILURE : FALSE (nothing has changed)
2406 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2408 /* FIXME: pass in the state */
2409 LONG lStyle
= infoPtr
->dwStyle
;
2410 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
2411 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
2412 BOOL bResult
= FALSE
;
2414 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
2416 if (lStyle
& LVS_SINGLESEL
)
2419 LISTVIEW_SetSelection(infoPtr
, nItem
);
2420 ListView_EnsureVisible(infoPtr
->hwndSelf
, nItem
, FALSE
);
2427 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
2431 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
2436 LISTVIEW_SetSelection(infoPtr
, nItem
);
2437 ListView_EnsureVisible(infoPtr
->hwndSelf
, nItem
, FALSE
);
2442 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
2448 * Selects an item based on coordinates.
2451 * [I] infoPtr : valid pointer to the listview structure
2452 * [I] pt : mouse click ccordinates
2455 * SUCCESS : item index
2458 static INT
LISTVIEW_GetItemAtPt(LISTVIEW_INFO
*infoPtr
, POINT pt
)
2464 visrange
= LISTVIEW_GetVisibleRange(infoPtr
);
2465 for (i
= visrange
.lower
; i
<= visrange
.upper
; i
++)
2467 rcItem
.left
= LVIR_SELECTBOUNDS
;
2468 if (LISTVIEW_GetItemRect(infoPtr
, i
, &rcItem
))
2470 TRACE("i=%d, rcItem=%s\n", i
, debugrect(&rcItem
));
2471 if (PtInRect(&rcItem
, pt
)) return i
;
2479 * Called when the mouse is being actively tracked and has hovered for a specified
2483 * [I] infoPtr : valid pointer to the listview structure
2484 * [I] fwKeys : key indicator
2485 * [I] pts : mouse position
2488 * 0 if the message was processed, non-zero if there was an error
2491 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2492 * over the item for a certain period of time.
2495 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, WORD fwKyes
, POINTS pts
)
2497 if(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)
2498 /* FIXME: select the item!!! */
2499 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2506 * Called whenever WM_MOUSEMOVE is received.
2509 * [I] infoPtr : valid pointer to the listview structure
2510 * [I] fwKeys : key indicator
2511 * [I] pts : mouse position
2514 * 0 if the message is processed, non-zero if there was an error
2516 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, POINTS pts
)
2518 TRACKMOUSEEVENT trackinfo
;
2520 /* see if we are supposed to be tracking mouse hovering */
2521 if(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) {
2522 /* fill in the trackinfo struct */
2523 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
2524 trackinfo
.dwFlags
= TME_QUERY
;
2525 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
2526 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
2528 /* see if we are already tracking this hwnd */
2529 _TrackMouseEvent(&trackinfo
);
2531 if(!(trackinfo
.dwFlags
& TME_HOVER
)) {
2532 trackinfo
.dwFlags
= TME_HOVER
;
2534 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2535 _TrackMouseEvent(&trackinfo
);
2544 * Tests wheather the item is assignable to a list with style lStyle
2546 static inline BOOL
is_assignable_item(LPLVITEMW lpLVItem
, LONG lStyle
)
2548 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
2549 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
2550 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
2557 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2560 * [I] infoPtr : valid pointer to the listview structure
2561 * [I] lpLVItem : valid pointer to new item atttributes
2562 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2568 static BOOL
set_owner_item(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
2570 LONG lStyle
= infoPtr
->dwStyle
;
2574 /* a virtual listview stores only the state for the main item */
2575 if (lpLVItem
->iSubItem
|| !(lpLVItem
->mask
& LVIF_STATE
)) return FALSE
;
2577 oldState
= LISTVIEW_GetItemState(infoPtr
, lpLVItem
->iItem
, LVIS_FOCUSED
| LVIS_SELECTED
);
2578 TRACE("oldState=%x, newState=%x, uCallbackMask=%x\n",
2579 oldState
, lpLVItem
->state
, infoPtr
->uCallbackMask
);
2581 /* we're done if we don't need to change anything we handle */
2582 if ( !((oldState
^ lpLVItem
->state
) & lpLVItem
->stateMask
&
2583 ~infoPtr
->uCallbackMask
& (LVIS_FOCUSED
| LVIS_SELECTED
))) return TRUE
;
2586 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2587 * by LVS_OWERNDATA list controls
2590 /* if we handle the focus, and we're asked to change it, do it now */
2591 if ( lpLVItem
->stateMask
& LVIS_FOCUSED
)
2593 if (lpLVItem
->state
& LVIS_FOCUSED
)
2594 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
2595 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
2596 infoPtr
->nFocusedItem
= -1;
2599 /* and the selection is the only other state a virtual list may hold */
2600 if (lpLVItem
->stateMask
& LVIS_SELECTED
)
2602 if (lpLVItem
->state
& LVIS_SELECTED
)
2604 if (lStyle
& LVS_SINGLESEL
) LISTVIEW_RemoveAllSelections(infoPtr
);
2605 add_selection_range(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iItem
, TRUE
);
2608 remove_selection_range(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iItem
, TRUE
);
2611 /* notify the parent now that things have changed */
2612 ZeroMemory(&nmlv
, sizeof(nmlv
));
2613 nmlv
.iItem
= lpLVItem
->iItem
;
2614 nmlv
.uNewState
= lpLVItem
->state
;
2615 nmlv
.uOldState
= oldState
;
2616 nmlv
.uChanged
= LVIF_STATE
;
2617 notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
2624 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2627 * [I] infoPtr : valid pointer to the listview structure
2628 * [I] lpLVItem : valid pointer to new item atttributes
2629 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2635 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
2637 LONG lStyle
= infoPtr
->dwStyle
;
2638 UINT uView
= lStyle
& LVS_TYPEMASK
;
2640 LISTVIEW_ITEM
*lpItem
;
2644 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
2645 if (!hdpaSubItems
&& hdpaSubItems
!= (HDPA
)-1) return FALSE
;
2647 lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0);
2648 if (!lpItem
) return FALSE
;
2650 /* determine what fields will change */
2651 if ((lpLVItem
->mask
& LVIF_STATE
) &&
2652 ((lpItem
->state
^ lpLVItem
->state
) & lpLVItem
->stateMask
))
2653 uChanged
|= LVIF_STATE
;
2655 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
2656 uChanged
|= LVIF_IMAGE
;
2658 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
2659 uChanged
|= LVIF_PARAM
;
2661 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
2662 uChanged
|= LVIF_INDENT
;
2664 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
2665 uChanged
|= LVIF_TEXT
;
2667 if (!uChanged
) return TRUE
;
2669 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
2670 nmlv
.iItem
= lpLVItem
->iItem
;
2671 nmlv
.uNewState
= lpLVItem
->state
& lpLVItem
->stateMask
;
2672 nmlv
.uOldState
= lpItem
->state
& lpLVItem
->stateMask
;
2673 nmlv
.uChanged
= uChanged
;
2674 nmlv
.lParam
= lpItem
->lParam
;
2676 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2677 if(lpItem
->valid
&& notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
2680 /* copy information */
2681 if (lpLVItem
->mask
& LVIF_TEXT
)
2682 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
2684 if (lpLVItem
->mask
& LVIF_IMAGE
)
2685 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
2687 if (lpLVItem
->mask
& LVIF_PARAM
)
2688 lpItem
->lParam
= lpLVItem
->lParam
;
2690 if (lpLVItem
->mask
& LVIF_INDENT
)
2691 lpItem
->iIndent
= lpLVItem
->iIndent
;
2693 if (uChanged
& LVIF_STATE
)
2695 lpItem
->state
&= ~lpLVItem
->stateMask
;
2696 lpItem
->state
|= (lpLVItem
->state
& lpLVItem
->stateMask
);
2697 if (nmlv
.uNewState
& LVIS_SELECTED
)
2699 if (lStyle
& LVS_SINGLESEL
) LISTVIEW_RemoveAllSelections(infoPtr
);
2700 add_selection_range(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iItem
, TRUE
);
2702 else if (lpLVItem
->stateMask
& LVIS_SELECTED
)
2703 remove_selection_range(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iItem
, TRUE
);
2705 /* if we are asked to change focus, and we manage it, do it */
2706 if (nmlv
.uNewState
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
2708 if (lpLVItem
->state
& LVIS_FOCUSED
)
2710 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
2711 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, FALSE
);
2713 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
2714 infoPtr
->nFocusedItem
= -1;
2718 /* if LVS_LIST or LVS_SMALLICON, update the width of the items */
2719 if((uChanged
& LVIF_TEXT
) && ((uView
== LVS_LIST
) || (uView
== LVS_SMALLICON
)))
2721 int item_width
= LISTVIEW_CalculateItemWidth(infoPtr
, lpLVItem
->iItem
);
2722 if(item_width
> infoPtr
->nItemWidth
) infoPtr
->nItemWidth
= item_width
;
2725 /* if we're inserting the item, we're done */
2726 if (!lpItem
->valid
) return TRUE
;
2728 /* send LVN_ITEMCHANGED notification */
2729 nmlv
.lParam
= lpItem
->lParam
;
2730 notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
2737 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2740 * [I] infoPtr : valid pointer to the listview structure
2741 * [I] lpLVItem : valid pointer to new subitem atttributes
2742 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2748 static BOOL
set_sub_item(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
2751 LISTVIEW_SUBITEM
*lpSubItem
;
2752 BOOL bModified
= FALSE
;
2754 /* set subitem only if column is present */
2755 if (Header_GetItemCount(infoPtr
->hwndHeader
) <= lpLVItem
->iSubItem
)
2758 /* First do some sanity checks */
2759 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
)) return FALSE
;
2760 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
))) return TRUE
;
2762 /* get the subitem structure, and create it if not there */
2763 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
2764 if (!hdpaSubItems
) return FALSE
;
2766 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
2769 LISTVIEW_SUBITEM
*tmpSubItem
;
2772 lpSubItem
= (LISTVIEW_SUBITEM
*)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM
));
2773 if (!lpSubItem
) return FALSE
;
2774 /* we could binary search here, if need be...*/
2775 for (i
= 1; i
< hdpaSubItems
->nItemCount
; i
++)
2777 tmpSubItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, i
);
2778 if (tmpSubItem
&& tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
2780 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
2782 COMCTL32_Free(lpSubItem
);
2785 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
2789 if (lpLVItem
->mask
& LVIF_IMAGE
)
2790 if (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
)
2792 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
2796 if (lpLVItem
->mask
& LVIF_TEXT
)
2797 if (lpSubItem
->hdr
.pszText
!= lpLVItem
->pszText
)
2799 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
2803 if (bModified
&& !infoPtr
->bIsDrawing
)
2807 rect
.left
= LVIR_BOUNDS
;
2808 rect
.top
= lpLVItem
->iSubItem
;
2809 /* GetSubItemRect will fail in non-report mode, so there's
2810 * gonna be no invalidation then, yay! */
2811 if (LISTVIEW_GetSubItemRect(infoPtr
, lpLVItem
->iItem
, &rect
))
2812 LISTVIEW_InvalidateRect(infoPtr
, &rect
);
2820 * Sets item attributes.
2823 * [I] infoPtr : valid pointer to the listview structure
2824 * [I] LPLVITEM : new item atttributes
2825 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2831 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
2833 INT oldFocus
= infoPtr
->nFocusedItem
;
2834 LPWSTR pszText
= NULL
;
2837 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
2839 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
2842 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
2843 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_textW(lpLVItem
->pszText
))
2845 pszText
= lpLVItem
->pszText
;
2846 lpLVItem
->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
2849 /* actually set the fields */
2850 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
2851 bResult
= set_owner_item(infoPtr
, lpLVItem
, TRUE
);
2854 /* sanity checks first */
2855 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
2857 if (lpLVItem
->iSubItem
)
2858 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
);
2860 bResult
= set_main_item(infoPtr
, lpLVItem
, TRUE
);
2863 /* redraw item, if necessary */
2864 if (bResult
&& !infoPtr
->bIsDrawing
&& lpLVItem
->iSubItem
== 0)
2866 if (oldFocus
!= infoPtr
->nFocusedItem
&& infoPtr
->bFocus
)
2867 LISTVIEW_ShowFocusRect(infoPtr
, oldFocus
, FALSE
);
2869 /* this little optimization eliminates some nasty flicker */
2870 if ( (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
&&
2871 !(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) &&
2872 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))
2876 rect
.left
= LVIR_BOUNDS
;
2878 if (LISTVIEW_GetSubItemRect(infoPtr
, lpLVItem
->iItem
, &rect
))
2879 LISTVIEW_InvalidateRect(infoPtr
, &rect
);
2882 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
2887 textfreeT(lpLVItem
->pszText
, isW
);
2888 lpLVItem
->pszText
= pszText
;
2896 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2899 * [I] infoPtr : valid pointer to the listview structure
2904 static INT
LISTVIEW_GetTopIndex(LISTVIEW_INFO
*infoPtr
)
2906 LONG lStyle
= infoPtr
->dwStyle
;
2907 UINT uView
= lStyle
& LVS_TYPEMASK
;
2909 SCROLLINFO scrollInfo
;
2911 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
2912 scrollInfo
.fMask
= SIF_POS
;
2914 if (uView
== LVS_LIST
)
2916 if ((lStyle
& WS_HSCROLL
) && GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
2917 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
2919 else if (uView
== LVS_REPORT
)
2921 if ((lStyle
& WS_VSCROLL
) && GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
2922 nItem
= scrollInfo
.nPos
;
2926 if ((lStyle
& WS_VSCROLL
) && GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
2927 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
2930 TRACE("nItem=%d\n", nItem
);
2935 /* used by the drawing code */
2936 typedef struct tagTEXTATTR
2943 /* helper function for the drawing code */
2944 static inline void set_text_attr(HDC hdc
, TEXTATTR
*ta
)
2946 ta
->bkMode
= SetBkMode(hdc
, ta
->bkMode
);
2947 ta
->bkColor
= SetBkColor(hdc
, ta
->bkColor
);
2948 ta
->fgColor
= SetTextColor(hdc
, ta
->fgColor
);
2951 /* helper function for the drawing code */
2952 static void select_text_attr(LISTVIEW_INFO
*infoPtr
, HDC hdc
, BOOL isSelected
, TEXTATTR
*ta
)
2954 ta
->bkMode
= OPAQUE
;
2956 if (isSelected
&& infoPtr
->bFocus
)
2958 ta
->bkColor
= comctl32_color
.clrHighlight
;
2959 ta
->fgColor
= comctl32_color
.clrHighlightText
;
2961 else if (isSelected
&& (infoPtr
->dwStyle
& LVS_SHOWSELALWAYS
))
2963 ta
->bkColor
= comctl32_color
.clr3dFace
;
2964 ta
->fgColor
= comctl32_color
.clrBtnText
;
2966 else if ( (infoPtr
->clrTextBk
!= CLR_DEFAULT
) && (infoPtr
->clrTextBk
!= CLR_NONE
) )
2968 ta
->bkColor
= infoPtr
->clrTextBk
;
2969 ta
->fgColor
= infoPtr
->clrText
;
2973 ta
->bkMode
= TRANSPARENT
;
2974 ta
->bkColor
= GetBkColor(hdc
);
2975 ta
->fgColor
= infoPtr
->clrText
;
2978 set_text_attr(hdc
, ta
);
2983 * Erases the background of the given rectangle
2986 * [I] infoPtr : valid pointer to the listview structure
2987 * [I] hdc : device context handle
2988 * [I] lprcBox : clipping rectangle
2994 static inline BOOL
LISTVIEW_FillBkgnd(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
* lprcBox
)
2996 if (!infoPtr
->hBkBrush
) return FALSE
;
2998 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc
, debugrect(lprcBox
), infoPtr
->hBkBrush
);
3000 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
3008 * [I] infoPtr : valid pointer to the listview structure
3009 * [I] HDC : device context handle
3010 * [I] INT : item index
3011 * [I] INT : subitem index
3012 * [I] RECT * : clipping rectangle
3018 static BOOL
LISTVIEW_DrawSubItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
,
3019 INT nSubItem
, RECT rcItem
, UINT align
)
3021 WCHAR szDispText
[DISP_TEXT_SIZE
];
3024 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, rcItem=%s)\n",
3025 hdc
, nItem
, nSubItem
, debugrect(&rcItem
));
3027 /* get information needed for drawing the item */
3028 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
;
3029 lvItem
.iItem
= nItem
;
3030 lvItem
.iSubItem
= nSubItem
;
3031 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3032 lvItem
.pszText
= szDispText
;
3033 *lvItem
.pszText
= '\0';
3034 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
3036 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3038 if (lvItem
.iImage
) FIXME("Draw the image for the subitem\n");
3040 DrawTextW(hdc
, lvItem
.pszText
, -1, &rcItem
,
3041 DT_SINGLELINE
| DT_VCENTER
| DT_WORD_ELLIPSIS
| align
);
3052 * [I] infoPtr : valid pointer to the listview structure
3053 * [I] hdc : device context handle
3054 * [I] nItem : item index
3055 * [I] rcItem : item rectangle
3058 * TRUE: if item is focused
3061 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, RECT rcItem
)
3063 WCHAR szDispText
[DISP_TEXT_SIZE
];
3064 INT nLabelWidth
, imagePadding
= 0;
3065 RECT
* lprcFocus
, rcOrig
= rcItem
;
3069 TRACE("(hdc=%x, nItem=%d)\n", hdc
, nItem
);
3071 /* get information needed for drawing the item */
3072 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
| LVIF_INDENT
;
3073 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
;
3074 lvItem
.iItem
= nItem
;
3075 lvItem
.iSubItem
= 0;
3076 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3077 lvItem
.pszText
= szDispText
;
3078 *lvItem
.pszText
= '\0';
3079 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
3080 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3082 /* now check if we need to update the focus rectangle */
3083 lprcFocus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
3084 if (lprcFocus
) SetRectEmpty(lprcFocus
);
3087 rcItem
.left
+= infoPtr
->iconSize
.cx
* lvItem
.iIndent
;
3090 if (infoPtr
->himlState
)
3092 UINT uStateImage
= (lvItem
.state
& LVIS_STATEIMAGEMASK
) >> 12;
3095 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
,
3096 rcItem
.left
, rcItem
.top
, ILD_NORMAL
);
3098 rcItem
.left
+= infoPtr
->iconStateSize
.cx
;
3099 imagePadding
= IMAGE_PADDING
;
3103 if (infoPtr
->himlSmall
)
3105 if (lvItem
.iImage
>= 0)
3107 UINT mode
= (lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
) ?
3108 ILD_SELECTED
: ILD_NORMAL
;
3109 ImageList_SetBkColor(infoPtr
->himlSmall
, CLR_NONE
);
3110 ImageList_Draw(infoPtr
->himlSmall
, lvItem
.iImage
, hdc
,
3111 rcItem
.left
, rcItem
.top
, mode
);
3113 rcItem
.left
+= infoPtr
->iconSize
.cx
;
3114 imagePadding
= IMAGE_PADDING
;
3117 /* Don't bother painting item being edited */
3118 if (infoPtr
->bEditing
&& lprcFocus
)
3121 select_text_attr(infoPtr
, hdc
, lvItem
.state
& LVIS_SELECTED
, &ta
);
3123 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
3124 rcItem
.left
+= imagePadding
;
3125 rcItem
.right
= rcItem
.left
+ nLabelWidth
+ TRAILING_PADDING
;
3126 if (rcItem
.right
> rcOrig
.right
) rcItem
.right
= rcOrig
.right
;
3130 TRACE("drawing text=%s, in rect=%s\n", debugstr_w(lvItem
.pszText
), debugrect(&rcItem
));
3131 if(lprcFocus
) *lprcFocus
= rcItem
;
3132 if (lvItem
.state
& LVIS_SELECTED
)
3133 ExtTextOutW(hdc
, rcItem
.left
, rcItem
.top
, ETO_OPAQUE
, &rcItem
, 0, 0, 0);
3134 DrawTextW(hdc
, lvItem
.pszText
, -1, &rcItem
,
3135 DT_SINGLELINE
| DT_VCENTER
| DT_WORD_ELLIPSIS
| DT_CENTER
);
3138 set_text_attr(hdc
, &ta
);
3139 return lprcFocus
!= NULL
;
3144 * Draws an item when in large icon display mode.
3147 * [I] infoPtr : valid pointer to the listview structure
3148 * [I] hdc : device context handle
3149 * [I] nItem : item index
3150 * [I] rcItem : clipping rectangle
3153 * TRUE: if item is focused
3156 static BOOL
LISTVIEW_DrawLargeItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, RECT rcItem
)
3158 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
3160 UINT uFormat
= LISTVIEW_DTFLAGS
;
3161 RECT rcIcon
, rcFocus
, rcLabel
, *lprcFocus
;
3163 TRACE("(hdc=%x, nItem=%d, rcItem=%s)\n", hdc
, nItem
, debugrect(&rcItem
));
3165 /* get information needed for drawing the item */
3166 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
;
3167 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
;
3168 lvItem
.iItem
= nItem
;
3169 lvItem
.iSubItem
= 0;
3170 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3171 lvItem
.pszText
= szDispText
;
3172 *lvItem
.pszText
= '\0';
3173 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
3174 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3176 /* now check if we need to update the focus rectangle */
3177 lprcFocus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
3179 if (!LISTVIEW_GetItemMeasures(infoPtr
, nItem
, NULL
, NULL
, &rcIcon
, &rcLabel
)) return FALSE
;
3181 /* Set the item to the boundary box for now */
3182 TRACE("rcIcon=%s, rcLabel=%s\n", debugrect(&rcIcon
), debugrect(&rcLabel
));
3184 /* Figure out text colours etc. depending on state
3185 * At least the following states exist; there may be more.
3186 * Many items may be selected
3187 * At most one item may have the focus
3188 * The application may not actually be active currently
3189 * 1. The item is not selected in any way
3190 * 2. The cursor is flying over the icon or text and the text is being
3191 * expanded because it is not fully displayed currently.
3192 * 3. The item is selected and is focussed, i.e. the user has not clicked
3193 * in the blank area of the window, and the window (or application?)
3194 * still has the focus.
3195 * 4. As 3 except that a different window has the focus
3196 * 5. The item is the selected item of all the items, but the user has
3197 * clicked somewhere else on the window.
3198 * Only a few of these are handled currently. In particular 2 is not yet
3199 * handled since we do not support the functionality currently (or at least
3200 * we didn't when I wrote this)
3203 if (lvItem
.state
& LVIS_SELECTED
)
3205 /* set item colors */
3206 SetBkColor(hdc
, comctl32_color
.clrHighlight
);
3207 SetTextColor(hdc
, comctl32_color
.clrHighlightText
);
3208 SetBkMode (hdc
, OPAQUE
);
3209 /* set raster mode */
3210 SetROP2(hdc
, R2_XORPEN
);
3211 /* When exactly is it in XOR? while being dragged? */
3215 /* set item colors */
3216 if ( (infoPtr
->clrTextBk
== CLR_DEFAULT
) || (infoPtr
->clrTextBk
== CLR_NONE
) )
3218 SetBkMode(hdc
, TRANSPARENT
);
3222 SetBkMode(hdc
, OPAQUE
);
3223 SetBkColor(hdc
, infoPtr
->clrTextBk
);
3226 SetTextColor(hdc
, infoPtr
->clrText
);
3227 /* set raster mode */
3228 SetROP2(hdc
, R2_COPYPEN
);
3231 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3232 * wrapping and long words split.
3233 * In cases 1 and 4 only a portion of the text is displayed with word
3234 * wrapping and both word and end ellipsis. (I don't yet know about path
3237 uFormat
|= lprcFocus
? DT_NOCLIP
: DT_WORD_ELLIPSIS
| DT_END_ELLIPSIS
;
3240 if (infoPtr
->himlState
!= NULL
)
3242 UINT uStateImage
= (lvItem
.state
& LVIS_STATEIMAGEMASK
) >> 12;
3245 x
= rcIcon
.left
- infoPtr
->iconStateSize
.cx
+ 10;
3246 y
= rcIcon
.top
+ infoPtr
->iconSize
.cy
- infoPtr
->iconStateSize
.cy
+ 4;
3247 if (uStateImage
> 0)
3248 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
, x
, y
, ILD_NORMAL
);
3252 if (infoPtr
->himlNormal
!= NULL
)
3254 if (lvItem
.iImage
>= 0)
3255 ImageList_Draw (infoPtr
->himlNormal
, lvItem
.iImage
, hdc
,
3256 rcIcon
.left
+ ICON_LR_HALF
, rcIcon
.top
+ ICON_TOP_PADDING
,
3257 (lvItem
.state
& LVIS_SELECTED
) ? ILD_SELECTED
: ILD_NORMAL
);
3260 /* Draw the text below the icon */
3262 /* Don't bother painting item being edited */
3263 if ((infoPtr
->bEditing
&& lprcFocus
) || !lvItem
.pszText
|| !lstrlenW(lvItem
.pszText
))
3265 if(lprcFocus
) SetRectEmpty(lprcFocus
);
3271 /* I am sure of most of the uFormat values. However I am not sure about
3272 * whether we need or do not need the following:
3273 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3274 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3275 * We certainly do not need
3276 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3277 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3280 /* If the text is being drawn without clipping (i.e. the full text) then we
3281 * need to jump through a few hoops to ensure that it all gets displayed and
3282 * that the background is complete
3284 rcFocus
= rcLabel
; /* save for focus */
3285 if (lvItem
.state
& LVIS_SELECTED
)
3286 ExtTextOutW(hdc
, rcLabel
.left
, rcLabel
.top
, ETO_OPAQUE
, &rcLabel
, 0, 0, 0);
3287 /* else ? What if we are losing the focus? will we not get a complete
3291 DrawTextW (hdc
, lvItem
.pszText
, -1, &rcLabel
, uFormat
);
3292 TRACE("text at rcLabel=%s is %s\n", debugrect(&rcLabel
), debugstr_w(lvItem
.pszText
));
3294 if(lprcFocus
) CopyRect(lprcFocus
, &rcFocus
);
3296 return lprcFocus
!= NULL
;
3301 * Draws listview items when in report display mode.
3304 * [I] infoPtr : valid pointer to the listview structure
3305 * [I] HDC : device context handle
3310 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD cdmode
)
3312 INT rgntype
, nDrawPosY
, j
;
3313 INT nTop
, nItem
, nLast
, nUpdateHeight
, nUpdateWidth
;
3314 INT nColumnCount
, nFirstCol
, nLastCol
;
3315 RECT rcItem
, rcClip
, rcFullSelect
;
3316 BOOL bFullSelected
, isFocused
;
3317 DWORD cditemmode
= CDRF_DODEFAULT
;
3318 LONG lStyle
= infoPtr
->dwStyle
;
3319 UINT uID
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_ID
);
3320 TEXTATTR tmpTa
, oldTa
;
3321 COLUMNCACHE
*lpCols
;
3328 /* figure out what to draw */
3329 rgntype
= GetClipBox(hdc
, &rcClip
);
3330 if (rgntype
== NULLREGION
) return;
3331 nUpdateHeight
= rcClip
.bottom
- rcClip
.top
+ 1;
3332 nUpdateWidth
= rcClip
.right
- rcClip
.left
;
3333 nTop
= LISTVIEW_GetTopIndex(infoPtr
);
3334 nItem
= nTop
+ (rcClip
.top
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
;
3337 nLast
= nItem
+ nUpdateHeight
/ infoPtr
->nItemHeight
;
3338 if (nUpdateHeight
% infoPtr
->nItemHeight
) nLast
++;
3339 if (nLast
> infoPtr
->nItemCount
)
3340 nLast
= infoPtr
->nItemCount
;
3342 /* send cache hint notification */
3343 if (lStyle
& LVS_OWNERDATA
)
3344 notify_odcachehint(infoPtr
, nItem
, nLast
);
3346 /* cache column info */
3347 nColumnCount
= Header_GetItemCount(infoPtr
->hwndHeader
);
3348 lpCols
= COMCTL32_Alloc(nColumnCount
* sizeof(COLUMNCACHE
));
3349 if (!lpCols
) return;
3350 for (j
= 0; j
< nColumnCount
; j
++)
3352 Header_GetItemRect(infoPtr
->hwndHeader
, j
, &lpCols
[j
].rc
);
3353 TRACE("lpCols[%d].rc=%s\n", j
, debugrect(&lpCols
[j
].rc
));
3356 /* Get scroll info once before loop */
3357 if (!LISTVIEW_GetOrigin(infoPtr
, &ptOrig
)) return;
3359 /* we now narrow the columns as well */
3360 nLastCol
= nColumnCount
- 1;
3361 for(nFirstCol
= 0; nFirstCol
< nColumnCount
; nFirstCol
++)
3362 if (lpCols
[nFirstCol
].rc
.right
+ ptOrig
.x
>= rcClip
.left
) break;
3363 for(nLastCol
= nColumnCount
- 1; nLastCol
>= 0; nLastCol
--)
3364 if (lpCols
[nLastCol
].rc
.left
+ ptOrig
.x
< rcClip
.right
) break;
3366 /* cache the per-column information before we start drawing */
3367 for (j
= nFirstCol
; j
<= nLastCol
; j
++)
3369 lvColumn
.mask
= LVCF_FMT
;
3370 LISTVIEW_GetColumnT(infoPtr
, j
, &lvColumn
, TRUE
);
3371 TRACE("lvColumn=%s\n", debuglvcolumn_t(&lvColumn
, TRUE
));
3372 lpCols
[j
].align
= DT_LEFT
;
3373 if (lvColumn
.fmt
& LVCFMT_RIGHT
)
3374 lpCols
[j
].align
= DT_RIGHT
;
3375 else if (lvColumn
.fmt
& LVCFMT_CENTER
)
3376 lpCols
[j
].align
= DT_CENTER
;
3379 /* a last few bits before we start drawing */
3380 TRACE("nTop=%d, nItem=%d, nLast=%d, nFirstCol=%d, nLastCol=%d\n",
3381 nTop
, nItem
, nLast
, nFirstCol
, nLastCol
);
3382 bFullSelected
= infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
;
3383 nDrawPosY
= infoPtr
->rcList
.top
+ (nItem
- nTop
) * infoPtr
->nItemHeight
;
3385 /* save dc values we're gonna trash while drawing */
3386 oldTa
.bkMode
= GetBkMode(hdc
);
3387 oldTa
.bkColor
= GetBkColor(hdc
);
3388 oldTa
.fgColor
= GetTextColor(hdc
);
3390 /* iterate through the invalidated rows */
3391 for (; nItem
< nLast
; nItem
++, nDrawPosY
+= infoPtr
->nItemHeight
)
3393 /* if owner wants to take a first stab at it, have it his way... */
3394 if (lStyle
& LVS_OWNERDRAWFIXED
)
3398 TRACE("Owner Drawn\n");
3402 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
3403 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
3404 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
3406 ZeroMemory(&dis
, sizeof(dis
));
3407 dis
.CtlType
= ODT_LISTVIEW
;
3410 dis
.itemAction
= ODA_DRAWENTIRE
;
3411 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
3412 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
3413 dis
.hwndItem
= infoPtr
->hwndSelf
;
3415 dis
.rcItem
.left
= lpCols
[0].rc
.left
;
3416 dis
.rcItem
.right
= lpCols
[nColumnCount
- 1].rc
.right
;
3417 dis
.rcItem
.top
= nDrawPosY
;
3418 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
3419 OffsetRect(&dis
.rcItem
, ptOrig
.x
, 0);
3420 dis
.itemData
= item
.lParam
;
3422 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), debugrect(&dis
.rcItem
));
3423 SendMessageW(GetParent(infoPtr
->hwndSelf
), WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
3424 /* In theory we should do the default drawing if WM_DRAWITEM
3425 * returns FALSE but, in the words of Larry McVoy, in practice
3426 * theory is different than practice, and hence there are
3427 * important apps out there that depend on no default drawing
3428 * in LVS_OWNERDRAWFIXED. So we always skip to the next item. */
3432 /* compute the full select rectangle, if needed */
3435 item
.mask
= LVIF_IMAGE
| LVIF_STATE
| LVIF_INDENT
;
3436 item
.stateMask
= LVIS_SELECTED
;
3439 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
3441 rcFullSelect
.left
= lpCols
[0].rc
.left
+ REPORT_MARGINX
+
3442 infoPtr
->iconSize
.cx
* item
.iIndent
+
3443 (infoPtr
->himlSmall
? infoPtr
->iconSize
.cx
: 0);
3444 rcFullSelect
.right
= max(rcFullSelect
.left
, lpCols
[nColumnCount
- 1].rc
.right
- REPORT_MARGINX
);
3445 rcFullSelect
.top
= nDrawPosY
;
3446 rcFullSelect
.bottom
= rcFullSelect
.top
+ infoPtr
->nItemHeight
;
3447 OffsetRect(&rcFullSelect
, ptOrig
.x
, 0);
3450 /* draw the background of the selection rectangle, if need be */
3451 select_text_attr(infoPtr
, hdc
, bFullSelected
&& (item
.state
& LVIS_SELECTED
), &tmpTa
);
3452 if (bFullSelected
&& (item
.state
& LVIS_SELECTED
))
3453 ExtTextOutW(hdc
, rcFullSelect
.left
, rcFullSelect
.top
, ETO_OPAQUE
, &rcFullSelect
, 0, 0, 0);
3455 /* iterate through the invalidated columns */
3457 for (j
= nFirstCol
; j
<= nLastCol
; j
++)
3459 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3460 cditemmode
= notify_customdrawitem (infoPtr
, hdc
, nItem
, j
, CDDS_ITEMPREPAINT
);
3461 if (cditemmode
& CDRF_SKIPDEFAULT
) continue;
3463 rcItem
= lpCols
[j
].rc
;
3464 rcItem
.left
+= REPORT_MARGINX
;
3465 rcItem
.right
= max(rcItem
.left
, rcItem
.right
- REPORT_MARGINX
);
3466 rcItem
.top
= nDrawPosY
;
3467 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
3469 /* Offset the Scroll Bar Pos */
3470 OffsetRect(&rcItem
, ptOrig
.x
, 0);
3473 isFocused
= LISTVIEW_DrawItem(infoPtr
, hdc
, nItem
, rcItem
);
3475 LISTVIEW_DrawSubItem(infoPtr
, hdc
, nItem
, j
, rcItem
, lpCols
[j
].align
);
3477 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3478 notify_customdrawitem(infoPtr
, hdc
, nItem
, 0, CDDS_ITEMPOSTPAINT
);
3481 /* Adjust focus if we have it, and we are in full select */
3482 if (bFullSelected
&& isFocused
) infoPtr
->rcFocus
= rcFullSelect
;
3485 /* cleanup the mess */
3486 set_text_attr(hdc
, &oldTa
);
3487 COMCTL32_Free(lpCols
);
3492 * Draws listview items when in list display mode.
3495 * [I] infoPtr : valid pointer to the listview structure
3496 * [I] HDC : device context handle
3501 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD cdmode
)
3507 INT nCountPerColumn
;
3508 INT nItemWidth
= infoPtr
->nItemWidth
;
3509 INT nItemHeight
= infoPtr
->nItemHeight
;
3510 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
3511 DWORD cditemmode
= CDRF_DODEFAULT
;
3513 /* get number of fully visible columns */
3514 nColumnCount
= nListWidth
/ nItemWidth
;
3515 if (nListWidth
% nItemWidth
) nColumnCount
++;
3516 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
3517 nItem
= ListView_GetTopIndex(infoPtr
->hwndSelf
);
3518 TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3519 nColumnCount
, nCountPerColumn
, nItem
);
3521 for (i
= 0; i
< nColumnCount
; i
++)
3523 for (j
= 0; j
< nCountPerColumn
; j
++, nItem
++)
3525 if (nItem
>= infoPtr
->nItemCount
)
3528 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3529 cditemmode
= notify_customdrawitem (infoPtr
, hdc
, nItem
, 0, CDDS_ITEMPREPAINT
);
3530 if (cditemmode
& CDRF_SKIPDEFAULT
)
3533 rcItem
.top
= j
* nItemHeight
;
3534 rcItem
.left
= i
* nItemWidth
;
3535 rcItem
.bottom
= rcItem
.top
+ nItemHeight
;
3536 rcItem
.right
= rcItem
.left
+ nItemWidth
;
3538 LISTVIEW_DrawItem(infoPtr
, hdc
, nItem
, rcItem
);
3540 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3541 notify_customdrawitem(infoPtr
, hdc
, nItem
, 0, CDDS_ITEMPOSTPAINT
);
3549 * Draws listview items when in icon or small icon display mode.
3552 * [I] infoPtr : valid pointer to the listview structure
3553 * [I] HDC : device context handle
3558 static void LISTVIEW_RefreshIcon(LISTVIEW_INFO
*infoPtr
, HDC hdc
, BOOL bSmall
, DWORD cdmode
)
3561 RECT rcItem
, rcClip
, rcTemp
;
3563 DWORD cditemmode
= CDRF_DODEFAULT
;
3567 GetClipBox(hdc
, &rcClip
);
3569 /* Draw the visible non-selected items */
3570 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
3572 if (LISTVIEW_GetItemState(infoPtr
,i
,LVIS_SELECTED
))
3575 rcItem
.left
= LVIR_BOUNDS
;
3576 LISTVIEW_GetItemRect(infoPtr
, i
, &rcItem
);
3577 if (!IntersectRect(&rcTemp
, &rcItem
, &rcClip
))
3580 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3581 cditemmode
= notify_customdrawitem (infoPtr
, hdc
, i
, 0, CDDS_ITEMPREPAINT
);
3582 if (cditemmode
& CDRF_SKIPDEFAULT
)
3585 LISTVIEW_GetItemPosition(infoPtr
, i
, &ptPosition
);
3587 if (ptPosition
.y
+ infoPtr
->nItemHeight
> infoPtr
->rcList
.top
)
3589 if (ptPosition
.x
+ infoPtr
->nItemWidth
> infoPtr
->rcList
.left
)
3591 if (ptPosition
.y
< infoPtr
->rcList
.bottom
)
3593 if (ptPosition
.x
< infoPtr
->rcList
.right
)
3595 rcItem
.top
= ptPosition
.y
;
3596 rcItem
.left
= ptPosition
.x
;
3597 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
3598 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
3601 LISTVIEW_DrawItem(infoPtr
, hdc
, i
, rcItem
);
3603 LISTVIEW_DrawLargeItem(infoPtr
, hdc
, i
, rcItem
);
3608 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3609 notify_customdrawitem(infoPtr
, hdc
, i
, 0, CDDS_ITEMPOSTPAINT
);
3612 /* Draw the visible selected items */
3613 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
3615 if (!LISTVIEW_GetItemState(infoPtr
,i
,LVIS_SELECTED
))
3618 rcItem
.left
= LVIR_BOUNDS
;
3619 LISTVIEW_GetItemRect(infoPtr
, i
, &rcItem
);
3620 if (!IntersectRect(&rcTemp
, &rcItem
, &rcClip
))
3623 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3624 cditemmode
= notify_customdrawitem (infoPtr
, hdc
, i
, 0, CDDS_ITEMPREPAINT
);
3625 if (cditemmode
& CDRF_SKIPDEFAULT
)
3628 LISTVIEW_GetItemPosition(infoPtr
, i
, &ptPosition
);
3630 if (ptPosition
.y
+ infoPtr
->nItemHeight
> infoPtr
->rcList
.top
)
3632 if (ptPosition
.x
+ infoPtr
->nItemWidth
> infoPtr
->rcList
.left
)
3634 if (ptPosition
.y
< infoPtr
->rcList
.bottom
)
3636 if (ptPosition
.x
< infoPtr
->rcList
.right
)
3638 rcItem
.top
= ptPosition
.y
;
3639 rcItem
.left
= ptPosition
.x
;
3640 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
3641 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
3644 LISTVIEW_DrawItem(infoPtr
, hdc
, i
, rcItem
);
3646 LISTVIEW_DrawLargeItem(infoPtr
, hdc
, i
, rcItem
);
3651 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3652 notify_customdrawitem(infoPtr
, hdc
, i
, 0, CDDS_ITEMPOSTPAINT
);
3658 * Draws listview items.
3661 * [I] infoPtr : valid pointer to the listview structure
3662 * [I] HDC : device context handle
3667 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
3669 UINT uView
= LISTVIEW_GetType(infoPtr
);
3674 LISTVIEW_DUMP(infoPtr
);
3676 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
3678 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, hdc
, rcClient
);
3679 if (cdmode
== CDRF_SKIPDEFAULT
) return;
3681 infoPtr
->bIsDrawing
= TRUE
;
3683 /* nothing to draw */
3684 if(infoPtr
->nItemCount
== 0) goto enddraw
;
3687 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
3689 if (uView
== LVS_LIST
)
3690 LISTVIEW_RefreshList(infoPtr
, hdc
, cdmode
);
3691 else if (uView
== LVS_REPORT
)
3692 LISTVIEW_RefreshReport(infoPtr
, hdc
, cdmode
);
3694 LISTVIEW_RefreshIcon(infoPtr
, hdc
, uView
== LVS_SMALLICON
, cdmode
);
3696 /* if we have a focus rect, draw it */
3697 if (infoPtr
->bFocus
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))
3698 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
3700 /* unselect objects */
3701 SelectObject(hdc
, hOldFont
);
3704 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
3705 notify_customdraw(infoPtr
, CDDS_POSTPAINT
, hdc
, rcClient
);
3707 infoPtr
->bIsDrawing
= FALSE
;
3713 * Calculates the approximate width and height of a given number of items.
3716 * [I] infoPtr : valid pointer to the listview structure
3717 * [I] INT : number of items
3722 * Returns a DWORD. The width in the low word and the height in high word.
3724 static LRESULT
LISTVIEW_ApproximateViewRect(LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
3725 WORD wWidth
, WORD wHeight
)
3727 UINT uView
= LISTVIEW_GetType(infoPtr
);
3728 INT nItemCountPerColumn
= 1;
3729 INT nColumnCount
= 0;
3730 DWORD dwViewRect
= 0;
3732 if (nItemCount
== -1)
3733 nItemCount
= infoPtr
->nItemCount
;
3735 if (uView
== LVS_LIST
)
3737 if (wHeight
== 0xFFFF)
3739 /* use current height */
3740 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
3743 if (wHeight
< infoPtr
->nItemHeight
)
3744 wHeight
= infoPtr
->nItemHeight
;
3748 if (infoPtr
->nItemHeight
> 0)
3750 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
3751 if (nItemCountPerColumn
== 0)
3752 nItemCountPerColumn
= 1;
3754 if (nItemCount
% nItemCountPerColumn
!= 0)
3755 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
3757 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
3761 /* Microsoft padding magic */
3762 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
3763 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
3765 dwViewRect
= MAKELONG(wWidth
, wHeight
);
3767 else if (uView
== LVS_REPORT
)
3768 FIXME("uView == LVS_REPORT: not implemented\n");
3769 else if (uView
== LVS_SMALLICON
)
3770 FIXME("uView == LVS_SMALLICON: not implemented\n");
3771 else if (uView
== LVS_ICON
)
3772 FIXME("uView == LVS_ICON: not implemented\n");
3779 * Arranges listview items in icon display mode.
3782 * [I] infoPtr : valid pointer to the listview structure
3783 * [I] INT : alignment code
3789 static LRESULT
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
3791 UINT uView
= LISTVIEW_GetType(infoPtr
);
3792 BOOL bResult
= FALSE
;
3794 if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
3799 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3802 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3805 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3807 case LVA_SNAPTOGRID
:
3808 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3816 /* << LISTVIEW_CreateDragImage >> */
3821 * Removes all listview items and subitems.
3824 * [I] infoPtr : valid pointer to the listview structure
3830 static LRESULT
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
)
3832 LONG lStyle
= infoPtr
->dwStyle
;
3833 UINT uView
= lStyle
& LVS_TYPEMASK
;
3834 LISTVIEW_ITEM
*lpItem
;
3835 LISTVIEW_SUBITEM
*lpSubItem
;
3838 BOOL bResult
= FALSE
;
3843 LISTVIEW_RemoveAllSelections(infoPtr
);
3844 infoPtr
->nSelectionMark
=-1;
3845 infoPtr
->nFocusedItem
=-1;
3846 /* But we are supposed to leave nHotItem as is! */
3848 if (lStyle
& LVS_OWNERDATA
)
3850 infoPtr
->nItemCount
= 0;
3851 LISTVIEW_InvalidateList(infoPtr
);
3855 if (infoPtr
->nItemCount
> 0)
3859 /* send LVN_DELETEALLITEMS notification */
3860 /* verify if subsequent LVN_DELETEITEM notifications should be
3862 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
3864 bSuppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
3866 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
3868 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
3869 if (hdpaSubItems
!= NULL
)
3871 for (j
= 1; j
< hdpaSubItems
->nItemCount
; j
++)
3873 lpSubItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, j
);
3874 if (lpSubItem
!= NULL
)
3876 /* free subitem string */
3877 if (is_textW(lpSubItem
->hdr
.pszText
))
3878 COMCTL32_Free(lpSubItem
->hdr
.pszText
);
3881 COMCTL32_Free(lpSubItem
);
3885 lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0);
3890 /* send LVN_DELETEITEM notification */
3892 nmlv
.lParam
= lpItem
->lParam
;
3893 notify_listview(infoPtr
, LVN_DELETEITEM
, &nmlv
);
3896 /* free item string */
3897 if (is_textW(lpItem
->hdr
.pszText
))
3898 COMCTL32_Free(lpItem
->hdr
.pszText
);
3901 COMCTL32_Free(lpItem
);
3904 DPA_Destroy(hdpaSubItems
);
3908 /* reinitialize listview memory */
3909 bResult
= DPA_DeleteAllPtrs(infoPtr
->hdpaItems
);
3910 infoPtr
->nItemCount
= 0;
3911 DPA_DeleteAllPtrs(infoPtr
->hdpaPosX
);
3912 DPA_DeleteAllPtrs(infoPtr
->hdpaPosY
);
3914 /* align items (set position of each item) */
3915 if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
3917 if (lStyle
& LVS_ALIGNLEFT
)
3919 LISTVIEW_AlignLeft(infoPtr
);
3923 LISTVIEW_AlignTop(infoPtr
);
3927 LISTVIEW_UpdateScroll(infoPtr
);
3929 LISTVIEW_InvalidateList(infoPtr
);
3937 * Removes a column from the listview control.
3940 * [I] infoPtr : valid pointer to the listview structure
3941 * [I] INT : column index
3947 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
3949 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3952 TRACE("nColumn=%d\n", nColumn
);
3954 if (nColumn
<= 0) return FALSE
;
3956 if (uView
== LVS_REPORT
)
3958 if (!Header_GetItemRect(infoPtr
->hwndHeader
, nColumn
, &rcCol
))
3961 if (!Header_DeleteItem(infoPtr
->hwndHeader
, nColumn
))
3965 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
3967 LISTVIEW_SUBITEM
*lpSubItem
, *lpDelItem
;
3969 INT nItem
, nSubItem
, i
;
3971 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
3973 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
3974 if (!hdpaSubItems
) continue;
3977 for (i
= 1; i
< hdpaSubItems
->nItemCount
; i
++)
3979 lpSubItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, i
);
3980 if (!lpSubItem
) break;
3981 if (lpSubItem
->iSubItem
== nColumn
)
3984 lpDelItem
= lpSubItem
;
3986 else if (lpSubItem
->iSubItem
> nColumn
)
3988 lpSubItem
->iSubItem
--;
3992 /* if we found our subitem, zapp it */
3996 if (is_textW(lpDelItem
->hdr
.pszText
))
3997 COMCTL32_Free(lpDelItem
->hdr
.pszText
);
4000 COMCTL32_Free(lpDelItem
);
4002 /* free dpa memory */
4003 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
4008 /* we need to worry about display issues in report mode only */
4009 if (uView
!= LVS_REPORT
) return TRUE
;
4011 /* if we have a focus, must first erase the focus rect */
4012 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
4014 /* Need to reset the item width when deleting a column */
4015 infoPtr
->nItemWidth
-= rcCol
.right
- rcCol
.left
;
4017 /* update scrollbar(s) */
4018 LISTVIEW_UpdateScroll(infoPtr
);
4020 /* scroll to cover the deleted column, and invalidate for redraw */
4021 rcOld
= infoPtr
->rcList
;
4022 rcOld
.left
= rcCol
.left
;
4023 ScrollWindowEx(infoPtr
->hwndSelf
, -(rcCol
.right
- rcCol
.left
), 0,
4024 &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4026 /* we can restore focus now */
4027 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, infoPtr
->nFocusedItem
, TRUE
);
4034 * Removes an item from the listview control.
4037 * [I] infoPtr : valid pointer to the listview structure
4038 * [I] INT : item index
4044 static LRESULT
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
4046 LONG lStyle
= infoPtr
->dwStyle
;
4047 UINT uView
= lStyle
& LVS_TYPEMASK
;
4048 LONG lCtrlId
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_ID
);
4050 BOOL bResult
= FALSE
;
4052 LISTVIEW_ITEM
*lpItem
;
4053 LISTVIEW_SUBITEM
*lpSubItem
;
4057 TRACE("(nItem=%d)\n", nItem
);
4060 /* First, send LVN_DELETEITEM notification. */
4061 memset(&nmlv
, 0, sizeof (NMLISTVIEW
));
4062 nmlv
.hdr
.hwndFrom
= infoPtr
->hwndSelf
;
4063 nmlv
.hdr
.idFrom
= lCtrlId
;
4064 nmlv
.hdr
.code
= LVN_DELETEITEM
;
4066 SendMessageW((infoPtr
->hwndSelf
), WM_NOTIFY
, (WPARAM
)lCtrlId
,
4070 /* remove it from the selection range */
4071 item
.state
= LVIS_SELECTED
;
4072 item
.stateMask
= LVIS_SELECTED
;
4073 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
4075 if (lStyle
& LVS_OWNERDATA
)
4077 infoPtr
->nItemCount
--;
4078 LISTVIEW_InvalidateList(infoPtr
); /*FIXME: optimize */
4082 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
4084 /* initialize memory */
4085 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4087 hdpaSubItems
= (HDPA
)DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
4088 if (hdpaSubItems
!= NULL
)
4090 infoPtr
->nItemCount
--;
4091 for (i
= 1; i
< hdpaSubItems
->nItemCount
; i
++)
4093 lpSubItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, i
);
4094 if (lpSubItem
!= NULL
)
4096 /* free item string */
4097 if (is_textW(lpSubItem
->hdr
.pszText
))
4098 COMCTL32_Free(lpSubItem
->hdr
.pszText
);
4101 COMCTL32_Free(lpSubItem
);
4105 lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0);
4108 /* free item string */
4109 if (is_textW(lpItem
->hdr
.pszText
))
4110 COMCTL32_Free(lpItem
->hdr
.pszText
);
4113 COMCTL32_Free(lpItem
);
4116 bResult
= DPA_Destroy(hdpaSubItems
);
4117 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
4118 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
4121 LISTVIEW_ShiftIndices(infoPtr
,nItem
,-1);
4123 /* align items (set position of each item) */
4124 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
4126 if (lStyle
& LVS_ALIGNLEFT
)
4127 LISTVIEW_AlignLeft(infoPtr
);
4129 LISTVIEW_AlignTop(infoPtr
);
4132 LISTVIEW_UpdateScroll(infoPtr
);
4134 LISTVIEW_InvalidateList(infoPtr
); /* FIXME: optimize */
4143 * Callback implementation for editlabel control
4146 * [I] infoPtr : valid pointer to the listview structure
4147 * [I] pszText : modified text
4148 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4154 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, LPWSTR pszText
, BOOL isW
)
4156 NMLVDISPINFOW dispInfo
;
4158 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
4160 infoPtr
->bEditing
= FALSE
;
4162 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4163 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4164 dispInfo
.item
.iItem
= infoPtr
->nEditLabelItem
;
4165 dispInfo
.item
.iSubItem
= 0;
4166 dispInfo
.item
.stateMask
= ~0;
4167 if (!LISTVIEW_GetItemW(infoPtr
, &dispInfo
.item
)) return FALSE
;
4168 dispInfo
.item
.pszText
= pszText
;
4169 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4171 /* Do we need to update the Item Text */
4172 if (!notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
)) return FALSE
;
4173 if (!pszText
) return TRUE
;
4175 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4176 dispInfo
.item
.mask
= LVIF_TEXT
;
4177 dispInfo
.item
.iItem
= infoPtr
->nEditLabelItem
;
4178 dispInfo
.item
.iSubItem
= 0;
4179 dispInfo
.item
.pszText
= pszText
;
4180 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4181 return LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
4186 * Begin in place editing of specified list view item
4189 * [I] infoPtr : valid pointer to the listview structure
4190 * [I] INT : item index
4191 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4197 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
4199 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
4200 NMLVDISPINFOW dispInfo
;
4203 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
4205 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
4207 infoPtr
->nEditLabelItem
= nItem
;
4209 /* Is the EditBox still there, if so remove it */
4210 if(infoPtr
->hwndEdit
!= 0)
4212 SetFocus(infoPtr
->hwndSelf
);
4213 infoPtr
->hwndEdit
= 0;
4216 LISTVIEW_SetSelection(infoPtr
, nItem
);
4217 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
4219 rect
.left
= LVIR_LABEL
;
4220 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
4222 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4223 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4224 dispInfo
.item
.iItem
= nItem
;
4225 dispInfo
.item
.iSubItem
= 0;
4226 dispInfo
.item
.stateMask
= ~0;
4227 dispInfo
.item
.pszText
= szDispText
;
4228 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
4229 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
4231 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, WS_VISIBLE
,
4232 rect
.left
-2, rect
.top
-1, 0, rect
.bottom
- rect
.top
+2, isW
);
4233 if (!infoPtr
->hwndEdit
) return 0;
4235 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
4237 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
4238 infoPtr
->hwndEdit
= 0;
4242 infoPtr
->bEditing
= TRUE
;
4243 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
4244 SetFocus(infoPtr
->hwndEdit
);
4245 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
4246 return infoPtr
->hwndEdit
;
4252 * Ensures the specified item is visible, scrolling into view if necessary.
4255 * [I] infoPtr : valid pointer to the listview structure
4256 * [I] nItem : item index
4257 * [I] bPartial : partially or entirely visible
4263 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
4265 UINT uView
= LISTVIEW_GetType(infoPtr
);
4266 INT nScrollPosHeight
= 0;
4267 INT nScrollPosWidth
= 0;
4268 INT nHorzAdjust
= 0;
4269 INT nVertAdjust
= 0;
4274 /* FIXME: ALWAYS bPartial == FALSE, FOR NOW! */
4276 rcItem
.left
= LVIR_BOUNDS
;
4277 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
4279 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
4281 /* scroll left/right, but in LVS_REPORT mode */
4282 if (uView
== LVS_LIST
)
4283 nScrollPosWidth
= infoPtr
->nItemWidth
;
4284 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
4285 nScrollPosWidth
= 1;
4287 if (rcItem
.left
< infoPtr
->rcList
.left
)
4290 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
4295 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
4299 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
4301 /* scroll up/down, but not in LVS_LIST mode */
4302 if (uView
== LVS_REPORT
)
4303 nScrollPosHeight
= infoPtr
->nItemHeight
;
4304 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
4305 nScrollPosHeight
= 1;
4307 if (rcItem
.top
< infoPtr
->rcList
.top
)
4310 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
4315 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
4319 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
4321 if (nScrollPosWidth
)
4323 INT diff
= nHorzDiff
/ nScrollPosWidth
;
4324 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
4325 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
4328 if (nScrollPosHeight
)
4330 INT diff
= nVertDiff
/ nScrollPosHeight
;
4331 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
4332 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
4340 * Retrieves the nearest item, given a position and a direction.
4343 * [I] infoPtr : valid pointer to the listview structure
4344 * [I] POINT : start position
4345 * [I] UINT : direction
4348 * Item index if successdful, -1 otherwise.
4350 static INT
LISTVIEW_GetNearestItem(LISTVIEW_INFO
*infoPtr
, POINT pt
, UINT vkDirection
)
4355 TRACE("point %ld,%ld, direction %s\n", pt
.x
, pt
.y
,
4356 (vkDirection
== VK_DOWN
) ? "VK_DOWN" :
4357 ((vkDirection
== VK_UP
) ? "VK_UP" :
4358 ((vkDirection
== VK_LEFT
) ? "VK_LEFT" : "VK_RIGHT")));
4360 if (!LISTVIEW_GetViewRect(infoPtr
, &rcView
)) return -1;
4362 if (!LISTVIEW_GetOrigin(infoPtr
, &ht
.pt
)) return -1;
4367 if (vkDirection
== VK_DOWN
) ht
.pt
.y
+= infoPtr
->nItemHeight
;
4368 else if (vkDirection
== VK_UP
) ht
.pt
.y
-= infoPtr
->nItemHeight
;
4369 else if (vkDirection
== VK_LEFT
) ht
.pt
.x
-= infoPtr
->nItemWidth
;
4370 else if (vkDirection
== VK_RIGHT
) ht
.pt
.x
+= infoPtr
->nItemWidth
;
4372 if (!PtInRect(&rcView
, ht
.pt
)) return -1;
4374 return LISTVIEW_SuperHitTestItem(infoPtr
, &ht
, TRUE
, TRUE
);
4379 * Searches for an item with specific characteristics.
4382 * [I] hwnd : window handle
4383 * [I] nStart : base item index
4384 * [I] lpFindInfo : item information to look for
4387 * SUCCESS : index of item
4390 static LRESULT
LISTVIEW_FindItemW(LISTVIEW_INFO
*infoPtr
, INT nStart
,
4391 LPLVFINDINFOW lpFindInfo
)
4394 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
4398 INT nLast
= infoPtr
->nItemCount
;
4400 if ((nItem
>= -1) && (lpFindInfo
!= NULL
))
4403 if (lpFindInfo
->flags
& LVFI_PARAM
)
4405 lvItem
.mask
|= LVIF_PARAM
;
4408 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
))
4410 lvItem
.mask
|= LVIF_TEXT
;
4411 lvItem
.pszText
= szDispText
;
4412 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4415 if (lpFindInfo
->flags
& LVFI_WRAP
)
4418 if (lpFindInfo
->flags
& LVFI_NEARESTXY
)
4420 ptItem
.x
= lpFindInfo
->pt
.x
;
4421 ptItem
.y
= lpFindInfo
->pt
.y
;
4426 while (nItem
< nLast
)
4428 if (lpFindInfo
->flags
& LVFI_NEARESTXY
)
4430 nItem
= LISTVIEW_GetNearestItem(infoPtr
, ptItem
,
4431 lpFindInfo
->vkDirection
);
4434 /* get position of the new item index */
4435 if (!ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &ptItem
))
4446 lvItem
.iItem
= nItem
;
4447 lvItem
.iSubItem
= 0;
4448 if (LISTVIEW_GetItemW(infoPtr
, &lvItem
))
4450 if (lvItem
.mask
& LVIF_TEXT
)
4452 if (lpFindInfo
->flags
& LVFI_PARTIAL
)
4454 if (strstrW(lvItem
.pszText
, lpFindInfo
->psz
) == NULL
)
4459 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0)
4464 if (lvItem
.mask
& LVIF_PARAM
)
4466 if (lpFindInfo
->lParam
!= lvItem
.lParam
)
4492 * Searches for an item with specific characteristics.
4495 * [I] hwnd : window handle
4496 * [I] nStart : base item index
4497 * [I] lpFindInfo : item information to look for
4500 * SUCCESS : index of item
4503 static LRESULT
LISTVIEW_FindItemA(LISTVIEW_INFO
*infoPtr
, INT nStart
,
4504 LPLVFINDINFOA lpFindInfo
)
4506 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
);
4510 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
4511 if (hasText
) fiw
.psz
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
4512 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
4513 if (hasText
) textfreeT((LPWSTR
)fiw
.psz
, FALSE
);
4519 * Retrieves the background image of the listview control.
4522 * [I] infoPtr : valid pointer to the listview structure
4523 * [O] LPLVMKBIMAGE : background image attributes
4529 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4531 /* FIXME (listview, "empty stub!\n"); */
4537 * Retrieves column attributes.
4540 * [I] infoPtr : valid pointer to the listview structure
4541 * [I] INT : column index
4542 * [IO] LPLVCOLUMNW : column information
4543 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4544 * otherwise it is in fact a LPLVCOLUMNA
4550 static LRESULT
LISTVIEW_GetColumnT(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVCOLUMNW lpColumn
, BOOL isW
)
4553 BOOL bResult
= FALSE
;
4555 if (lpColumn
!= NULL
)
4558 /* initialize memory */
4559 ZeroMemory(&hdi
, sizeof(hdi
));
4561 if (lpColumn
->mask
& LVCF_FMT
)
4562 hdi
.mask
|= HDI_FORMAT
;
4564 if (lpColumn
->mask
& LVCF_WIDTH
)
4565 hdi
.mask
|= HDI_WIDTH
;
4567 if (lpColumn
->mask
& LVCF_TEXT
)
4569 hdi
.mask
|= HDI_TEXT
;
4570 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
4571 hdi
.pszText
= lpColumn
->pszText
;
4574 if (lpColumn
->mask
& LVCF_IMAGE
)
4575 hdi
.mask
|= HDI_IMAGE
;
4577 if (lpColumn
->mask
& LVCF_ORDER
)
4578 hdi
.mask
|= HDI_ORDER
;
4581 bResult
= Header_GetItemW(infoPtr
->hwndHeader
, nItem
, &hdi
);
4583 bResult
= Header_GetItemA(infoPtr
->hwndHeader
, nItem
, &hdi
);
4587 if (lpColumn
->mask
& LVCF_FMT
)
4591 if (hdi
.fmt
& HDF_LEFT
)
4592 lpColumn
->fmt
|= LVCFMT_LEFT
;
4593 else if (hdi
.fmt
& HDF_RIGHT
)
4594 lpColumn
->fmt
|= LVCFMT_RIGHT
;
4595 else if (hdi
.fmt
& HDF_CENTER
)
4596 lpColumn
->fmt
|= LVCFMT_CENTER
;
4598 if (hdi
.fmt
& HDF_IMAGE
)
4599 lpColumn
->fmt
|= LVCFMT_COL_HAS_IMAGES
;
4601 if (hdi
.fmt
& HDF_BITMAP_ON_RIGHT
)
4602 lpColumn
->fmt
|= LVCFMT_BITMAP_ON_RIGHT
;
4605 if (lpColumn
->mask
& LVCF_WIDTH
)
4606 lpColumn
->cx
= hdi
.cxy
;
4608 if (lpColumn
->mask
& LVCF_IMAGE
)
4609 lpColumn
->iImage
= hdi
.iImage
;
4611 if (lpColumn
->mask
& LVCF_ORDER
)
4612 lpColumn
->iOrder
= hdi
.iOrder
;
4614 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4615 nItem
, debuglvcolumn_t(lpColumn
, isW
), isW
);
4624 static LRESULT
LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
4631 /* FIXME: little hack */
4632 for (i
= 0; i
< iCount
; i
++)
4640 * Retrieves the column width.
4643 * [I] infoPtr : valid pointer to the listview structure
4644 * [I] int : column index
4647 * SUCCESS : column width
4650 static LRESULT
LISTVIEW_GetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4652 INT nColumnWidth
= 0;
4655 TRACE("nColumn=%d\n", nColumn
);
4657 switch(LISTVIEW_GetType(infoPtr
))
4660 nColumnWidth
= infoPtr
->nItemWidth
;
4663 hdi
.mask
= HDI_WIDTH
;
4664 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
))
4665 nColumnWidth
= hdi
.cxy
;
4668 /* we don't have a 'column' in [SMALL]ICON mode */
4671 TRACE("nColumnWidth=%d\n", nColumnWidth
);
4672 return nColumnWidth
;
4677 * In list or report display mode, retrieves the number of items that can fit
4678 * vertically in the visible area. In icon or small icon display mode,
4679 * retrieves the total number of visible items.
4682 * [I] infoPtr : valid pointer to the listview structure
4685 * Number of fully visible items.
4687 static LRESULT
LISTVIEW_GetCountPerPage(LISTVIEW_INFO
*infoPtr
)
4689 UINT uView
= LISTVIEW_GetType(infoPtr
);
4692 if (uView
== LVS_LIST
)
4694 if (infoPtr
->rcList
.right
> infoPtr
->nItemWidth
)
4696 nItemCount
= LISTVIEW_GetCountPerRow(infoPtr
) *
4697 LISTVIEW_GetCountPerColumn(infoPtr
);
4700 else if (uView
== LVS_REPORT
)
4702 nItemCount
= LISTVIEW_GetCountPerColumn(infoPtr
);
4706 nItemCount
= infoPtr
->nItemCount
;
4715 * Retrieves an image list handle.
4718 * [I] infoPtr : valid pointer to the listview structure
4719 * [I] nImageList : image list identifier
4722 * SUCCESS : image list handle
4725 static LRESULT
LISTVIEW_GetImageList(LISTVIEW_INFO
*infoPtr
, INT nImageList
)
4727 HIMAGELIST himl
= NULL
;
4732 himl
= infoPtr
->himlNormal
;
4735 himl
= infoPtr
->himlSmall
;
4738 himl
= infoPtr
->himlState
;
4742 return (LRESULT
)himl
;
4745 /* LISTVIEW_GetISearchString */
4748 * Helper function for LISTVIEW_GetItemT *only*. Tests if an item is selected.
4749 * It is important that no other functions call this because of callbacks.
4751 static inline BOOL
is_item_selected(LISTVIEW_INFO
*infoPtr
, INT nItem
)
4753 RANGE selection
= { nItem
, nItem
};
4755 return DPA_Search(infoPtr
->hdpaSelectionRanges
, &selection
, 0,
4756 LISTVIEW_CompareSelectionRanges
, 0, DPAS_SORTED
) != -1;
4761 * Retrieves item attributes.
4764 * [I] hwnd : window handle
4765 * [IO] lpLVItem : item info
4766 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4767 * if FALSE, the lpLVItem is a LPLVITEMA.
4770 * This is the internal 'GetItem' interface -- it tries to
4771 * be smart, and avoids text copies, if possible, by modifing
4772 * lpLVItem->pszText to point to the text string. Please note
4773 * that this is not always possible (e.g. OWNERDATA), so on
4774 * entry you *must* supply valid values for pszText, and cchTextMax.
4775 * The only difference to the documented interface is that upon
4776 * return, you should use *only* the lpLVItem->pszText, rather than
4777 * the buffer pointer you provided on input. Most code already does
4778 * that, so it's not a problem.
4779 * For the two cases when the text must be copied (that is,
4780 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4786 static BOOL
LISTVIEW_GetItemT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
4788 NMLVDISPINFOW dispInfo
;
4789 LISTVIEW_ITEM
*lpItem
;
4793 /* In the following:
4794 * lpLVItem describes the information requested by the user
4795 * lpItem is what we have
4796 * dispInfo is a structure we use to request the missing
4797 * information from the application
4800 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
4802 if (!lpLVItem
|| (lpLVItem
->iItem
< 0) ||
4803 (lpLVItem
->iItem
>= infoPtr
->nItemCount
))
4806 /* a quick optimization if all we're asked is the focus state
4807 * these queries are worth optimising since they are common,
4808 * and can be answered in constant time, without the heavy accesses */
4809 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIF_STATE
) &&
4810 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
4812 lpLVItem
->state
= 0;
4813 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
4814 lpLVItem
->state
|= LVIS_FOCUSED
;
4818 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4820 /* if the app stores all the data, handle it separately */
4821 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4823 dispInfo
.item
.state
= 0;
4825 /* if we need to callback, do it now */
4826 if ((lpLVItem
->mask
& ~LVIF_STATE
) || infoPtr
->uCallbackMask
)
4828 /* NOTE: copy only fields which we _know_ are initialized, some apps
4829 * depend on the uninitialized fields being 0 */
4830 dispInfo
.item
.mask
= lpLVItem
->mask
;
4831 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
4832 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
;
4833 if (lpLVItem
->mask
& LVIF_TEXT
)
4835 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
4836 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
4838 if (lpLVItem
->mask
& LVIF_STATE
)
4839 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
4840 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
4841 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
4842 *lpLVItem
= dispInfo
.item
;
4843 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
4846 /* we store only a little state, so if we're not asked, we're done */
4847 if (!(lpLVItem
->mask
& LVIF_STATE
) || lpLVItem
->iSubItem
) return TRUE
;
4849 /* if focus is handled by us, report it */
4850 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
4852 lpLVItem
->state
&= ~LVIS_FOCUSED
;
4853 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
4854 lpLVItem
->state
|= LVIS_FOCUSED
;
4857 /* and do the same for selection, if we handle it */
4858 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
4860 lpLVItem
->state
&= ~LVIS_SELECTED
;
4861 if (is_item_selected(infoPtr
, lpLVItem
->iItem
))
4862 lpLVItem
->state
|= LVIS_SELECTED
;
4868 /* find the item and subitem structures before we proceed */
4869 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
4870 if (hdpaSubItems
== NULL
) return FALSE
;
4872 if ( !(lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0)) )
4875 if (lpLVItem
->iSubItem
)
4877 LISTVIEW_SUBITEM
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
4878 if(!lpSubItem
) return FALSE
;
4879 pItemHdr
= &lpSubItem
->hdr
;
4882 pItemHdr
= &lpItem
->hdr
;
4884 /* Do we need to query the state from the app? */
4885 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& lpLVItem
->iSubItem
== 0)
4887 dispInfo
.item
.mask
|= LVIF_STATE
;
4888 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
4891 /* Do we need to enquire about the image? */
4892 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (pItemHdr
->iImage
==I_IMAGECALLBACK
))
4893 dispInfo
.item
.mask
|= LVIF_IMAGE
;
4895 /* Do we need to enquire about the text? */
4896 if ((lpLVItem
->mask
& LVIF_TEXT
) && !is_textW(pItemHdr
->pszText
))
4898 dispInfo
.item
.mask
|= LVIF_TEXT
;
4899 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
4900 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
4901 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
4902 *dispInfo
.item
.pszText
= '\0';
4905 /* If we don't have all the requested info, query the application */
4906 if (dispInfo
.item
.mask
!= 0)
4908 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
4909 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
;
4910 dispInfo
.item
.lParam
= lpItem
->lParam
;
4911 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
4912 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
4915 /* Now, handle the iImage field */
4916 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
4918 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
4919 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && (pItemHdr
->iImage
==I_IMAGECALLBACK
))
4920 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
4922 else if (lpLVItem
->mask
& LVIF_IMAGE
)
4923 lpLVItem
->iImage
= pItemHdr
->iImage
;
4925 /* The pszText field */
4926 if (dispInfo
.item
.mask
& LVIF_TEXT
)
4928 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
4929 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
4931 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
4933 else if (lpLVItem
->mask
& LVIF_TEXT
)
4935 if (isW
) lpLVItem
->pszText
= pItemHdr
->pszText
;
4936 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
4939 /* if this is a subitem, we're done*/
4940 if (lpLVItem
->iSubItem
) return TRUE
;
4942 /* Next is the lParam field */
4943 if (dispInfo
.item
.mask
& LVIF_PARAM
)
4945 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
4946 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
4947 lpItem
->lParam
= dispInfo
.item
.lParam
;
4949 else if (lpLVItem
->mask
& LVIF_PARAM
)
4950 lpLVItem
->lParam
= lpItem
->lParam
;
4952 /* ... the state field (this one is different due to uCallbackmask) */
4953 if (lpLVItem
->mask
& LVIF_STATE
)
4955 lpLVItem
->state
= lpItem
->state
;
4956 if (dispInfo
.item
.mask
& LVIF_STATE
)
4958 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
4959 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
4961 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
4963 lpLVItem
->state
&= ~LVIS_FOCUSED
;
4964 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
4965 lpLVItem
->state
|= LVIS_FOCUSED
;
4967 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
4969 lpLVItem
->state
&= ~LVIS_SELECTED
;
4970 if (is_item_selected(infoPtr
, lpLVItem
->iItem
))
4971 lpLVItem
->state
|= LVIS_SELECTED
;
4975 /* and last, but not least, the indent field */
4976 if (lpLVItem
->mask
& LVIF_INDENT
)
4977 lpLVItem
->iIndent
= lpItem
->iIndent
;
4984 * Retrieves item attributes.
4987 * [I] hwnd : window handle
4988 * [IO] lpLVItem : item info
4989 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4990 * if FALSE, the lpLVItem is a LPLVITEMA.
4993 * This is the external 'GetItem' interface -- it properly copies
4994 * the text in the provided buffer.
5000 static BOOL
LISTVIEW_GetItemExtT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5005 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5008 pszText
= lpLVItem
->pszText
;
5009 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
5010 if (bResult
&& lpLVItem
->pszText
!= pszText
)
5011 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
5012 lpLVItem
->pszText
= pszText
;
5020 * Retrieves the position (upper-left) of the listview control item.
5021 * Note that for LVS_ICON style, the upper-left is that of the icon
5022 * and not the bounding box.
5025 * [I] infoPtr : valid pointer to the listview structure
5026 * [I] nItem : item index
5027 * [O] lpptPosition : coordinate information
5033 static BOOL
LISTVIEW_GetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
5035 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5038 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
5040 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5042 /* These should be very cheap to compute */
5043 if (!LISTVIEW_GetItemMeasures(infoPtr
, nItem
, &Box
, NULL
, NULL
, NULL
)) return FALSE
;
5045 lpptPosition
->x
= Box
.left
;
5046 lpptPosition
->y
= Box
.top
;
5047 if (uView
== LVS_ICON
)
5049 lpptPosition
->x
+= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2;
5050 lpptPosition
->y
+= ICON_TOP_PADDING
;
5053 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition
));
5060 * Retrieves the bounding rectangle for a listview control item.
5063 * [I] infoPtr : valid pointer to the listview structure
5064 * [I] nItem : item index
5065 * [IO] lprc : bounding rectangle coordinates
5066 * lprc->left specifies the portion of the item for which the bounding
5067 * rectangle will be retrieved.
5069 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5070 * including the icon and label.
5073 * * Experiment shows that native control returns:
5074 * * width = min (48, length of text line)
5075 * * .left = position.x - (width - iconsize.cx)/2
5076 * * .right = .left + width
5077 * * height = #lines of text * ntmHeight + icon height + 8
5078 * * .top = position.y - 2
5079 * * .bottom = .top + height
5080 * * separation between items .y = itemSpacing.cy - height
5081 * * .x = itemSpacing.cx - width
5082 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5085 * * Experiment shows that native control returns:
5086 * * width = iconSize.cx + 16
5087 * * .left = position.x - (width - iconsize.cx)/2
5088 * * .right = .left + width
5089 * * height = iconSize.cy + 4
5090 * * .top = position.y - 2
5091 * * .bottom = .top + height
5092 * * separation between items .y = itemSpacing.cy - height
5093 * * .x = itemSpacing.cx - width
5094 * LVIR_LABEL Returns the bounding rectangle of the item text.
5097 * * Experiment shows that native control returns:
5098 * * width = text length
5099 * * .left = position.x - width/2
5100 * * .right = .left + width
5101 * * height = ntmH * linecount + 2
5102 * * .top = position.y + iconSize.cy + 6
5103 * * .bottom = .top + height
5104 * * separation between items .y = itemSpacing.cy - height
5105 * * .x = itemSpacing.cx - width
5106 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5107 * rectangles, but excludes columns in report view.
5114 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5115 * upon whether the window has the focus currently and on whether the item
5116 * is the one with the focus. Ensure that the control's record of which
5117 * item has the focus agrees with the items' records.
5119 static BOOL
LISTVIEW_GetItemRect(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5123 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
5125 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5130 if (!LISTVIEW_GetItemMeasures(infoPtr
, nItem
, NULL
, NULL
, lprc
, NULL
)) return FALSE
;
5134 if (!LISTVIEW_GetItemMeasures(infoPtr
, nItem
, NULL
, NULL
, NULL
, lprc
)) return FALSE
;
5138 if (!LISTVIEW_GetItemMeasures(infoPtr
, nItem
, NULL
, lprc
, NULL
, NULL
)) return FALSE
;
5141 case LVIR_SELECTBOUNDS
:
5142 if (!LISTVIEW_GetItemMeasures(infoPtr
, nItem
, NULL
, lprc
, NULL
, &label_rect
)) return FALSE
;
5143 if ( (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
&&
5144 !(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) )
5145 lprc
->right
= label_rect
.right
;
5149 WARN("Unknown value: %d\n", lprc
->left
);
5153 TRACE(" rect=%s\n", debugrect(lprc
));
5160 * Retrieves the spacing between listview control items.
5163 * [I] infoPtr : valid pointer to the listview structure
5164 * [IO] lprc : rectangle to receive the output
5165 * on input, lprc->top = nSubItem
5166 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5168 * NOTE: this call is succeeds only for REPORT style listviews.
5169 * Because we can calculate things much faster in report mode,
5170 * we're gonna do the calculations inline here, instead of
5171 * calling functions that do heavy lifting.
5177 static BOOL
LISTVIEW_GetSubItemRect(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5180 INT nSubItem
, flags
;
5182 if (!lprc
|| LISTVIEW_GetType(infoPtr
) != LVS_REPORT
) return FALSE
;
5184 nSubItem
= lprc
->top
;
5187 TRACE("(nItem=%d, nSubItem=%d)\n", nItem
, nSubItem
);
5189 if (!Header_GetItemRect(infoPtr
->hwndHeader
, nSubItem
, lprc
)) return FALSE
;
5190 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &ptPosition
)) return FALSE
;
5191 lprc
->top
= ptPosition
.y
;
5192 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5197 FIXME("Unimplemented LVIR_ICON\n");
5201 /* nothing to do here, we're done */
5204 ERR("Unknown bounds=%d\n", lprc
->left
);
5213 * Retrieves the width of a label.
5216 * [I] infoPtr : valid pointer to the listview structure
5219 * SUCCESS : string width (in pixels)
5222 static INT
LISTVIEW_GetLabelWidth(LISTVIEW_INFO
*infoPtr
, INT nItem
)
5224 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5227 TRACE("(nItem=%d)\n", nItem
);
5229 lvItem
.mask
= LVIF_TEXT
;
5230 lvItem
.iItem
= nItem
;
5231 lvItem
.iSubItem
= 0;
5232 lvItem
.pszText
= szDispText
;
5233 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5234 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5236 /* FIXME: is this right? What if the label is very long? */
5237 return LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
5242 * Retrieves the spacing between listview control items.
5245 * [I] infoPtr : valid pointer to the listview structure
5246 * [I] BOOL : flag for small or large icon
5249 * Horizontal + vertical spacing
5251 static LRESULT
LISTVIEW_GetItemSpacing(LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
5257 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
5261 if (LISTVIEW_GetType(infoPtr
) == LVS_ICON
)
5262 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
5264 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
5271 * Retrieves the state of a listview control item.
5274 * [I] infoPtr : valid pointer to the listview structure
5275 * [I] nItem : item index
5276 * [I] uMask : state mask
5279 * State specified by the mask.
5281 static LRESULT
LISTVIEW_GetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
5285 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
5287 lvItem
.iItem
= nItem
;
5288 lvItem
.iSubItem
= 0;
5289 lvItem
.mask
= LVIF_STATE
;
5290 lvItem
.stateMask
= uMask
;
5291 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5293 return lvItem
.state
& uMask
;
5298 * Retrieves the text of a listview control item or subitem.
5301 * [I] hwnd : window handle
5302 * [I] nItem : item index
5303 * [IO] lpLVItem : item information
5304 * [I] isW : TRUE if lpLVItem is Unicode
5307 * SUCCESS : string length
5310 static LRESULT
LISTVIEW_GetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
5312 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
5314 lpLVItem
->mask
= LVIF_TEXT
;
5315 lpLVItem
->iItem
= nItem
;
5316 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
5318 return textlenT(lpLVItem
->pszText
, isW
);
5323 * Searches for an item based on properties + relationships.
5326 * [I] infoPtr : valid pointer to the listview structure
5327 * [I] nItem : item index
5328 * [I] uFlags : relationship flag
5331 * This function is ver, very inefficient! Needs work.
5334 * SUCCESS : item index
5337 static LRESULT
LISTVIEW_GetNextItem(LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
5339 UINT uView
= LISTVIEW_GetType(infoPtr
);
5341 LVFINDINFOW lvFindInfo
;
5342 INT nCountPerColumn
;
5345 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
5346 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
5348 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
5350 if (uFlags
& LVNI_CUT
)
5353 if (uFlags
& LVNI_DROPHILITED
)
5354 uMask
|= LVIS_DROPHILITED
;
5356 if (uFlags
& LVNI_FOCUSED
)
5357 uMask
|= LVIS_FOCUSED
;
5359 if (uFlags
& LVNI_SELECTED
)
5360 uMask
|= LVIS_SELECTED
;
5362 /* if we're asked for the focused item, that's only one,
5363 * so it's worth optimizing */
5364 if (uFlags
& LVNI_FOCUSED
)
5366 if (!(LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) == uMask
) return -1;
5367 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
5370 if (uFlags
& LVNI_ABOVE
)
5372 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
5377 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5383 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5384 lvFindInfo
.vkDirection
= VK_UP
;
5385 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5386 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5388 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5393 else if (uFlags
& LVNI_BELOW
)
5395 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
5397 while (nItem
< infoPtr
->nItemCount
)
5400 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5406 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5407 lvFindInfo
.vkDirection
= VK_DOWN
;
5408 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5409 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5411 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5416 else if (uFlags
& LVNI_TOLEFT
)
5418 if (uView
== LVS_LIST
)
5420 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
5421 while (nItem
- nCountPerColumn
>= 0)
5423 nItem
-= nCountPerColumn
;
5424 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5428 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5430 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5431 lvFindInfo
.vkDirection
= VK_LEFT
;
5432 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5433 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5435 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5440 else if (uFlags
& LVNI_TORIGHT
)
5442 if (uView
== LVS_LIST
)
5444 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
5445 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
5447 nItem
+= nCountPerColumn
;
5448 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5452 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5454 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5455 lvFindInfo
.vkDirection
= VK_RIGHT
;
5456 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5457 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5459 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
5468 /* search by index */
5469 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
5471 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
5479 /* LISTVIEW_GetNumberOfWorkAreas */
5483 * Retrieves the origin coordinates when in icon or small icon display mode.
5486 * [I] infoPtr : valid pointer to the listview structure
5487 * [O] lpptOrigin : coordinate information
5493 static BOOL
LISTVIEW_GetOrigin(LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
5495 DWORD lStyle
= infoPtr
->dwStyle
;
5496 UINT uView
= lStyle
& LVS_TYPEMASK
;
5497 INT nHorzPos
= 0, nVertPos
= 0;
5498 SCROLLINFO scrollInfo
;
5500 if (!lpptOrigin
) return FALSE
;
5502 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
5503 scrollInfo
.fMask
= SIF_POS
;
5505 if ((lStyle
& WS_HSCROLL
) && GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
5506 nHorzPos
= scrollInfo
.nPos
;
5507 if ((lStyle
& WS_VSCROLL
) && GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
5508 nVertPos
= scrollInfo
.nPos
;
5510 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
5512 lpptOrigin
->x
= infoPtr
->rcList
.left
;
5513 lpptOrigin
->y
= infoPtr
->rcList
.top
;
5514 if (uView
== LVS_LIST
)
5515 nHorzPos
*= infoPtr
->nItemWidth
;
5516 else if (uView
== LVS_REPORT
)
5517 nVertPos
*= infoPtr
->nItemHeight
;
5519 lpptOrigin
->x
-= nHorzPos
;
5520 lpptOrigin
->y
-= nVertPos
;
5522 TRACE(" origin=%s\n", debugpoint(lpptOrigin
));
5529 * Retrieves the width of a string.
5532 * [I] hwnd : window handle
5533 * [I] lpszText : text string to process
5534 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5537 * SUCCESS : string width (in pixels)
5540 static LRESULT
LISTVIEW_GetStringWidthT(LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
5545 if (is_textT(lpszText
, isW
))
5547 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
5548 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
5549 HFONT hOldFont
= SelectObject(hdc
, hFont
);
5552 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
5554 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
5555 SelectObject(hdc
, hOldFont
);
5556 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
5558 return stringSize
.cx
;
5564 * Determines item if a hit or closest if not
5567 * [I] infoPtr : valid pointer to the listview structure
5568 * [IO] lpht : hit test information
5569 * [I] subitem : fill out iSubItem.
5570 * [I] bNearItem : return the nearest item
5573 * SUCCESS : item index of hit
5576 static INT
LISTVIEW_SuperHitTestItem(LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL bNearItem
)
5578 LONG lStyle
= infoPtr
->dwStyle
;
5579 UINT uView
= lStyle
& LVS_TYPEMASK
;
5580 INT i
,j
,topindex
,bottomindex
,nearestItem
;
5581 RECT rcItem
,rcSubItem
;
5582 DWORD xterm
, yterm
, dist
, mindist
;
5584 TRACE("(lpht->pt=%s, subitem=%d\n", debugpoint(&lpht
->pt
), subitem
);
5589 /* FIXME: get the visible range */
5590 topindex
= LISTVIEW_GetTopIndex(infoPtr
);
5591 if (uView
== LVS_REPORT
)
5593 bottomindex
= topindex
+ LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
5594 bottomindex
= min(bottomindex
,infoPtr
->nItemCount
);
5598 bottomindex
= infoPtr
->nItemCount
;
5601 for (i
= topindex
; i
< bottomindex
; i
++)
5603 rcItem
.left
= LVIR_BOUNDS
;
5604 if (LISTVIEW_GetItemRect(infoPtr
, i
, &rcItem
))
5606 if (PtInRect(&rcItem
, lpht
->pt
))
5609 rcItem
.left
= LVIR_ICON
;
5610 if (LISTVIEW_GetItemRect(infoPtr
, i
, &rcItem
))
5612 if (PtInRect(&rcItem
, lpht
->pt
))
5614 lpht
->flags
= LVHT_ONITEMICON
;
5620 rcItem
.left
= LVIR_LABEL
;
5621 if (LISTVIEW_GetItemRect(infoPtr
, i
, &rcItem
))
5623 if (PtInRect(&rcItem
, lpht
->pt
))
5625 lpht
->flags
= LVHT_ONITEMLABEL
;
5631 lpht
->flags
= LVHT_ONITEMSTATEICON
;
5636 INT nColumnCount
= Header_GetItemCount(infoPtr
->hwndHeader
);
5638 rcSubItem
.right
= rcSubItem
.left
;
5639 for (j
= 0; j
< nColumnCount
; j
++)
5641 rcSubItem
.left
= rcSubItem
.right
;
5642 rcSubItem
.right
+= LISTVIEW_GetColumnWidth(infoPtr
, j
);
5643 if (PtInRect(&rcSubItem
, lpht
->pt
))
5650 TRACE("hit on item %d\n", i
);
5656 * Now compute distance from point to center of boundary
5657 * box. Since we are only interested in the relative
5658 * distance, we can skip the nasty square root operation
5660 xterm
= rcItem
.left
+ (rcItem
.right
- rcItem
.left
)/2 - lpht
->pt
.x
;
5661 yterm
= rcItem
.top
+ (rcItem
.bottom
- rcItem
.top
)/2 - lpht
->pt
.y
;
5662 dist
= xterm
* xterm
+ yterm
* yterm
;
5663 if (mindist
< 0 || dist
< mindist
)
5672 lpht
->flags
= LVHT_NOWHERE
;
5674 return bNearItem
? nearestItem
: -1;
5680 * Determines which listview item is located at the specified position.
5683 * [I] infoPtr : valid pointer to the listview structure
5684 * [IO] lpht : hit test information
5685 * [I] subitem : fill out iSubItem.
5688 * SUCCESS : item index
5691 static LRESULT
LISTVIEW_HitTest(LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
)
5693 TRACE("(x=%ld, y=%ld)\n", lpht
->pt
.x
, lpht
->pt
.y
);
5697 if (subitem
) lpht
->iSubItem
= 0;
5699 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
5700 lpht
->flags
|= LVHT_TOLEFT
;
5701 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
5702 lpht
->flags
|= LVHT_TORIGHT
;
5704 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
5705 lpht
->flags
|= LVHT_ABOVE
;
5706 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
5707 lpht
->flags
|= LVHT_BELOW
;
5709 if (lpht
->flags
) return -1;
5711 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
5712 * an app might pass only a structure with space up to iItem!
5713 * (MS Office 97 does that for instance in the file open dialog)
5715 return LISTVIEW_SuperHitTestItem(infoPtr
, lpht
, subitem
, FALSE
);
5721 * Inserts a new column.
5724 * [I] infoPtr : valid pointer to the listview structure
5725 * [I] INT : column index
5726 * [I] LPLVCOLUMNW : column information
5729 * SUCCESS : new column index
5732 static LRESULT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
5733 LPLVCOLUMNW lpColumn
, BOOL isW
)
5739 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
5741 if (!lpColumn
) return -1;
5743 hdi
.mask
= hdi
.fmt
= 0;
5744 if (lpColumn
->mask
& LVCF_FMT
)
5746 /* format member is valid */
5747 hdi
.mask
|= HDI_FORMAT
;
5749 /* set text alignment (leftmost column must be left-aligned) */
5750 if (nColumn
== 0 || lpColumn
->fmt
& LVCFMT_LEFT
)
5751 hdi
.fmt
|= HDF_LEFT
;
5752 else if (lpColumn
->fmt
& LVCFMT_RIGHT
)
5753 hdi
.fmt
|= HDF_RIGHT
;
5754 else if (lpColumn
->fmt
& LVCFMT_CENTER
)
5755 hdi
.fmt
|= HDF_CENTER
;
5757 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
5758 hdi
.fmt
|= HDF_BITMAP_ON_RIGHT
;
5760 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
5762 hdi
.fmt
|= HDF_IMAGE
;
5763 hdi
.iImage
= I_IMAGECALLBACK
;
5766 if (lpColumn
->fmt
& LVCFMT_IMAGE
)
5767 ; /* FIXME: enable images for *(sub)items* this column */
5770 if (lpColumn
->mask
& LVCF_WIDTH
)
5772 hdi
.mask
|= HDI_WIDTH
;
5773 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
5775 /* make it fill the remainder of the controls width */
5780 /* get the width of every item except the current one */
5781 hdit
.mask
= HDI_WIDTH
;
5784 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
5785 if (Header_GetItemW(infoPtr
->hwndHeader
, item_index
, (LPARAM
)(&hdit
)))
5786 hdi
.cxy
+= hdit
.cxy
;
5788 /* retrieve the layout of the header */
5789 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
5790 TRACE("start cxy=%d rcHeader=%s\n", hdi
.cxy
, debugrect(&rcHeader
));
5792 hdi
.cxy
= (rcHeader
.right
- rcHeader
.left
) - hdi
.cxy
;
5795 hdi
.cxy
= lpColumn
->cx
;
5798 if (lpColumn
->mask
& LVCF_TEXT
)
5800 hdi
.mask
|= HDI_TEXT
| HDI_FORMAT
;
5801 hdi
.fmt
|= HDF_STRING
;
5802 hdi
.pszText
= lpColumn
->pszText
;
5803 hdi
.cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
5806 if (lpColumn
->mask
& LVCF_IMAGE
)
5808 hdi
.mask
|= HDI_IMAGE
;
5809 hdi
.iImage
= lpColumn
->iImage
;
5812 if (lpColumn
->mask
& LVCF_ORDER
)
5814 hdi
.mask
|= HDI_ORDER
;
5815 hdi
.iOrder
= lpColumn
->iOrder
;
5818 /* insert item in header control */
5819 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
5820 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
5821 (WPARAM
)nColumn
, (LPARAM
)&hdi
);
5822 if (nNewColumn
== -1) return -1;
5823 if (!Header_GetItemRect(infoPtr
->hwndHeader
, nNewColumn
, &rcCol
)) return -1;
5825 /* now we have to actually adjust the data */
5826 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
5828 LISTVIEW_SUBITEM
*lpSubItem
, *lpMainItem
, **lpNewItems
= 0;
5832 /* preallocate memory, so we can fail gracefully */
5833 if (nNewColumn
== 0)
5835 lpNewItems
= COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM
*) * infoPtr
->nItemCount
);
5836 if (!lpNewItems
) return -1;
5837 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
5838 if (!(lpNewItems
[i
] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM
)))) break;
5839 if (i
!= infoPtr
->nItemCount
)
5841 for(; i
>=0; i
--) COMCTL32_Free(lpNewItems
[i
]);
5842 COMCTL32_Free(lpNewItems
);
5847 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
5849 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
5850 if (!hdpaSubItems
) continue;
5851 for (i
= 1; i
< hdpaSubItems
->nItemCount
; i
++)
5853 lpSubItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, i
);
5854 if (!lpSubItem
) break;
5855 if (lpSubItem
->iSubItem
>= nNewColumn
)
5856 lpSubItem
->iSubItem
++;
5859 /* if we found our subitem, zapp it */
5860 if (nNewColumn
== 0)
5862 lpMainItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, 0);
5863 lpSubItem
= lpNewItems
[nItem
];
5864 lpSubItem
->hdr
= lpMainItem
->hdr
;
5865 lpSubItem
->iSubItem
= 1;
5866 ZeroMemory(&lpMainItem
->hdr
, sizeof(lpMainItem
->hdr
));
5867 lpMainItem
->iSubItem
= 0;
5868 DPA_InsertPtr(hdpaSubItems
, 1, lpSubItem
);
5872 COMCTL32_Free(lpNewItems
);
5875 /* we don't have to worry abiut display issues in non-report mode */
5876 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return nNewColumn
;
5878 /* if we have a focus, must first erase the focus rect */
5879 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
5881 /* Need to reset the item width when inserting a new column */
5882 infoPtr
->nItemWidth
+= rcCol
.right
- rcCol
.left
;
5884 LISTVIEW_UpdateScroll(infoPtr
);
5886 /* scroll to cover the deleted column, and invalidate for redraw */
5887 rcOld
= infoPtr
->rcList
;
5888 rcOld
.left
= rcCol
.left
;
5889 ScrollWindowEx(infoPtr
->hwndSelf
, rcCol
.right
- rcCol
.left
, 0,
5890 &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
5892 /* we can restore focus now */
5893 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, infoPtr
->nFocusedItem
, TRUE
);
5898 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5899 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5900 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5901 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5902 their own sort proc. when sending LVM_SORTITEMS.
5905 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5907 LVS_SORTXXX must be specified,
5908 LVS_OWNERDRAW is not set,
5909 <item>.pszText is not LPSTR_TEXTCALLBACK.
5911 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5912 are sorted based on item text..."
5914 static INT WINAPI
LISTVIEW_InsertCompare( LPVOID first
, LPVOID second
, LPARAM lParam
)
5916 LISTVIEW_ITEM
* lv_first
= (LISTVIEW_ITEM
*) DPA_GetPtr( (HDPA
)first
, 0 );
5917 LISTVIEW_ITEM
* lv_second
= (LISTVIEW_ITEM
*) DPA_GetPtr( (HDPA
)second
, 0 );
5918 INT cmpv
= textcmpWT(lv_first
->hdr
.pszText
, lv_second
->hdr
.pszText
, TRUE
);
5920 /* if we're sorting descending, negate the return value */
5921 return (((LISTVIEW_INFO
*)lParam
)->dwStyle
& LVS_SORTDESCENDING
) ? -cmpv
: cmpv
;
5926 * Inserts a new item in the listview control.
5929 * [I] infoPtr : valid pointer to the listview structure
5930 * [I] lpLVItem : item information
5931 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5934 * SUCCESS : new item index
5937 static LRESULT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5939 LONG lStyle
= infoPtr
->dwStyle
;
5940 UINT uView
= lStyle
& LVS_TYPEMASK
;
5944 LISTVIEW_ITEM
*lpItem
;
5947 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
5949 if (lStyle
& LVS_OWNERDATA
)
5951 nItem
= infoPtr
->nItemCount
;
5952 infoPtr
->nItemCount
++;
5956 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5957 if (!lpLVItem
|| lpLVItem
->iSubItem
) return -1;
5959 if (!is_assignable_item(lpLVItem
, lStyle
)) return -1;
5961 if ( !(lpItem
= (LISTVIEW_ITEM
*)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM
))) )
5964 /* insert item in listview control data structure */
5965 if ( (hdpaSubItems
= DPA_Create(8)) )
5966 nItem
= DPA_InsertPtr(hdpaSubItems
, 0, lpItem
);
5967 if (nItem
== -1) goto fail
;
5969 is_sorted
= (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
5970 !(lStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
5972 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
,
5973 is_sorted
? infoPtr
->nItemCount
+ 1 : lpLVItem
->iItem
,
5975 if (nItem
== -1) goto fail
;
5976 infoPtr
->nItemCount
++;
5978 if (!LISTVIEW_SetItemT(infoPtr
, lpLVItem
, isW
))
5981 /* if we're sorted, sort the list, and update the index */
5984 DPA_Sort( infoPtr
->hdpaItems
, LISTVIEW_InsertCompare
, (LPARAM
)infoPtr
);
5985 nItem
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, hdpaSubItems
);
5988 ERR("We can't find the item we just inserted, possible memory corruption.");
5989 /* we can't remove it from the list if we can't find it, so just fail */
5990 /* we don't deallocate memory here, as it will probably cause more problems */
5995 /* make room for the position, if we are in the right mode */
5996 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5998 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
6000 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
6002 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
6007 /* Add the subitem list to the items array. Do this last in case we go to
6008 * fail during the above.
6010 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
6012 lpItem
->valid
= TRUE
;
6014 /* send LVN_INSERTITEM notification */
6015 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
6017 nmlv
.lParam
= lpItem
->lParam
;
6018 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
6020 /* align items (set position of each item) */
6021 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6023 if (lStyle
& LVS_ALIGNLEFT
) LISTVIEW_AlignLeft(infoPtr
);
6024 else LISTVIEW_AlignTop(infoPtr
);
6027 LISTVIEW_UpdateScroll(infoPtr
);
6029 LISTVIEW_InvalidateList(infoPtr
); /* FIXME: optimize */
6031 TRACE(" <- %d\n", nItem
);
6035 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
6036 infoPtr
->nItemCount
--;
6038 DPA_DeletePtr(hdpaSubItems
, 0);
6039 DPA_Destroy (hdpaSubItems
);
6040 COMCTL32_Free (lpItem
);
6046 * Redraws a range of items.
6049 * [I] infoPtr : valid pointer to the listview structure
6050 * [I] INT : first item
6051 * [I] INT : last item
6057 static LRESULT
LISTVIEW_RedrawItems(LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
6061 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
6062 max(nFirst
, nLast
) >= infoPtr
->nItemCount
)
6065 for (i
= nFirst
; i
<= nLast
; i
++)
6066 LISTVIEW_InvalidateItem(infoPtr
, i
);
6073 * Scroll the content of a listview.
6076 * [I] infoPtr : valid pointer to the listview structure
6077 * [I] INT : horizontal scroll amount in pixels
6078 * [I] INT : vertical scroll amount in pixels
6085 * If the control is in report mode (LVS_REPORT) the control can
6086 * be scrolled only in line increments. "dy" will be rounded to the
6087 * nearest number of pixels that are a whole line. Ex: if line height
6088 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6089 * is passed the the scroll will be 0. (per MSDN 7/2002)
6091 * For: (per experimentaion with native control and CSpy ListView)
6092 * LVS_ICON dy=1 = 1 pixel (vertical only)
6094 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6096 * LVS_LIST dx=1 = 1 column (horizontal only)
6097 * but will only scroll 1 column per message
6098 * no matter what the value.
6099 * dy must be 0 or FALSE returned.
6100 * LVS_REPORT dx=1 = 1 pixel
6104 static LRESULT
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
6106 switch(LISTVIEW_GetType(infoPtr
)) {
6108 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
6109 dy
/= infoPtr
->nItemHeight
;
6112 if (dy
!= 0) return FALSE
;
6119 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
, 0);
6120 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
, 0);
6127 * Sets the background color.
6130 * [I] infoPtr : valid pointer to the listview structure
6131 * [I] COLORREF : background color
6137 static LRESULT
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrBk
)
6139 TRACE("(clrBk=%lx)\n", clrBk
);
6141 if(infoPtr
->clrBk
!= clrBk
) {
6142 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
6143 infoPtr
->clrBk
= clrBk
;
6144 if (clrBk
== CLR_NONE
)
6145 infoPtr
->hBkBrush
= GetClassLongW(infoPtr
->hwndSelf
, GCL_HBRBACKGROUND
);
6147 infoPtr
->hBkBrush
= CreateSolidBrush(clrBk
);
6148 LISTVIEW_InvalidateList(infoPtr
);
6154 /* LISTVIEW_SetBkImage */
6158 * Sets the attributes of a header item.
6161 * [I] infoPtr : valid pointer to the listview structure
6162 * [I] INT : column index
6163 * [I] LPLVCOLUMNW : column attributes
6164 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6165 * otherwise it is in fact a LPLVCOLUMNA
6171 static LRESULT
LISTVIEW_SetColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6172 LPLVCOLUMNW lpColumn
, BOOL isW
)
6174 BOOL bResult
= FALSE
;
6175 HDITEMW hdi
, hdiget
;
6177 if ((lpColumn
!= NULL
) && (nColumn
>= 0) &&
6178 (nColumn
< Header_GetItemCount(infoPtr
->hwndHeader
)))
6180 /* initialize memory */
6181 ZeroMemory(&hdi
, sizeof(hdi
));
6183 if (lpColumn
->mask
& LVCF_FMT
)
6185 /* format member is valid */
6186 hdi
.mask
|= HDI_FORMAT
;
6188 /* get current format first */
6189 hdiget
.mask
= HDI_FORMAT
;
6190 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdiget
))
6191 /* preserve HDF_STRING if present */
6192 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
6194 /* set text alignment (leftmost column must be left-aligned) */
6197 hdi
.fmt
|= HDF_LEFT
;
6201 if (lpColumn
->fmt
& LVCFMT_LEFT
)
6202 hdi
.fmt
|= HDF_LEFT
;
6203 else if (lpColumn
->fmt
& LVCFMT_RIGHT
)
6204 hdi
.fmt
|= HDF_RIGHT
;
6205 else if (lpColumn
->fmt
& LVCFMT_CENTER
)
6206 hdi
.fmt
|= HDF_CENTER
;
6209 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6210 hdi
.fmt
|= HDF_BITMAP_ON_RIGHT
;
6212 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6213 hdi
.fmt
|= HDF_IMAGE
;
6215 if (lpColumn
->fmt
& LVCFMT_IMAGE
)
6217 hdi
.fmt
|= HDF_IMAGE
;
6218 hdi
.iImage
= I_IMAGECALLBACK
;
6222 if (lpColumn
->mask
& LVCF_WIDTH
)
6224 hdi
.mask
|= HDI_WIDTH
;
6225 hdi
.cxy
= lpColumn
->cx
;
6228 if (lpColumn
->mask
& LVCF_TEXT
)
6230 hdi
.mask
|= HDI_TEXT
| HDI_FORMAT
;
6231 hdi
.pszText
= lpColumn
->pszText
;
6232 hdi
.cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
6233 hdi
.fmt
|= HDF_STRING
;
6236 if (lpColumn
->mask
& LVCF_IMAGE
)
6238 hdi
.mask
|= HDI_IMAGE
;
6239 hdi
.iImage
= lpColumn
->iImage
;
6242 if (lpColumn
->mask
& LVCF_ORDER
)
6244 hdi
.mask
|= HDI_ORDER
;
6245 hdi
.iOrder
= lpColumn
->iOrder
;
6248 /* set header item attributes */
6250 bResult
= Header_SetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
);
6252 bResult
= Header_SetItemA(infoPtr
->hwndHeader
, nColumn
, &hdi
);
6260 * Sets the column order array
6263 * [I] infoPtr : valid pointer to the listview structure
6264 * [I] INT : number of elements in column order array
6265 * [I] INT : pointer to column order array
6271 static LRESULT
LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
6273 FIXME("iCount %d lpiArray %p\n", iCount
, lpiArray
);
6284 * Sets the width of a column
6287 * [I] infoPtr : valid pointer to the listview structure
6288 * [I] INT : column index
6289 * [I] INT : column width
6295 static LRESULT
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT iCol
, INT cx
)
6299 LONG lStyle
= infoPtr
->dwStyle
;
6300 UINT uView
= lStyle
& LVS_TYPEMASK
;
6305 WCHAR text_buffer
[DISP_TEXT_SIZE
];
6306 INT header_item_count
;
6311 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
6313 if (!infoPtr
->hwndHeader
) /* make sure we have a header */
6316 /* set column width only if in report or list mode */
6317 if ((uView
!= LVS_REPORT
) && (uView
!= LVS_LIST
))
6320 TRACE("(iCol=%d, cx=%d\n", iCol
, cx
);
6322 /* take care of invalid cx values */
6323 if((uView
== LVS_REPORT
) && (cx
< -2))
6324 cx
= LVSCW_AUTOSIZE
;
6325 else if (uView
== LVS_LIST
&& (cx
< 1))
6328 /* resize all columns if in LVS_LIST mode */
6329 if(uView
== LVS_LIST
) {
6330 infoPtr
->nItemWidth
= cx
;
6331 LISTVIEW_InvalidateList(infoPtr
); /* FIXME: optimize */
6335 /* autosize based on listview items width */
6336 if(cx
== LVSCW_AUTOSIZE
)
6338 /* set the width of the column to the width of the widest item */
6339 if (iCol
== 0 || uView
== LVS_LIST
)
6342 for(item_index
= 0; item_index
< infoPtr
->nItemCount
; item_index
++)
6344 nLabelWidth
= LISTVIEW_GetLabelWidth(infoPtr
, item_index
);
6345 cx
= (nLabelWidth
>cx
)?nLabelWidth
:cx
;
6347 if (infoPtr
->himlSmall
)
6348 cx
+= infoPtr
->iconSize
.cx
+ IMAGE_PADDING
;
6352 lvItem
.iSubItem
= iCol
;
6353 lvItem
.mask
= LVIF_TEXT
;
6354 lvItem
.pszText
= szDispText
;
6355 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6357 for(item_index
= 0; item_index
< infoPtr
->nItemCount
; item_index
++)
6359 lvItem
.iItem
= item_index
;
6360 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
6361 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
6362 cx
= (nLabelWidth
>cx
)?nLabelWidth
:cx
;
6365 cx
+= TRAILING_PADDING
;
6366 } /* autosize based on listview header width */
6367 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
6369 header_item_count
= Header_GetItemCount(infoPtr
->hwndHeader
);
6371 /* if iCol is the last column make it fill the remainder of the controls width */
6372 if(iCol
== (header_item_count
- 1)) {
6373 /* get the width of every item except the current one */
6374 hdi
.mask
= HDI_WIDTH
;
6377 for(item_index
= 0; item_index
< (header_item_count
- 1); item_index
++) {
6378 Header_GetItemW(infoPtr
->hwndHeader
, item_index
, (LPARAM
)(&hdi
));
6382 /* retrieve the layout of the header */
6383 GetWindowRect(infoPtr
->hwndHeader
, &rcHeader
);
6385 cx
= (rcHeader
.right
- rcHeader
.left
) - cx
;
6389 /* Despite what the MS docs say, if this is not the last
6390 column, then MS resizes the column to the width of the
6391 largest text string in the column, including headers
6392 and items. This is different from LVSCW_AUTOSIZE in that
6393 LVSCW_AUTOSIZE ignores the header string length.
6396 /* retrieve header font */
6397 header_font
= SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0L, 0L);
6399 /* retrieve header text */
6400 hdi
.mask
= HDI_TEXT
;
6401 hdi
.cchTextMax
= sizeof(text_buffer
)/sizeof(text_buffer
[0]);
6402 hdi
.pszText
= text_buffer
;
6404 Header_GetItemW(infoPtr
->hwndHeader
, iCol
, (LPARAM
)(&hdi
));
6406 /* determine the width of the text in the header */
6407 hdc
= GetDC(infoPtr
->hwndSelf
);
6408 old_font
= SelectObject(hdc
, header_font
); /* select the font into hdc */
6410 GetTextExtentPoint32W(hdc
, text_buffer
, lstrlenW(text_buffer
), &size
);
6412 SelectObject(hdc
, old_font
); /* restore the old font */
6413 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6415 lvItem
.iSubItem
= iCol
;
6416 lvItem
.mask
= LVIF_TEXT
;
6417 lvItem
.pszText
= szDispText
;
6418 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6420 for(item_index
= 0; item_index
< infoPtr
->nItemCount
; item_index
++)
6422 lvItem
.iItem
= item_index
;
6423 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
6424 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
6425 nLabelWidth
+= TRAILING_PADDING
;
6426 /* While it is possible for subitems to have icons, even MS messes
6427 up the positioning, so I suspect no applications actually use
6429 if (item_index
== 0 && infoPtr
->himlSmall
)
6430 nLabelWidth
+= infoPtr
->iconSize
.cx
+ IMAGE_PADDING
;
6431 cx
= (nLabelWidth
>cx
)?nLabelWidth
:cx
;
6436 /* call header to update the column change */
6437 hdi
.mask
= HDI_WIDTH
;
6440 lret
= Header_SetItemW(infoPtr
->hwndHeader
, (WPARAM
)iCol
, (LPARAM
)&hdi
);
6442 LISTVIEW_InvalidateList(infoPtr
); /* FIXME: optimize */
6449 * Sets the extended listview style.
6452 * [I] infoPtr : valid pointer to the listview structure
6457 * SUCCESS : previous style
6460 static LRESULT
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD dwMask
, DWORD dwStyle
)
6462 DWORD dwOldStyle
= infoPtr
->dwLvExStyle
;
6466 infoPtr
->dwLvExStyle
= (dwOldStyle
& ~dwMask
) | (dwStyle
& dwMask
);
6468 infoPtr
->dwLvExStyle
= dwStyle
;
6475 * Sets the new hot cursor used during hot tracking and hover selection.
6478 * [I] infoPtr : valid pointer to the listview structure
6479 * [I} hCurosr : the new hot cursor handle
6482 * Returns the previous hot cursor
6484 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
6486 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
6487 infoPtr
->hHotCursor
= hCursor
;
6494 * Sets the hot item index.
6497 * [I] infoPtr : valid pointer to the listview structure
6501 * SUCCESS : previous hot item index
6502 * FAILURE : -1 (no hot item)
6504 static LRESULT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
6506 INT iOldIndex
= infoPtr
->nHotItem
;
6507 infoPtr
->nHotItem
= iIndex
;
6514 * Sets the amount of time the cursor must hover over an item before it is selected.
6517 * [I] infoPtr : valid pointer to the listview structure
6518 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6521 * Returns the previous hover time
6523 static LRESULT
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
6525 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
6526 infoPtr
->dwHoverTime
= dwHoverTime
;
6527 return oldHoverTime
;
6532 * Sets spacing for icons of LVS_ICON style.
6535 * [I] infoPtr : valid pointer to the listview structure
6536 * [I] DWORD : MAKELONG(cx, cy)
6539 * MAKELONG(oldcx, oldcy)
6541 static LRESULT
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, DWORD spacing
)
6543 INT cy
= HIWORD(spacing
), cx
= LOWORD(spacing
);
6544 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
6545 LONG lStyle
= infoPtr
->dwStyle
;
6546 UINT uView
= lStyle
& LVS_TYPEMASK
;
6548 TRACE("requested=(%d,%d)\n", cx
, cy
);
6550 /* this is supported only for LVS_ICON style */
6551 if (uView
!= LVS_ICON
) return oldspacing
;
6553 /* set to defaults, if instructed to */
6554 if (cx
== -1) cx
= GetSystemMetrics(SM_CXICONSPACING
);
6555 if (cy
== -1) cy
= GetSystemMetrics(SM_CYICONSPACING
);
6557 /* if 0 then compute width
6558 * FIXME: Should scan each item and determine max width of
6559 * icon or label, then make that the width */
6561 cx
= infoPtr
->iconSpacing
.cx
;
6563 /* if 0 then compute height */
6565 cy
= infoPtr
->iconSize
.cy
+ 2 * infoPtr
->ntmHeight
+
6566 ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_PADDING
;
6569 infoPtr
->iconSpacing
.cx
= cx
;
6570 infoPtr
->iconSpacing
.cy
= cy
;
6572 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6573 LOWORD(oldspacing
), HIWORD(oldspacing
), cx
, cy
,
6574 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
6575 infoPtr
->ntmHeight
);
6577 /* these depend on the iconSpacing */
6578 infoPtr
->nItemWidth
= LISTVIEW_CalculateMaxWidth(infoPtr
);
6579 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(infoPtr
);
6584 inline void update_icon_size(HIMAGELIST himl
, SIZE
*size
)
6588 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
6594 size
->cx
= size
->cy
= 0;
6602 * [I] infoPtr : valid pointer to the listview structure
6603 * [I] INT : image list type
6604 * [I] HIMAGELIST : image list handle
6607 * SUCCESS : old image list
6610 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
6612 UINT uView
= LISTVIEW_GetType(infoPtr
);
6613 INT oldHeight
= infoPtr
->nItemHeight
;
6614 HIMAGELIST himlOld
= 0;
6619 himlOld
= infoPtr
->himlNormal
;
6620 infoPtr
->himlNormal
= himl
;
6621 if (uView
== LVS_ICON
) update_icon_size(himl
, &infoPtr
->iconSize
);
6622 LISTVIEW_SetIconSpacing(infoPtr
, 0);
6626 himlOld
= infoPtr
->himlSmall
;
6627 infoPtr
->himlSmall
= himl
;
6628 if (uView
!= LVS_ICON
) update_icon_size(himl
, &infoPtr
->iconSize
);
6632 himlOld
= infoPtr
->himlState
;
6633 infoPtr
->himlState
= himl
;
6634 update_icon_size(himl
, &infoPtr
->iconStateSize
);
6635 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
6639 ERR("Unknown icon type=%d\n", nType
);
6643 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(infoPtr
);
6644 if (infoPtr
->nItemHeight
!= oldHeight
)
6645 LISTVIEW_UpdateScroll(infoPtr
);
6652 * Preallocates memory (does *not* set the actual count of items !)
6655 * [I] infoPtr : valid pointer to the listview structure
6656 * [I] INT : item count (projected number of items to allocate)
6657 * [I] DWORD : update flags
6663 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
6665 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems
, dwFlags
);
6667 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
6669 int precount
,topvisible
;
6671 TRACE("LVS_OWNERDATA is set!\n");
6672 if (dwFlags
& (LVSICF_NOINVALIDATEALL
| LVSICF_NOSCROLL
))
6673 FIXME("flags %s %s not implemented\n",
6674 (dwFlags
& LVSICF_NOINVALIDATEALL
) ? "LVSICF_NOINVALIDATEALL"
6676 (dwFlags
& LVSICF_NOSCROLL
) ? "LVSICF_NOSCROLL" : "");
6678 LISTVIEW_RemoveAllSelections(infoPtr
);
6680 precount
= infoPtr
->nItemCount
;
6681 topvisible
= LISTVIEW_GetTopIndex(infoPtr
) +
6682 LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
6684 infoPtr
->nItemCount
= nItems
;
6685 infoPtr
->nItemWidth
= max(LISTVIEW_CalculateMaxWidth(infoPtr
),
6686 DEFAULT_COLUMN_WIDTH
);
6688 LISTVIEW_UpdateSize(infoPtr
);
6689 LISTVIEW_UpdateScroll(infoPtr
);
6691 if (min(precount
,infoPtr
->nItemCount
) < topvisible
)
6692 LISTVIEW_InvalidateList(infoPtr
); /* FIXME: optimize */
6696 /* According to MSDN for non-LVS_OWNERDATA this is just
6697 * a performance issue. The control allocates its internal
6698 * data structures for the number of items specified. It
6699 * cuts down on the number of memory allocations. Therefore
6700 * we will just issue a WARN here
6702 WARN("for non-ownerdata performance option not implemented.\n");
6710 * Sets the position of an item.
6713 * [I] infoPtr : valid pointer to the listview structure
6714 * [I] nItem : item index
6715 * [I] pt : coordinate
6721 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, POINT pt
)
6723 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6726 TRACE("(nItem=%d, &pt=%s\n", nItem
, debugpoint(&pt
));
6728 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
6729 !(uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)) return FALSE
;
6731 /* This point value seems to be an undocumented feature.
6732 * The best guess is that it means either at the origin,
6733 * or at true beginning of the list. I will assume the origin. */
6734 if ((pt
.x
== -1) && (pt
.y
== -1))
6735 LISTVIEW_GetOrigin(infoPtr
, &pt
);
6736 else if (uView
== LVS_ICON
)
6738 pt
.x
-= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2;
6739 pt
.y
-= ICON_TOP_PADDING
;
6742 /* save the old position */
6743 old
.x
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
6744 old
.y
= (LONG
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
6746 /* Is the position changing? */
6747 if (pt
.x
== old
.x
&& pt
.y
== old
.y
) return TRUE
;
6749 /* FIXME: shouldn't we invalidate, as the item moved? */
6751 /* Allocating a POINTER for every item is too resource intensive,
6752 * so we'll keep the (x,y) in different arrays */
6753 if (DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)pt
.x
) &&
6754 DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)pt
.y
) )
6757 ERR("We should never fail here (nItem=%d, pt=%s), please report.\n",
6758 nItem
, debugpoint(&pt
));
6764 * Sets the state of one or many items.
6767 * [I] infoPtr : valid pointer to the listview structure
6768 * [I]INT : item index
6769 * [I] LPLVITEM : item or subitem info
6775 static LRESULT
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
)
6777 BOOL bResult
= TRUE
;
6780 lvItem
.iItem
= nItem
;
6781 lvItem
.iSubItem
= 0;
6782 lvItem
.mask
= LVIF_STATE
;
6783 lvItem
.state
= lpLVItem
->state
;
6784 lvItem
.stateMask
= lpLVItem
->stateMask
;
6785 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
6789 /* apply to all items */
6790 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
6791 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) bResult
= FALSE
;
6794 bResult
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
6801 * Sets the text of an item or subitem.
6804 * [I] hwnd : window handle
6805 * [I] nItem : item index
6806 * [I] lpLVItem : item or subitem info
6807 * [I] isW : TRUE if input is Unicode
6813 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
6817 if (nItem
< 0 && nItem
>= infoPtr
->nItemCount
) return FALSE
;
6819 lvItem
.iItem
= nItem
;
6820 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
6821 lvItem
.mask
= LVIF_TEXT
;
6822 lvItem
.pszText
= lpLVItem
->pszText
;
6823 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
6825 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
6827 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
6832 * Set item index that marks the start of a multiple selection.
6835 * [I] infoPtr : valid pointer to the listview structure
6839 * Index number or -1 if there is no selection mark.
6841 static LRESULT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
6843 INT nOldIndex
= infoPtr
->nSelectionMark
;
6845 TRACE("(nIndex=%d)\n", nIndex
);
6847 infoPtr
->nSelectionMark
= nIndex
;
6854 * Sets the text background color.
6857 * [I] infoPtr : valid pointer to the listview structure
6858 * [I] COLORREF : text background color
6864 static LRESULT
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrTextBk
)
6866 TRACE("(clrTextBk=%lx)\n", clrTextBk
);
6868 if (infoPtr
->clrTextBk
!= clrTextBk
)
6870 infoPtr
->clrTextBk
= clrTextBk
;
6871 LISTVIEW_InvalidateList(infoPtr
);
6879 * Sets the text foreground color.
6882 * [I] infoPtr : valid pointer to the listview structure
6883 * [I] COLORREF : text color
6889 static LRESULT
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF clrText
)
6891 TRACE("(clrText=%lx)\n", clrText
);
6893 if (infoPtr
->clrText
!= clrText
)
6895 infoPtr
->clrText
= clrText
;
6896 LISTVIEW_InvalidateList(infoPtr
);
6902 /* LISTVIEW_SetToolTips */
6903 /* LISTVIEW_SetUnicodeFormat */
6904 /* LISTVIEW_SetWorkAreas */
6908 * Callback internally used by LISTVIEW_SortItems()
6911 * [I] LPVOID : first LISTVIEW_ITEM to compare
6912 * [I] LPVOID : second LISTVIEW_ITEM to compare
6913 * [I] LPARAM : HWND of control
6916 * if first comes before second : negative
6917 * if first comes after second : positive
6918 * if first and second are equivalent : zero
6920 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
6922 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW((HWND
)lParam
, 0);
6923 LISTVIEW_ITEM
* lv_first
= (LISTVIEW_ITEM
*) DPA_GetPtr( (HDPA
)first
, 0 );
6924 LISTVIEW_ITEM
* lv_second
= (LISTVIEW_ITEM
*) DPA_GetPtr( (HDPA
)second
, 0 );
6926 /* Forward the call to the client defined callback */
6927 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
6932 * Sorts the listview items.
6935 * [I] infoPtr : valid pointer to the listview structure
6936 * [I] WPARAM : application-defined value
6937 * [I] LPARAM : pointer to comparision callback
6943 static LRESULT
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
, LPARAM lParamSort
)
6945 UINT lStyle
= infoPtr
->dwStyle
;
6947 LISTVIEW_ITEM
*lpItem
;
6948 LPVOID selectionMarkItem
;
6951 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
6953 if (lStyle
& LVS_OWNERDATA
) return FALSE
;
6955 if (!infoPtr
->hdpaItems
) return FALSE
;
6957 /* if there are 0 or 1 items, there is no need to sort */
6958 if (infoPtr
->nItemCount
< 2) return TRUE
;
6960 if (infoPtr
->nFocusedItem
>= 0)
6962 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
6963 lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0);
6964 if (lpItem
) lpItem
->state
|= LVIS_FOCUSED
;
6967 infoPtr
->pfnCompare
= pfnCompare
;
6968 infoPtr
->lParamSort
= lParamSort
;
6969 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
->hwndSelf
);
6971 /* Adjust selections and indices so that they are the way they should
6972 * be after the sort (otherwise, the list items move around, but
6973 * whatever is at the item's previous original position will be
6976 selectionMarkItem
=(infoPtr
->nSelectionMark
>=0)?DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
):NULL
;
6977 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
6979 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
6980 lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0);
6982 if (lpItem
->state
& LVIS_SELECTED
)
6983 LISTVIEW_AddSelectionRange(infoPtr
, i
, i
);
6985 LISTVIEW_RemoveSelectionRange(infoPtr
, i
, i
);
6986 if (lpItem
->state
& LVIS_FOCUSED
)
6988 infoPtr
->nFocusedItem
= i
;
6989 lpItem
->state
&= ~LVIS_FOCUSED
;
6992 if (selectionMarkItem
!= NULL
)
6993 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
6994 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6996 /* align the items */
6997 LISTVIEW_AlignTop(infoPtr
);
6999 /* refresh the display */
7000 LISTVIEW_InvalidateList(infoPtr
); /* FIXME: display should not change for [SMALL]ICON view */
7007 * Updates an items or rearranges the listview control.
7010 * [I] infoPtr : valid pointer to the listview structure
7011 * [I] INT : item index
7017 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
7019 LONG lStyle
= infoPtr
->dwStyle
;
7020 UINT uView
= lStyle
& LVS_TYPEMASK
;
7022 TRACE("(nItem=%d)\n", nItem
);
7024 if (nItem
< 0 && nItem
>= infoPtr
->nItemCount
) return FALSE
;
7026 /* rearrange with default alignment style */
7027 if ((lStyle
& LVS_AUTOARRANGE
) && ((uView
== LVS_ICON
) ||(uView
== LVS_SMALLICON
)))
7028 LISTVIEW_Arrange(infoPtr
, 0);
7030 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
7038 * Creates the listview control.
7041 * [I] hwnd : window handle
7042 * [I] lpcs : the create parameters
7048 static LRESULT
LISTVIEW_Create(HWND hwnd
, LPCREATESTRUCTW lpcs
)
7050 LISTVIEW_INFO
*infoPtr
;
7051 UINT uView
= lpcs
->style
& LVS_TYPEMASK
;
7054 TRACE("(lpcs=%p)\n", lpcs
);
7056 /* initialize info pointer */
7057 infoPtr
= (LISTVIEW_INFO
*)COMCTL32_Alloc(sizeof(LISTVIEW_INFO
));
7058 if (!infoPtr
) return -1;
7060 SetWindowLongW(hwnd
, 0, (LONG
)infoPtr
);
7062 infoPtr
->hwndSelf
= hwnd
;
7063 infoPtr
->dwStyle
= lpcs
->style
;
7064 /* determine the type of structures to use */
7065 infoPtr
->notifyFormat
= SendMessageW(GetParent(infoPtr
->hwndSelf
), WM_NOTIFYFORMAT
,
7066 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
7068 /* initialize color information */
7069 infoPtr
->clrBk
= CLR_NONE
;
7070 infoPtr
->clrText
= comctl32_color
.clrWindowText
;
7071 infoPtr
->clrTextBk
= CLR_DEFAULT
;
7072 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
7074 /* set default values */
7075 infoPtr
->nFocusedItem
= -1;
7076 infoPtr
->nSelectionMark
= -1;
7077 infoPtr
->nHotItem
= -1;
7078 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
7079 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
7080 infoPtr
->nEditLabelItem
= -1;
7082 /* get default font (icon title) */
7083 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
7084 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
7085 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
7086 LISTVIEW_SaveTextMetrics(infoPtr
);
7089 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, (LPCWSTR
)NULL
,
7090 WS_CHILD
| HDS_HORZ
| (DWORD
)((LVS_NOSORTHEADER
& lpcs
->style
)?0:HDS_BUTTONS
),
7091 0, 0, 0, 0, hwnd
, (HMENU
)0,
7092 lpcs
->hInstance
, NULL
);
7094 /* set header unicode format */
7095 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
,(WPARAM
)TRUE
,(LPARAM
)NULL
);
7097 /* set header font */
7098 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
,
7101 if (uView
== LVS_ICON
)
7103 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXICON
);
7104 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYICON
);
7106 else if (uView
== LVS_REPORT
)
7108 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
))
7110 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
7114 /* set HDS_HIDDEN flag to hide the header bar */
7115 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
,
7116 GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
) | HDS_HIDDEN
);
7120 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
7121 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
7125 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
7126 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
7129 infoPtr
->iconStateSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
7130 infoPtr
->iconStateSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
7132 /* display unsupported listview window styles */
7133 LISTVIEW_UnsupportedStyles(lpcs
->style
);
7135 /* allocate memory for the data structure */
7136 infoPtr
->hdpaItems
= DPA_Create(10);
7137 infoPtr
->hdpaPosX
= DPA_Create(10);
7138 infoPtr
->hdpaPosY
= DPA_Create(10);
7140 /* allocate memory for the selection ranges */
7141 infoPtr
->hdpaSelectionRanges
= DPA_Create(10);
7143 /* initialize size of items */
7144 infoPtr
->nItemWidth
= LISTVIEW_CalculateMaxWidth(infoPtr
);
7145 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(infoPtr
);
7147 /* initialize the hover time to -1(indicating the default system hover time) */
7148 infoPtr
->dwHoverTime
= -1;
7155 * Erases the background of the listview control.
7158 * [I] infoPtr : valid pointer to the listview structure
7159 * [I] hdc : device context handle
7165 static inline BOOL
LISTVIEW_EraseBkgnd(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
7169 TRACE("(hdc=%x)\n", hdc
);
7171 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
7173 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
7179 * Helper function for LISTVIEW_[HV]Scroll *only*.
7180 * Performs vertical/horizontal scrolling by a give amount.
7183 * [I] infoPtr : valid pointer to the listview structure
7184 * [I] dx : amount of horizontal scroll
7185 * [I] dy : amount of vertical scroll
7187 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
7189 /* now we can scroll the list */
7190 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
7191 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
7192 /* if we have focus, adjust rect */
7193 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
7194 UpdateWindow(infoPtr
->hwndSelf
);
7199 * Performs vertical scrolling.
7202 * [I] infoPtr : valid pointer to the listview structure
7203 * [I] nScrollCode : scroll code
7204 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7205 * [I] hScrollWnd : scrollbar control window handle
7211 * SB_LINEUP/SB_LINEDOWN:
7212 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7213 * for LVS_REPORT is 1 line
7214 * for LVS_LIST cannot occur
7217 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
7218 INT nScrollDiff
, HWND hScrollWnd
)
7220 UINT uView
= LISTVIEW_GetType(infoPtr
);
7221 INT nOldScrollPos
, nNewScrollPos
;
7222 SCROLLINFO scrollInfo
;
7225 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode
, nScrollDiff
);
7227 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
7229 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7230 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
7232 is_an_icon
= ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
));
7234 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
7236 nOldScrollPos
= scrollInfo
.nPos
;
7237 switch (nScrollCode
)
7243 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
7247 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
7251 nScrollDiff
= -scrollInfo
.nPage
;
7255 nScrollDiff
= scrollInfo
.nPage
;
7258 case SB_THUMBPOSITION
:
7260 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
7267 /* quit right away if pos isn't changing */
7268 if (nScrollDiff
== 0) return 0;
7270 /* calculate new position, and handle overflows */
7271 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
7272 if (nScrollDiff
> 0) {
7273 if (nNewScrollPos
< nOldScrollPos
||
7274 nNewScrollPos
> scrollInfo
.nMax
)
7275 nNewScrollPos
= scrollInfo
.nMax
;
7277 if (nNewScrollPos
> nOldScrollPos
||
7278 nNewScrollPos
< scrollInfo
.nMin
)
7279 nNewScrollPos
= scrollInfo
.nMin
;
7282 /* set the new position, and reread in case it changed */
7283 scrollInfo
.fMask
= SIF_POS
;
7284 scrollInfo
.nPos
= nNewScrollPos
;
7285 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
7287 /* carry on only if it really changed */
7288 if (nNewScrollPos
== nOldScrollPos
) return 0;
7290 /* now adjust to client coordinates */
7291 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
7292 if (uView
== LVS_REPORT
) nScrollDiff
*= infoPtr
->nItemHeight
;
7294 /* and scroll the window */
7295 scroll_list(infoPtr
, 0, nScrollDiff
);
7302 * Performs horizontal scrolling.
7305 * [I] infoPtr : valid pointer to the listview structure
7306 * [I] nScrollCode : scroll code
7307 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7308 * [I] hScrollWnd : scrollbar control window handle
7314 * SB_LINELEFT/SB_LINERIGHT:
7315 * for LVS_ICON, LVS_SMALLICON 1 pixel
7316 * for LVS_REPORT is 1 pixel
7317 * for LVS_LIST is 1 column --> which is a 1 because the
7318 * scroll is based on columns not pixels
7321 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
7322 INT nScrollDiff
, HWND hScrollWnd
)
7324 UINT uView
= LISTVIEW_GetType(infoPtr
);
7325 INT nOldScrollPos
, nNewScrollPos
;
7326 SCROLLINFO scrollInfo
;
7328 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode
, nScrollDiff
);
7330 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
7332 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7333 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
7335 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
7337 nOldScrollPos
= scrollInfo
.nPos
;
7339 switch (nScrollCode
)
7353 nScrollDiff
= -scrollInfo
.nPage
;
7357 nScrollDiff
= scrollInfo
.nPage
;
7360 case SB_THUMBPOSITION
:
7362 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
7369 /* quit right away if pos isn't changing */
7370 if (nScrollDiff
== 0) return 0;
7372 /* calculate new position, and handle overflows */
7373 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
7374 if (nScrollDiff
> 0) {
7375 if (nNewScrollPos
< nOldScrollPos
||
7376 nNewScrollPos
> scrollInfo
.nMax
)
7377 nNewScrollPos
= scrollInfo
.nMax
;
7379 if (nNewScrollPos
> nOldScrollPos
||
7380 nNewScrollPos
< scrollInfo
.nMin
)
7381 nNewScrollPos
= scrollInfo
.nMin
;
7384 /* set the new position, and reread in case it changed */
7385 scrollInfo
.fMask
= SIF_POS
;
7386 scrollInfo
.nPos
= nNewScrollPos
;
7387 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
7389 /* carry on only if it really changed */
7390 if (nNewScrollPos
== nOldScrollPos
) return 0;
7392 if(uView
== LVS_REPORT
)
7393 LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
7395 /* now adjust to client coordinates */
7396 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
7397 if (uView
== LVS_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
7399 /* and scroll the window */
7400 scroll_list(infoPtr
, nScrollDiff
, 0);
7405 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
7407 UINT uView
= LISTVIEW_GetType(infoPtr
);
7408 INT gcWheelDelta
= 0;
7409 UINT pulScrollLines
= 3;
7410 SCROLLINFO scrollInfo
;
7412 TRACE("(wheelDelta=%d)\n", wheelDelta
);
7414 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
7415 gcWheelDelta
-= wheelDelta
;
7417 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7418 scrollInfo
.fMask
= SIF_POS
;
7425 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7426 * should be fixed in the future.
7428 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
7429 LISTVIEW_VScroll(infoPtr
, SB_THUMBPOSITION
,
7430 scrollInfo
.nPos
+ (gcWheelDelta
< 0) ?
7431 LISTVIEW_SCROLL_ICON_LINE_SIZE
:
7432 -LISTVIEW_SCROLL_ICON_LINE_SIZE
, 0);
7436 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
7438 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
7440 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
7441 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
7442 LISTVIEW_VScroll(infoPtr
, SB_THUMBPOSITION
, scrollInfo
.nPos
+ cLineScroll
, 0);
7448 LISTVIEW_HScroll(infoPtr
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0, 0);
7459 * [I] infoPtr : valid pointer to the listview structure
7460 * [I] INT : virtual key
7461 * [I] LONG : key data
7466 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
7468 UINT uView
= LISTVIEW_GetType(infoPtr
);
7470 NMLVKEYDOWN nmKeyDown
;
7472 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey
, lKeyData
);
7474 /* send LVN_KEYDOWN notification */
7475 nmKeyDown
.wVKey
= nVirtualKey
;
7476 nmKeyDown
.flags
= 0;
7477 notify(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
7479 switch (nVirtualKey
)
7482 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
7484 notify_return(infoPtr
);
7485 notify_itemactivate(infoPtr
);
7490 if (infoPtr
->nItemCount
> 0)
7495 if (infoPtr
->nItemCount
> 0)
7496 nItem
= infoPtr
->nItemCount
- 1;
7500 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
7504 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
7508 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
7512 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
7516 if (uView
== LVS_REPORT
)
7517 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
);
7519 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
7520 * LISTVIEW_GetCountPerRow(infoPtr
);
7521 if(nItem
< 0) nItem
= 0;
7525 if (uView
== LVS_REPORT
)
7526 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
);
7528 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
7529 * LISTVIEW_GetCountPerRow(infoPtr
);
7530 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
7534 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
))
7535 LISTVIEW_KeySelection(infoPtr
, nItem
);
7545 * [I] infoPtr : valid pointer to the listview structure
7550 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
7554 /* if we did not have the focus, there's nothing to do */
7555 if (!infoPtr
->bFocus
) return 0;
7557 /* send NM_KILLFOCUS notification */
7558 notify_killfocus(infoPtr
);
7560 /* if we have a focus rectagle, get rid of it */
7561 LISTVIEW_ShowFocusRect(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
7563 /* set window focus flag */
7564 infoPtr
->bFocus
= FALSE
;
7566 /* invalidate the selected items before reseting focus flag */
7567 LISTVIEW_InvalidateSelectedItems(infoPtr
);
7574 * Processes double click messages (left mouse button).
7577 * [I] infoPtr : valid pointer to the listview structure
7578 * [I] wKey : key flag
7579 * [I] pts : mouse coordinate
7584 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, POINTS pts
)
7586 LVHITTESTINFO htInfo
;
7589 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, pts
.x
, pts
.y
);
7591 htInfo
.pt
.x
= pts
.x
;
7592 htInfo
.pt
.y
= pts
.y
;
7594 /* send NM_DBLCLK notification */
7595 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
7596 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
);
7597 nmlv
.iItem
= htInfo
.iItem
;
7598 nmlv
.iSubItem
= htInfo
.iSubItem
;
7599 nmlv
.ptAction
= htInfo
.pt
;
7600 notify_listview(infoPtr
, NM_DBLCLK
, &nmlv
);
7602 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7603 if(nmlv
.iItem
!= -1)
7604 notify_itemactivate(infoPtr
);
7611 * Processes mouse down messages (left mouse button).
7614 * [I] infoPtr : valid pointer to the listview structure
7615 * [I] wKey : key flag
7616 * [I] pts : mouse coordinate
7621 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, POINTS pts
)
7623 LONG lStyle
= infoPtr
->dwStyle
;
7624 static BOOL bGroupSelect
= TRUE
;
7625 POINT pt
= { pts
.x
, pts
.y
};
7628 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, pts
.x
, pts
.y
);
7630 /* FIXME: NM_CLICK */
7632 /* send NM_RELEASEDCAPTURE notification */
7633 notify_releasedcapture(infoPtr
);
7635 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
7637 /* set left button down flag */
7638 infoPtr
->bLButtonDown
= TRUE
;
7640 nItem
= LISTVIEW_GetItemAtPt(infoPtr
, pt
);
7641 TRACE("at %s, nItem=%d\n", debugpoint(&pt
), nItem
);
7642 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
7644 if (lStyle
& LVS_SINGLESEL
)
7646 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
7647 && infoPtr
->nEditLabelItem
== -1)
7648 infoPtr
->nEditLabelItem
= nItem
;
7650 LISTVIEW_SetSelection(infoPtr
, nItem
);
7654 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
7657 LISTVIEW_AddGroupSelection(infoPtr
, nItem
);
7662 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
7663 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
7665 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
7666 infoPtr
->nSelectionMark
= nItem
;
7669 else if (wKey
& MK_CONTROL
)
7673 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
7675 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
7676 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
7677 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
7678 infoPtr
->nSelectionMark
= nItem
;
7680 else if (wKey
& MK_SHIFT
)
7682 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
7686 BOOL was_selected
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
7688 /* set selection (clears other pre-existing selections) */
7689 LISTVIEW_SetSelection(infoPtr
, nItem
);
7691 if (was_selected
&& infoPtr
->nEditLabelItem
== -1)
7692 infoPtr
->nEditLabelItem
= nItem
;
7698 /* remove all selections */
7699 LISTVIEW_RemoveAllSelections(infoPtr
);
7707 * Processes mouse up messages (left mouse button).
7710 * [I] infoPtr : valid pointer to the listview structure
7711 * [I] wKey : key flag
7712 * [I] pts : mouse coordinate
7717 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, POINTS pts
)
7719 LVHITTESTINFO lvHitTestInfo
;
7722 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, pts
.x
, pts
.y
);
7724 if (!infoPtr
->bLButtonDown
) return 0;
7726 lvHitTestInfo
.pt
.x
= pts
.x
;
7727 lvHitTestInfo
.pt
.y
= pts
.y
;
7729 /* send NM_CLICK notification */
7730 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
7731 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
);
7732 nmlv
.iItem
= lvHitTestInfo
.iItem
;
7733 nmlv
.iSubItem
= lvHitTestInfo
.iSubItem
;
7734 nmlv
.ptAction
= lvHitTestInfo
.pt
;
7735 notify_listview(infoPtr
, NM_CLICK
, &nmlv
);
7737 /* set left button flag */
7738 infoPtr
->bLButtonDown
= FALSE
;
7740 if(infoPtr
->nEditLabelItem
!= -1)
7742 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&&
7743 (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
7744 LISTVIEW_EditLabelT(infoPtr
, lvHitTestInfo
.iItem
, TRUE
);
7745 infoPtr
->nEditLabelItem
= -1;
7753 * Destroys the listview control (called after WM_DESTROY).
7756 * [I] infoPtr : valid pointer to the listview structure
7761 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
7763 LONG lStyle
= infoPtr
->dwStyle
;
7767 /* delete all items */
7768 LISTVIEW_DeleteAllItems(infoPtr
);
7770 /* destroy data structure */
7771 DPA_Destroy(infoPtr
->hdpaItems
);
7772 DPA_Destroy(infoPtr
->hdpaSelectionRanges
);
7774 /* destroy image lists */
7775 if (!(lStyle
& LVS_SHAREIMAGELISTS
))
7777 /* FIXME: If the caller does a ImageList_Destroy and then we
7778 * do this code the area will be freed twice. Currently
7779 * this generates an "err:heap:HEAP_ValidateInUseArena
7780 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
7781 * has PREV_FREE flag" sometimes.
7783 * We will leak the memory till we figure out how to fix
7785 if (infoPtr
->himlNormal
)
7786 ImageList_Destroy(infoPtr
->himlNormal
);
7787 if (infoPtr
->himlSmall
)
7788 ImageList_Destroy(infoPtr
->himlSmall
);
7789 if (infoPtr
->himlState
)
7790 ImageList_Destroy(infoPtr
->himlState
);
7793 /* destroy font, bkgnd brush */
7795 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
7796 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
7798 /* free listview info pointer*/
7799 COMCTL32_Free(infoPtr
);
7801 SetWindowLongW(infoPtr
->hwndSelf
, 0, 0);
7807 * Handles notifications from children.
7810 * [I] infoPtr : valid pointer to the listview structure
7811 * [I] INT : control identifier
7812 * [I] LPNMHDR : notification information
7817 static LRESULT
LISTVIEW_Notify(LISTVIEW_INFO
*infoPtr
, INT nCtrlId
, LPNMHDR lpnmh
)
7819 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId
, lpnmh
);
7821 if (lpnmh
->hwndFrom
== infoPtr
->hwndHeader
)
7823 /* handle notification from header control */
7824 if (lpnmh
->code
== HDN_ENDTRACKW
)
7826 infoPtr
->nItemWidth
= LISTVIEW_CalculateMaxWidth(infoPtr
);
7827 LISTVIEW_InvalidateList(infoPtr
); /* FIXME: optimize */
7829 else if(lpnmh
->code
== HDN_ITEMCLICKW
|| lpnmh
->code
== HDN_ITEMCLICKA
)
7831 /* Handle sorting by Header Column */
7834 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
7836 nmlv
.iSubItem
= ((LPNMHEADERW
)lpnmh
)->iItem
;
7837 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
7839 else if(lpnmh
->code
== NM_RELEASEDCAPTURE
)
7841 /* Idealy this should be done in HDN_ENDTRACKA
7842 * but since SetItemBounds in Header.c is called after
7843 * the notification is sent, it is neccessary to handle the
7844 * update of the scroll bar here (Header.c works fine as it is,
7845 * no need to disturb it)
7847 infoPtr
->nItemWidth
= LISTVIEW_CalculateMaxWidth(infoPtr
);
7848 LISTVIEW_UpdateScroll(infoPtr
);
7849 LISTVIEW_InvalidateList(infoPtr
); /* FIXME: optimize */
7859 * Determines the type of structure to use.
7862 * [I] infoPtr : valid pointer to the listview structureof the sender
7863 * [I] HWND : listview window handle
7864 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
7869 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
7871 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom
, nCommand
);
7873 if (nCommand
== NF_REQUERY
)
7874 infoPtr
->notifyFormat
= SendMessageW(hwndFrom
, WM_NOTIFYFORMAT
,
7875 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
7881 * Paints/Repaints the listview control.
7884 * [I] infoPtr : valid pointer to the listview structure
7885 * [I] HDC : device context handle
7890 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
7892 TRACE("(hdc=%x)\n", hdc
);
7895 LISTVIEW_Refresh(infoPtr
, hdc
);
7900 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
7902 if (ps
.fErase
) LISTVIEW_FillBkgnd(infoPtr
, hdc
, &ps
.rcPaint
);
7903 LISTVIEW_Refresh(infoPtr
, hdc
);
7904 EndPaint(infoPtr
->hwndSelf
, &ps
);
7912 * Processes double click messages (right mouse button).
7915 * [I] infoPtr : valid pointer to the listview structure
7916 * [I] wKey : key flag
7917 * [I] pts : mouse coordinate
7922 static LRESULT
LISTVIEW_RButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, POINTS pts
)
7924 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, pts
.x
, pts
.y
);
7926 /* send NM_RELEASEDCAPTURE notification */
7927 notify_releasedcapture(infoPtr
);
7929 /* send NM_RDBLCLK notification */
7930 notify_rdblclk(infoPtr
);
7937 * Processes mouse down messages (right mouse button).
7940 * [I] infoPtr : valid pointer to the listview structure
7941 * [I] wKey : key flag
7942 * [I] pts : mouse coordinate
7947 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, POINTS pts
)
7949 LVHITTESTINFO lvHitTestInfo
;
7953 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, pts
.x
, pts
.y
);
7955 /* FIXME: NM_CLICK */
7957 /* send NM_RELEASEDCAPTURE notification */
7958 notify_releasedcapture(infoPtr
);
7960 /* make sure the listview control window has the focus */
7961 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
7963 /* set right button down flag */
7964 infoPtr
->bRButtonDown
= TRUE
;
7966 /* determine the index of the selected item */
7967 lvHitTestInfo
.pt
.x
= pts
.x
;
7968 lvHitTestInfo
.pt
.y
= pts
.y
;
7969 nItem
= LISTVIEW_GetItemAtPt(infoPtr
, lvHitTestInfo
.pt
);
7971 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
7973 LISTVIEW_SetItemFocus(infoPtr
,nItem
);
7974 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
7975 !LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
7976 LISTVIEW_SetSelection(infoPtr
, nItem
);
7980 LISTVIEW_RemoveAllSelections(infoPtr
);
7984 /* Send NM_RClICK notification */
7985 ZeroMemory(&nmlv
, sizeof(nmlv
));
7986 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
);
7987 nmlv
.iItem
= lvHitTestInfo
.iItem
;
7988 nmlv
.iSubItem
= lvHitTestInfo
.iSubItem
;
7989 nmlv
.ptAction
= lvHitTestInfo
.pt
;
7990 notify_listview(infoPtr
, NM_RCLICK
, &nmlv
);
7997 * Processes mouse up messages (right mouse button).
8000 * [I] infoPtr : valid pointer to the listview structure
8001 * [I] wKey : key flag
8002 * [I] pts : mouse coordinate
8007 static LRESULT
LISTVIEW_RButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, POINTS pts
)
8009 POINT pt
= { pts
.x
, pts
.y
};
8011 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, pts
.x
, pts
.y
);
8013 if (!infoPtr
->bRButtonDown
) return 0;
8015 /* set button flag */
8016 infoPtr
->bRButtonDown
= FALSE
;
8018 /* Change to screen coordinate for WM_CONTEXTMENU */
8019 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
8021 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8022 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
8023 (WPARAM
)infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
8034 * [I] infoPtr : valid pointer to the listview structure
8035 * [I] hwnd : window handle of window containing the cursor
8036 * [I] nHittest : hit-test code
8037 * [I] wMouseMsg : ideintifier of the mouse message
8040 * TRUE if cursor is set
8043 static BOOL
LISTVIEW_SetCursor(LISTVIEW_INFO
*infoPtr
, HWND hwnd
, UINT nHittest
, UINT wMouseMsg
)
8047 if(!(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)) return FALSE
;
8049 if(!infoPtr
->hHotCursor
) return FALSE
;
8052 if (LISTVIEW_GetItemAtPt(infoPtr
, pt
) < 0) return FALSE
;
8054 SetCursor(infoPtr
->hHotCursor
);
8064 * [I] infoPtr : valid pointer to the listview structure
8065 * [I] infoPtr : handle of previously focused window
8070 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
8072 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus
);
8074 /* if we have the focus already, there's nothing to do */
8075 if (infoPtr
->bFocus
) return 0;
8077 /* send NM_SETFOCUS notification */
8078 notify_setfocus(infoPtr
);
8080 /* set window focus flag */
8081 infoPtr
->bFocus
= TRUE
;
8083 /* put the focus rect back on */
8084 LISTVIEW_ShowFocusRect(infoPtr
, infoPtr
->nFocusedItem
, TRUE
);
8086 /* redraw all visible selected items */
8087 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8097 * [I] infoPtr : valid pointer to the listview structure
8098 * [I] HFONT : font handle
8099 * [I] WORD : redraw flag
8104 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
8106 HFONT oldFont
= infoPtr
->hFont
;
8108 TRACE("(hfont=%x,redraw=%hu)\n", hFont
, fRedraw
);
8110 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
8111 if (infoPtr
->hFont
== oldFont
) return 0;
8113 LISTVIEW_SaveTextMetrics(infoPtr
);
8115 if (LISTVIEW_GetType(infoPtr
) == LVS_REPORT
)
8116 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
8118 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
8125 * Message handling for WM_SETREDRAW.
8126 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8129 * [I] infoPtr : valid pointer to the listview structure
8130 * [I] bRedraw: state of redraw flag
8133 * DefWinProc return value
8135 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
8137 /* FIXME: this is bogus */
8138 LRESULT lResult
= DefWindowProcW(infoPtr
->hwndSelf
, WM_SETREDRAW
, bRedraw
, 0);
8140 RedrawWindow(infoPtr
->hwndSelf
, NULL
, 0,
8141 RDW_INVALIDATE
| RDW_FRAME
| RDW_ERASE
| RDW_ALLCHILDREN
| RDW_ERASENOW
);
8147 * Resizes the listview control. This function processes WM_SIZE
8148 * messages. At this time, the width and height are not used.
8151 * [I] infoPtr : valid pointer to the listview structure
8152 * [I] WORD : new width
8153 * [I] WORD : new height
8158 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
8160 LONG lStyle
= infoPtr
->dwStyle
;
8161 UINT uView
= lStyle
& LVS_TYPEMASK
;
8163 TRACE("(width=%d, height=%d)\n", Width
, Height
);
8165 if (LISTVIEW_UpdateSize(infoPtr
))
8167 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
8169 if (lStyle
& LVS_ALIGNLEFT
)
8170 LISTVIEW_AlignLeft(infoPtr
);
8172 LISTVIEW_AlignTop(infoPtr
);
8175 LISTVIEW_UpdateScroll(infoPtr
);
8177 LISTVIEW_InvalidateList(infoPtr
); /* FIXME: optimize */
8185 * Sets the size information.
8188 * [I] infoPtr : valid pointer to the listview structure
8191 * Zero if no size change
8194 static BOOL
LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
8196 LONG lStyle
= infoPtr
->dwStyle
;
8197 UINT uView
= lStyle
& LVS_TYPEMASK
;
8201 GetClientRect(infoPtr
->hwndSelf
, &rcList
);
8202 CopyRect(&rcOld
,&(infoPtr
->rcList
));
8203 infoPtr
->rcList
.left
= 0;
8204 infoPtr
->rcList
.right
= max(rcList
.right
- rcList
.left
, 1);
8205 infoPtr
->rcList
.top
= 0;
8206 infoPtr
->rcList
.bottom
= max(rcList
.bottom
- rcList
.top
, 1);
8208 if (uView
== LVS_LIST
)
8210 /* Apparently the "LIST" style is supposed to have the same
8211 * number of items in a column even if there is no scroll bar.
8212 * Since if a scroll bar already exists then the bottom is already
8213 * reduced, only reduce if the scroll bar does not currently exist.
8214 * The "2" is there to mimic the native control. I think it may be
8215 * related to either padding or edges. (GLA 7/2002)
8217 if (!(lStyle
& WS_HSCROLL
))
8219 INT nHScrollHeight
= GetSystemMetrics(SM_CYHSCROLL
);
8220 if (infoPtr
->rcList
.bottom
> nHScrollHeight
)
8221 infoPtr
->rcList
.bottom
-= (nHScrollHeight
+ 2);
8225 if (infoPtr
->rcList
.bottom
> 2)
8226 infoPtr
->rcList
.bottom
-= 2;
8229 else if (uView
== LVS_REPORT
)
8236 Header_Layout(infoPtr
->hwndHeader
, &hl
);
8238 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
8240 if (!(LVS_NOCOLUMNHEADER
& lStyle
))
8241 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
8243 return (EqualRect(&rcOld
,&(infoPtr
->rcList
)));
8248 * Processes WM_STYLECHANGED messages.
8251 * [I] infoPtr : valid pointer to the listview structure
8252 * [I] WPARAM : window style type (normal or extended)
8253 * [I] LPSTYLESTRUCT : window style information
8258 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
8261 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
8262 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
8263 RECT rcList
= infoPtr
->rcList
;
8265 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8266 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
8268 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8270 if (wStyleType
== GWL_STYLE
)
8272 infoPtr
->dwStyle
= lpss
->styleNew
;
8274 if (uOldView
== LVS_REPORT
)
8275 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
8277 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
8278 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
8279 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
8281 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
8282 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
8283 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
8285 /* If switching modes, then start with no scroll bars and then
8288 if (uNewView
!= uOldView
)
8289 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
8291 if (uNewView
== LVS_ICON
)
8295 /* First readjust the iconSize and if necessary the iconSpacing */
8296 oldcx
= infoPtr
->iconSize
.cx
;
8297 oldcy
= infoPtr
->iconSize
.cy
;
8298 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXICON
);
8299 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYICON
);
8300 if (infoPtr
->himlNormal
!= NULL
)
8303 ImageList_GetIconSize(infoPtr
->himlNormal
, &cx
, &cy
);
8304 infoPtr
->iconSize
.cx
= cx
;
8305 infoPtr
->iconSize
.cy
= cy
;
8307 if ((infoPtr
->iconSize
.cx
!= oldcx
) || (infoPtr
->iconSize
.cy
!= oldcy
))
8309 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8310 oldcx
, oldcy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
8311 LISTVIEW_SetIconSpacing(infoPtr
,0);
8314 /* Now update the full item width and height */
8315 infoPtr
->nItemWidth
= LISTVIEW_CalculateMaxWidth(infoPtr
);
8316 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(infoPtr
);
8317 if (lpss
->styleNew
& LVS_ALIGNLEFT
)
8318 LISTVIEW_AlignLeft(infoPtr
);
8320 LISTVIEW_AlignTop(infoPtr
);
8322 else if (uNewView
== LVS_REPORT
)
8329 Header_Layout(infoPtr
->hwndHeader
, &hl
);
8330 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
8332 if (!(LVS_NOCOLUMNHEADER
& lpss
->styleNew
))
8333 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
8335 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
8336 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
8337 infoPtr
->nItemWidth
= LISTVIEW_CalculateMaxWidth(infoPtr
);
8338 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(infoPtr
);
8340 else if (uNewView
== LVS_LIST
)
8342 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
8343 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
8344 infoPtr
->nItemWidth
= LISTVIEW_CalculateMaxWidth(infoPtr
);
8345 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(infoPtr
);
8349 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
8350 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
8351 infoPtr
->nItemWidth
= LISTVIEW_CalculateMaxWidth(infoPtr
);
8352 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(infoPtr
);
8353 if (lpss
->styleNew
& LVS_ALIGNLEFT
)
8354 LISTVIEW_AlignLeft(infoPtr
);
8356 LISTVIEW_AlignTop(infoPtr
);
8359 /* update the size of the client area */
8360 LISTVIEW_UpdateSize(infoPtr
);
8362 /* add scrollbars if needed */
8363 LISTVIEW_UpdateScroll(infoPtr
);
8365 /* invalidate client area + erase background */
8366 LISTVIEW_InvalidateList(infoPtr
); /* FIXME: optimize */
8368 /* print the list of unsupported window styles */
8369 LISTVIEW_UnsupportedStyles(lpss
->styleNew
);
8372 /* If they change the view and we have an active edit control
8373 we will need to kill the control since the redraw will
8374 misplace the edit control.
8376 if (infoPtr
->bEditing
&&
8377 ((uNewView
& (LVS_ICON
|LVS_LIST
|LVS_SMALLICON
)) !=
8378 ((LVS_ICON
|LVS_LIST
|LVS_SMALLICON
) & uOldView
)))
8380 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8388 * Window procedure of the listview control.
8391 static LRESULT WINAPI
8392 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
8394 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8396 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg
, wParam
, lParam
);
8398 if (!infoPtr
&& (uMsg
!= WM_CREATE
))
8399 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
8403 infoPtr
->dwStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
8408 case LVM_APPROXIMATEVIEWRECT
:
8409 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
8410 LOWORD(lParam
), HIWORD(lParam
));
8412 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
8414 /* case LVN_CANCELEDITLABEL */
8416 /* case LVM_CREATEDRAGIMAGE: */
8418 case LVM_DELETEALLITEMS
:
8419 return LISTVIEW_DeleteAllItems(infoPtr
);
8421 case LVM_DELETECOLUMN
:
8422 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
8424 case LVM_DELETEITEM
:
8425 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
8427 case LVM_EDITLABELW
:
8428 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, TRUE
);
8430 case LVM_EDITLABELA
:
8431 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, FALSE
);
8433 /* case LVN_ENABLEGROUPVIEW: */
8435 case LVM_ENSUREVISIBLE
:
8436 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
8439 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
8442 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
8444 case LVM_GETBKCOLOR
:
8445 return infoPtr
->clrBk
;
8447 /* case LVM_GETBKIMAGE: */
8449 case LVM_GETCALLBACKMASK
:
8450 return infoPtr
->uCallbackMask
;
8452 case LVM_GETCOLUMNA
:
8453 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
8455 case LVM_GETCOLUMNW
:
8456 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
8458 case LVM_GETCOLUMNORDERARRAY
:
8459 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
8461 case LVM_GETCOLUMNWIDTH
:
8462 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
8464 case LVM_GETCOUNTPERPAGE
:
8465 return LISTVIEW_GetCountPerPage(infoPtr
);
8467 case LVM_GETEDITCONTROL
:
8468 return (LRESULT
)infoPtr
->hwndEdit
;
8470 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
8471 return infoPtr
->dwLvExStyle
;
8474 return (LRESULT
)infoPtr
->hwndHeader
;
8476 case LVM_GETHOTCURSOR
:
8477 return infoPtr
->hHotCursor
;
8479 case LVM_GETHOTITEM
:
8480 return infoPtr
->nHotItem
;
8482 case LVM_GETHOVERTIME
:
8483 return infoPtr
->dwHoverTime
;
8485 case LVM_GETIMAGELIST
:
8486 return LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
8488 /* case LVN_GETINSERTMARK: */
8490 /* case LVN_GETINSERTMARKCOLOR: */
8492 /* case LVN_GETINSERTMARKRECT: */
8494 case LVM_GETISEARCHSTRINGA
:
8495 case LVM_GETISEARCHSTRINGW
:
8496 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8500 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
8503 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
8505 case LVM_GETITEMCOUNT
:
8506 return infoPtr
->nItemCount
;
8508 case LVM_GETITEMPOSITION
:
8509 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
8511 case LVM_GETITEMRECT
:
8512 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
8514 case LVM_GETITEMSPACING
:
8515 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
8517 case LVM_GETITEMSTATE
:
8518 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
8520 case LVM_GETITEMTEXTA
:
8521 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
8523 case LVM_GETITEMTEXTW
:
8524 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
8526 case LVM_GETNEXTITEM
:
8527 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
8529 case LVM_GETNUMBEROFWORKAREAS
:
8530 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8534 return LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
8536 /* case LVN_GETOUTLINECOLOR: */
8538 /* case LVM_GETSELECTEDCOLUMN: */
8540 case LVM_GETSELECTEDCOUNT
:
8541 return LISTVIEW_GetSelectedCount(infoPtr
);
8543 case LVM_GETSELECTIONMARK
:
8544 return infoPtr
->nSelectionMark
;
8546 case LVM_GETSTRINGWIDTHA
:
8547 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, FALSE
);
8549 case LVM_GETSTRINGWIDTHW
:
8550 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, TRUE
);
8552 case LVM_GETSUBITEMRECT
:
8553 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
8555 case LVM_GETTEXTBKCOLOR
:
8556 return infoPtr
->clrTextBk
;
8558 case LVM_GETTEXTCOLOR
:
8559 return infoPtr
->clrText
;
8561 /* case LVN_GETTILEINFO: */
8563 /* case LVN_GETTILEVIEWINFO: */
8565 case LVM_GETTOOLTIPS
:
8566 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8569 case LVM_GETTOPINDEX
:
8570 return LISTVIEW_GetTopIndex(infoPtr
);
8572 /*case LVM_GETUNICODEFORMAT:
8573 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8576 case LVM_GETVIEWRECT
:
8577 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
8579 case LVM_GETWORKAREAS
:
8580 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8583 /* case LVN_HASGROUP: */
8586 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
);
8588 case LVM_INSERTCOLUMNA
:
8589 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
8591 case LVM_INSERTCOLUMNW
:
8592 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
8594 /* case LVN_INSERTGROUP: */
8596 /* case LVN_INSERTGROUPSORTED: */
8598 case LVM_INSERTITEMA
:
8599 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
8601 case LVM_INSERTITEMW
:
8602 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
8604 /* case LVN_INSERTMARKHITTEST: */
8606 /* case LVN_ISGROUPVIEWENABLED: */
8608 /* case LVN_MAPIDTOINDEX: */
8610 /* case LVN_INEDXTOID: */
8612 /* case LVN_MOVEGROUP: */
8614 /* case LVN_MOVEITEMTOGROUP: */
8616 case LVM_REDRAWITEMS
:
8617 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
8619 /* case LVN_REMOVEALLGROUPS: */
8621 /* case LVN_REMOVEGROUP: */
8624 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
8626 case LVM_SETBKCOLOR
:
8627 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
8629 /* case LVM_SETBKIMAGE: */
8631 case LVM_SETCALLBACKMASK
:
8632 infoPtr
->uCallbackMask
= (UINT
)wParam
;
8635 case LVM_SETCOLUMNA
:
8636 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
8638 case LVM_SETCOLUMNW
:
8639 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
8641 case LVM_SETCOLUMNORDERARRAY
:
8642 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
8644 case LVM_SETCOLUMNWIDTH
:
8645 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, SLOWORD(lParam
));
8647 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
8648 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
8650 /* case LVN_SETGROUPINFO: */
8652 /* case LVN_SETGROUPMETRICS: */
8654 case LVM_SETHOTCURSOR
:
8655 return LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
8657 case LVM_SETHOTITEM
:
8658 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
8660 case LVM_SETHOVERTIME
:
8661 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)wParam
);
8663 case LVM_SETICONSPACING
:
8664 return LISTVIEW_SetIconSpacing(infoPtr
, (DWORD
)lParam
);
8666 case LVM_SETIMAGELIST
:
8667 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
8669 /* case LVN_SETINFOTIP: */
8671 /* case LVN_SETINSERTMARK: */
8673 /* case LVN_SETINSERTMARKCOLOR: */
8676 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
8679 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
8681 case LVM_SETITEMCOUNT
:
8682 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
8684 case LVM_SETITEMPOSITION
:
8686 POINT pt
= { SLOWORD(lParam
), SHIWORD(lParam
) };
8687 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, pt
);
8690 case LVM_SETITEMPOSITION32
:
8691 if (lParam
== 0) return FALSE
;
8692 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, *((POINT
*)lParam
));
8694 case LVM_SETITEMSTATE
:
8695 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
8697 case LVM_SETITEMTEXTA
:
8698 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
8700 case LVM_SETITEMTEXTW
:
8701 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
8703 /* case LVN_SETOUTLINECOLOR: */
8705 /* case LVN_SETSELECTEDCOLUMN: */
8707 case LVM_SETSELECTIONMARK
:
8708 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
8710 case LVM_SETTEXTBKCOLOR
:
8711 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
8713 case LVM_SETTEXTCOLOR
:
8714 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
8716 /* case LVN_SETTILEINFO: */
8718 /* case LVN_SETTILEVIEWINFO: */
8720 /* case LVN_SETTILEWIDTH: */
8722 /* case LVM_SETTOOLTIPS: */
8724 /* case LVM_SETUNICODEFORMAT: */
8726 /* case LVN_SETVIEW: */
8728 /* case LVM_SETWORKAREAS: */
8730 /* case LVN_SORTGROUPS: */
8733 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
);
8735 case LVM_SUBITEMHITTEST
:
8736 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
);
8739 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
8742 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
8745 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
8748 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
8751 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
8754 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
8757 return infoPtr
->hFont
;
8760 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
8763 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
8766 return LISTVIEW_KillFocus(infoPtr
);
8768 case WM_LBUTTONDBLCLK
:
8769 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
8771 case WM_LBUTTONDOWN
:
8772 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
8775 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
8778 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
8781 return LISTVIEW_MouseHover(infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
8784 return LISTVIEW_NCDestroy(infoPtr
);
8787 return LISTVIEW_Notify(infoPtr
, (INT
)wParam
, (LPNMHDR
)lParam
);
8789 case WM_NOTIFYFORMAT
:
8790 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
8793 return LISTVIEW_Paint(infoPtr
, (HDC
)wParam
);
8795 case WM_RBUTTONDBLCLK
:
8796 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
8798 case WM_RBUTTONDOWN
:
8799 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
8802 return LISTVIEW_RButtonUp(infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
8805 if(LISTVIEW_SetCursor(infoPtr
, (HWND
)wParam
, LOWORD(lParam
), HIWORD(lParam
)))
8810 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
8813 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
8816 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
8819 return LISTVIEW_Size(infoPtr
, (int)SLOWORD(lParam
), (int)SHIWORD(lParam
));
8821 case WM_STYLECHANGED
:
8822 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
8824 case WM_SYSCOLORCHANGE
:
8825 COMCTL32_RefreshSysColors();
8828 /* case WM_TIMER: */
8831 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
8834 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
8835 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
8836 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
8838 case WM_WINDOWPOSCHANGED
:
8839 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
)) {
8840 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
8841 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
8842 LISTVIEW_UpdateSize(infoPtr
);
8843 LISTVIEW_UpdateScroll(infoPtr
);
8845 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
8847 /* case WM_WININICHANGE: */
8850 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
))
8851 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg
, wParam
, lParam
);
8854 /* call default window procedure */
8855 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
8863 * Registers the window class.
8871 void LISTVIEW_Register(void)
8875 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
8876 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
8877 wndClass
.lpfnWndProc
= (WNDPROC
)LISTVIEW_WindowProc
;
8878 wndClass
.cbClsExtra
= 0;
8879 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
8880 wndClass
.hCursor
= LoadCursorW(0, IDC_ARROWW
);
8881 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
8882 wndClass
.lpszClassName
= WC_LISTVIEWW
;
8883 RegisterClassW(&wndClass
);
8888 * Unregisters the window class.
8896 void LISTVIEW_Unregister(void)
8898 UnregisterClassW(WC_LISTVIEWW
, (HINSTANCE
)NULL
);
8903 * Handle any WM_COMMAND messages
8909 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
8911 switch (HIWORD(wParam
))
8916 * Adjust the edit window size
8919 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
8920 HFONT hFont
, hOldFont
= 0;
8925 len
= GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
8926 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
8928 /* Select font to get the right dimension of the string */
8929 hFont
= SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
8932 hOldFont
= SelectObject(hdc
, hFont
);
8935 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
8937 TEXTMETRICW textMetric
;
8939 /* Add Extra spacing for the next character */
8940 GetTextMetricsW(hdc
, &textMetric
);
8941 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
8949 rect
.bottom
- rect
.top
,
8950 SWP_DRAWFRAME
|SWP_NOMOVE
);
8953 SelectObject(hdc
, hOldFont
);
8955 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
8961 return SendMessageW (GetParent (infoPtr
->hwndSelf
), WM_COMMAND
, wParam
, lParam
);
8970 * Subclassed edit control windproc function
8976 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
,
8977 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
8979 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(GetParent(hwnd
), 0);
8980 static BOOL bIgnoreKillFocus
= FALSE
;
8981 BOOL cancel
= FALSE
;
8983 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8984 hwnd
, uMsg
, wParam
, lParam
, isW
);
8989 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
8992 if(bIgnoreKillFocus
) return TRUE
;
8997 WNDPROC editProc
= infoPtr
->EditWndProc
;
8998 infoPtr
->EditWndProc
= 0;
8999 SetWindowLongW(hwnd
, GWL_WNDPROC
, (LONG
)editProc
);
9000 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
9004 if (VK_ESCAPE
== (INT
)wParam
)
9009 else if (VK_RETURN
== (INT
)wParam
)
9013 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
9016 if (infoPtr
->bEditing
)
9018 LPWSTR buffer
= NULL
;
9022 DWORD len
= isW
? GetWindowTextLengthW(hwnd
) : GetWindowTextLengthA(hwnd
);
9026 if ( (buffer
= COMCTL32_Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))) )
9028 if (isW
) GetWindowTextW(hwnd
, buffer
, len
+1);
9029 else GetWindowTextA(hwnd
, (CHAR
*)buffer
, len
+1);
9033 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9034 /* eg. Using a messagebox */
9035 bIgnoreKillFocus
= TRUE
;
9036 LISTVIEW_EndEditLabelT(infoPtr
, buffer
, isW
);
9038 if (buffer
) COMCTL32_Free(buffer
);
9040 bIgnoreKillFocus
= FALSE
;
9043 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
9049 * Subclassed edit control windproc function
9055 LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9057 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
9062 * Subclassed edit control windproc function
9068 LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9070 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
9075 * Creates a subclassed edit cotrol
9081 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, DWORD style
,
9082 INT x
, INT y
, INT width
, INT height
, BOOL isW
)
9084 WCHAR editName
[5] = { 'E', 'd', 'i', 't', '\0' };
9089 TEXTMETRICW textMetric
;
9090 HINSTANCE hinst
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_HINSTANCE
);
9092 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text
, isW
), isW
);
9094 style
|= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|WS_BORDER
;
9095 hdc
= GetDC(infoPtr
->hwndSelf
);
9097 /* Select the font to get appropriate metric dimensions */
9098 if(infoPtr
->hFont
!= 0)
9099 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
9101 /*Get String Lenght in pixels */
9102 GetTextExtentPoint32W(hdc
, text
, lstrlenW(text
), &sz
);
9104 /*Add Extra spacing for the next character */
9105 GetTextMetricsW(hdc
, &textMetric
);
9106 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
9108 if(infoPtr
->hFont
!= 0)
9109 SelectObject(hdc
, hOldFont
);
9111 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
9113 hedit
= CreateWindowW(editName
, text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
9115 hedit
= CreateWindowA("Edit", (LPCSTR
)text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
9117 if (!hedit
) return 0;
9119 infoPtr
->EditWndProc
= (WNDPROC
)
9120 (isW
? SetWindowLongW(hedit
, GWL_WNDPROC
, (LONG
)EditLblWndProcW
) :
9121 SetWindowLongA(hedit
, GWL_WNDPROC
, (LONG
)EditLblWndProcA
) );
9123 SendMessageW(hedit
, WM_SETFONT
, infoPtr
->hFont
, FALSE
);