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 * 1. No horizontal scrolling when header is larger than the client area.
29 * 2. Drawing optimizations.
30 * 3. Hot item handling.
33 * LISTVIEW_Notify : most notifications from children (editbox and header)
36 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
38 * Advanced functionality:
39 * LISTVIEW_GetNumberOfWorkAreas : not implemented
40 * LISTVIEW_GetISearchString : not implemented
41 * LISTVIEW_GetBkImage : not implemented
42 * LISTVIEW_SetBkImage : not implemented
43 * LISTVIEW_GetColumnOrderArray : simple hack only
44 * LISTVIEW_SetColumnOrderArray : simple hack only
45 * LISTVIEW_Arrange : empty stub
46 * LISTVIEW_ApproximateViewRect : incomplete
47 * LISTVIEW_Update : not completed
49 * Known differences in message stream from native control (not known if
50 * these differences cause problems):
51 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
52 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
53 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
54 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
55 * does *not* invoke DefWindowProc
56 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
57 * processing for "USEDOUBLECLICKTIME".
62 #include "wine/port.h"
74 #include "wine/debug.h"
76 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
78 /* Some definitions for inline edit control */
80 typedef struct tagLV_INTHIT
83 DWORD distance
; /* distance to closest item */
84 INT iDistItem
; /* item number that is closest */
85 } LV_INTHIT
, *LPLV_INTHIT
;
87 typedef struct tagITEMHDR
91 } ITEMHDR
, *LPITEMHDR
;
93 typedef struct tagLISTVIEW_SUBITEM
99 typedef struct tagLISTVIEW_ITEM
109 typedef struct tagLISTVIEW_SELECTION
113 } LISTVIEW_SELECTION
;
115 typedef struct tagLISTVIEW_INFO
122 HIMAGELIST himlNormal
;
123 HIMAGELIST himlSmall
;
124 HIMAGELIST himlState
;
129 HDPA hdpaSelectionRanges
;
142 INT ntmHeight
; /* from GetTextMetrics from above font */
143 INT ntmAveCharWidth
; /* from GetTextMetrics from above font */
147 DWORD dwExStyle
; /* extended listview style */
149 PFNLVCOMPARE pfnCompare
;
156 INT nColumnCount
; /* the number of columns in this control */
158 DWORD lastKeyPressTimestamp
; /* Added */
159 WPARAM charCode
; /* Added */
160 INT nSearchParamLength
; /* Added */
161 WCHAR szSearchParam
[ MAX_PATH
]; /* Added */
165 DEFINE_COMMON_NOTIFICATIONS(LISTVIEW_INFO
, hwndSelf
);
171 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
172 #define SB_INTERNAL -1
174 /* maximum size of a label */
175 #define DISP_TEXT_SIZE 512
177 /* padding for items in list and small icon display modes */
178 #define WIDTH_PADDING 12
180 /* padding for items in list, report and small icon display modes */
181 #define HEIGHT_PADDING 1
183 /* offset of items in report display mode */
184 #define REPORT_MARGINX 2
186 /* padding for icon in large icon display mode
187 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
188 * that HITTEST will see.
189 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
190 * ICON_TOP_PADDING - sum of the two above.
191 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
192 * LABEL_VERT_PADDING - between bottom of text and end of box
194 #define ICON_TOP_PADDING_NOTHITABLE 2
195 #define ICON_TOP_PADDING_HITABLE 2
196 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
197 #define ICON_BOTTOM_PADDING 4
198 #define LABEL_VERT_PADDING 7
200 /* default label width for items in list and small icon display modes */
201 #define DEFAULT_LABEL_WIDTH 40
203 /* default column width for items in list display mode */
204 #define DEFAULT_COLUMN_WIDTH 128
206 /* Size of "line" scroll for V & H scrolls */
207 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
209 /* Padding betwen image and label */
210 #define IMAGE_PADDING 2
212 /* Padding behind the label */
213 #define TRAILING_PADDING 5
215 /* Border for the icon caption */
216 #define CAPTION_BORDER 2
218 /* Standard DrawText flags for LISTVIEW_UpdateLargeItemLabelRect and LISTVIEW_DrawLargeItem */
219 #define LISTVIEW_DTFLAGS DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL
224 /* retrieve the number of items in the listview */
225 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
227 #define LISTVIEW_DUMP(iP) do { \
228 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
229 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
230 iP->nItemHeight, iP->nItemWidth, GetWindowLongW (iP->hwndSelf, GWL_STYLE)); \
231 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n", \
232 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
233 iP->nFocusedItem, iP->nHotItem, iP->dwExStyle); \
234 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
235 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
236 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
237 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
239 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
240 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
245 * forward declarations
247 static LRESULT
LISTVIEW_GetItemT(LISTVIEW_INFO
*, LPLVITEMW
, BOOL
, BOOL
);
248 static INT
LISTVIEW_SuperHitTestItem(LISTVIEW_INFO
*, LPLV_INTHIT
, BOOL
);
249 static INT
LISTVIEW_HitTestItem(LISTVIEW_INFO
*, LPLVHITTESTINFO
, BOOL
);
250 static void LISTVIEW_AlignLeft(LISTVIEW_INFO
*);
251 static void LISTVIEW_AlignTop(LISTVIEW_INFO
*);
252 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*, INT
);
253 static void LISTVIEW_AddSelection(LISTVIEW_INFO
*, INT
);
254 static INT
LISTVIEW_GetItemHeight(LISTVIEW_INFO
*);
255 static BOOL
LISTVIEW_GetItemBoundBox(LISTVIEW_INFO
*, INT
, LPRECT
);
256 static BOOL
LISTVIEW_GetItemPosition(LISTVIEW_INFO
*, INT
, LPPOINT
);
257 static LRESULT
LISTVIEW_GetItemRect(LISTVIEW_INFO
*, INT
, LPRECT
);
258 static LRESULT
LISTVIEW_GetSubItemRect(LISTVIEW_INFO
*, INT
, INT
, INT
, LPRECT
);
259 static INT
LISTVIEW_GetItemWidth(LISTVIEW_INFO
*);
260 static INT
LISTVIEW_GetLabelWidth(LISTVIEW_INFO
*, INT
);
261 static LRESULT
LISTVIEW_GetOrigin(LISTVIEW_INFO
*, LPPOINT
);
262 static LRESULT
LISTVIEW_GetViewRect(LISTVIEW_INFO
*, LPRECT
);
263 static BOOL
LISTVIEW_RemoveColumn(HDPA
, INT
);
264 static BOOL
LISTVIEW_RemoveSubItem(HDPA
, INT
);
265 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*, INT
);
266 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
267 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*, INT
, LONG
, LONG
);
268 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO
*);
269 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*, INT
);
270 static BOOL
LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
271 static LRESULT
LISTVIEW_SetViewRect(LISTVIEW_INFO
*, LPRECT
);
272 static void LISTVIEW_UnsupportedStyles(LONG
);
273 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*, INT
, BOOL
);
274 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*, WPARAM
, LPARAM
);
275 static LRESULT
LISTVIEW_SortItems(LISTVIEW_INFO
*, PFNLVCOMPARE
, LPARAM
);
276 static LRESULT
LISTVIEW_GetStringWidthT(LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
277 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*, WPARAM
, LPARAM
);
278 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
);
279 static LRESULT
LISTVIEW_GetItemState(LISTVIEW_INFO
*, INT
, UINT
);
280 static LRESULT
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, LPLVITEMW
);
281 static void LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO
*, int, RECT
*);
282 static LRESULT
LISTVIEW_GetColumnT(LISTVIEW_INFO
*, INT
, LPLVCOLUMNW
, BOOL
);
283 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
284 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
285 static INT
LISTVIEW_GetTopIndex(LISTVIEW_INFO
*);
286 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
287 static HWND
CreateEditLabelT(LISTVIEW_INFO
*, LPCWSTR
, DWORD
, INT
, INT
, INT
, INT
, BOOL
);
289 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
290 #define KEY_DELAY 450
292 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
294 static inline BOOL
is_textW(LPCWSTR text
)
296 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
299 static inline BOOL
is_textT(LPCWSTR text
, BOOL isW
)
301 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
302 return is_textW(text
);
305 static inline int textlenT(LPCWSTR text
, BOOL isW
)
307 return !is_textT(text
, isW
) ? 0 :
308 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
311 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
314 if (isSrcW
) lstrcpynW(dest
, src
, max
);
315 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
317 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
318 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
321 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
323 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
324 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
327 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
329 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
330 n
= min(textlenT(text
, isW
), n
);
331 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
334 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
336 LPWSTR wstr
= (LPWSTR
)text
;
338 TRACE("(text=%s, isW=%d)\n", debugtext_t(text
, isW
), isW
);
339 if (!isW
&& is_textT(text
, isW
))
341 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
342 wstr
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
343 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
345 TRACE(" wstr=%s\n", debugstr_w(wstr
));
349 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
351 if (!isW
&& is_textT(wstr
, isW
)) HeapFree(GetProcessHeap(), 0, wstr
);
355 * dest is a pointer to a Unicode string
356 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
358 static BOOL
textsetptrT(LPWSTR
*dest
, LPWSTR src
, BOOL isW
)
362 if (src
== LPSTR_TEXTCALLBACKW
)
364 if (is_textW(*dest
)) COMCTL32_Free(*dest
);
365 *dest
= LPSTR_TEXTCALLBACKW
;
369 LPWSTR pszText
= textdupTtoW(src
, isW
);
370 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
371 bResult
= Str_SetPtrW(dest
, pszText
);
372 textfreeT(pszText
, isW
);
378 * compares a Unicode to a Unicode/ANSI text string
380 static inline int textcmpWT(LPWSTR aw
, LPWSTR bt
, BOOL isW
)
382 if (!aw
) return bt
? -1 : 0;
383 if (!bt
) return aw
? 1 : 0;
384 if (aw
== LPSTR_TEXTCALLBACKW
)
385 return bt
== LPSTR_TEXTCALLBACKW
? 0 : -1;
386 if (bt
!= LPSTR_TEXTCALLBACKW
)
388 LPWSTR bw
= textdupTtoW(bt
, isW
);
389 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
397 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
398 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
401 return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
403 return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
406 static inline BOOL
notify(LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
408 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
409 pnmh
->idFrom
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_ID
);
411 return (BOOL
)SendMessageW(GetParent(infoPtr
->hwndSelf
), WM_NOTIFY
,
412 (WPARAM
)pnmh
->idFrom
, (LPARAM
)pnmh
);
415 static inline void notify_itemactivate(LISTVIEW_INFO
*infoPtr
)
417 hwnd_notify(infoPtr
->hwndSelf
, LVN_ITEMACTIVATE
);
420 static inline BOOL
listview_notify(LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
422 return notify(infoPtr
, code
, (LPNMHDR
)plvnm
);
425 static int tabNotification
[] = {
426 LVN_BEGINLABELEDITW
, LVN_BEGINLABELEDITA
,
427 LVN_ENDLABELEDITW
, LVN_ENDLABELEDITA
,
428 LVN_GETDISPINFOW
, LVN_GETDISPINFOA
,
429 LVN_SETDISPINFOW
, LVN_SETDISPINFOA
,
430 LVN_ODFINDITEMW
, LVN_ODFINDITEMA
,
431 LVN_GETINFOTIPW
, LVN_GETINFOTIPA
,
435 static int get_ansi_notification(INT unicodeNotificationCode
)
437 int *pTabNotif
= tabNotification
;
438 while (*pTabNotif
&& (unicodeNotificationCode
!= *pTabNotif
++));
439 if (*pTabNotif
) return *pTabNotif
;
440 ERR("unknown notification %x\n", unicodeNotificationCode
);
441 return unicodeNotificationCode
;
445 Send notification. depends on dispinfoW having same
446 structure as dispinfoA.
447 infoPtr : listview struct
448 notificationCode : *Unicode* notification code
449 pdi : dispinfo structure (can be unicode or ansi)
450 isW : TRUE if dispinfo is Unicode
452 static BOOL
dispinfo_notifyT(LISTVIEW_INFO
*infoPtr
, INT notificationCode
, LPNMLVDISPINFOW pdi
, BOOL isW
)
454 BOOL bResult
= FALSE
;
455 BOOL convertToAnsi
= FALSE
, convertToUnicode
= FALSE
;
457 INT cchTempBufMax
= 0, savCchTextMax
= 0;
458 LPWSTR pszTempBuf
= NULL
, savPszText
= NULL
;
460 TRACE("(code=%x, pdi=%p, isW=%d)\n", notificationCode
, pdi
, isW
);
461 TRACE(" notifyFormat=%s\n",
462 infoPtr
->notifyFormat
== NFR_UNICODE
? "NFR_UNICODE" :
463 infoPtr
->notifyFormat
== NFR_ANSI
? "NFR_ANSI" : "(not set)");
464 if (infoPtr
->notifyFormat
== NFR_ANSI
)
465 realNotifCode
= get_ansi_notification(notificationCode
);
467 realNotifCode
= notificationCode
;
469 if (is_textT(pdi
->item
.pszText
, isW
))
471 if (isW
&& infoPtr
->notifyFormat
== NFR_ANSI
)
472 convertToAnsi
= TRUE
;
473 if (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
)
474 convertToUnicode
= TRUE
;
477 if (convertToAnsi
|| convertToUnicode
)
479 TRACE(" we have to convert the text to the correct format\n");
480 if (notificationCode
!= LVN_GETDISPINFOW
)
481 { /* length of existing text */
482 cchTempBufMax
= convertToUnicode
?
483 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
484 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
487 cchTempBufMax
= pdi
->item
.cchTextMax
;
489 pszTempBuf
= HeapAlloc(GetProcessHeap(), 0,
490 (convertToUnicode
? sizeof(WCHAR
) : sizeof(CHAR
)) * cchTempBufMax
);
491 if (!pszTempBuf
) return FALSE
;
492 if (convertToUnicode
)
493 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
494 pszTempBuf
, cchTempBufMax
);
496 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) pszTempBuf
,
497 cchTempBufMax
, NULL
, NULL
);
498 TRACE(" text=%s\n", debugtext_t(pszTempBuf
, convertToUnicode
));
499 savCchTextMax
= pdi
->item
.cchTextMax
;
500 savPszText
= pdi
->item
.pszText
;
501 pdi
->item
.pszText
= pszTempBuf
;
502 pdi
->item
.cchTextMax
= cchTempBufMax
;
505 bResult
= notify(infoPtr
, realNotifCode
, (LPNMHDR
)pdi
);
507 if (convertToUnicode
|| convertToAnsi
)
508 { /* convert back result */
509 TRACE(" returned text=%s\n", debugtext_t(pdi
->item
.pszText
, convertToUnicode
));
510 if (convertToUnicode
) /* note : pointer can be changed by app ! */
511 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) savPszText
,
512 savCchTextMax
, NULL
, NULL
);
514 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
515 savPszText
, savCchTextMax
);
516 pdi
->item
.pszText
= savPszText
; /* restores our buffer */
517 pdi
->item
.cchTextMax
= savCchTextMax
;
518 HeapFree(GetProcessHeap(), 0, pszTempBuf
);
523 static inline LRESULT
LISTVIEW_GetItemW(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL internal
)
525 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, internal
, TRUE
);
528 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
532 n
= min(min(n
, strlenW(s1
)), strlenW(s2
));
533 res
= CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
);
534 return res
? res
- sizeof(WCHAR
) : res
;
537 static char* debuglvitem_t(LPLVITEMW lpLVItem
, BOOL isW
)
539 static int index
= 0;
540 static char buffers
[20][256];
541 char* buf
= buffers
[index
++ % 20];
542 if (lpLVItem
== NULL
) return "(null)";
543 snprintf(buf
, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
544 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
545 lpLVItem
->mask
, lpLVItem
->iItem
, lpLVItem
->iSubItem
,
546 lpLVItem
->state
, lpLVItem
->stateMask
,
547 debugtext_tn(lpLVItem
->pszText
, isW
, 80),
548 lpLVItem
->cchTextMax
, lpLVItem
->iImage
, lpLVItem
->lParam
,
553 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn
, BOOL isW
)
555 static int index
= 0;
556 static char buffers
[20][256];
557 char* buf
= buffers
[index
++ % 20];
558 if (lpColumn
== NULL
) return "(null)";
559 snprintf(buf
, 256, "{mask=%x, fmt=%x, cx=%d,"
560 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
561 lpColumn
->mask
, lpColumn
->fmt
, lpColumn
->cx
,
562 lpColumn
->mask
& LVCF_TEXT
? debugtext_tn(lpColumn
->pszText
, isW
, 80): "",
563 lpColumn
->mask
& LVCF_TEXT
? lpColumn
->cchTextMax
: 0, lpColumn
->iSubItem
);
568 LISTVIEW_SendCustomDrawNotify (LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, HDC hdc
,
571 NMLVCUSTOMDRAW nmcdhdr
;
574 TRACE("(dwDrawStage=%lx, hdc=%x, rc=?)\n", dwDrawStage
, hdc
);
576 nmcd
= & nmcdhdr
.nmcd
;
577 nmcd
->hdr
.hwndFrom
= infoPtr
->hwndSelf
;
578 nmcd
->hdr
.idFrom
= GetWindowLongW( infoPtr
->hwndSelf
, GWL_ID
);
579 nmcd
->hdr
.code
= NM_CUSTOMDRAW
;
580 nmcd
->dwDrawStage
= dwDrawStage
;
582 nmcd
->rc
.left
= rc
.left
;
583 nmcd
->rc
.right
= rc
.right
;
584 nmcd
->rc
.bottom
= rc
.bottom
;
585 nmcd
->rc
.top
= rc
.top
;
586 nmcd
->dwItemSpec
= 0;
587 nmcd
->uItemState
= 0;
588 nmcd
->lItemlParam
= 0;
589 nmcdhdr
.clrText
= infoPtr
->clrText
;
590 nmcdhdr
.clrTextBk
= infoPtr
->clrBk
;
592 return (BOOL
)SendMessageW (GetParent (infoPtr
->hwndSelf
), WM_NOTIFY
,
593 (WPARAM
) nmcd
->hdr
.idFrom
, (LPARAM
)&nmcdhdr
);
597 LISTVIEW_SendCustomDrawItemNotify (LISTVIEW_INFO
*infoPtr
, HDC hdc
,
598 UINT iItem
, UINT iSubItem
,
601 NMLVCUSTOMDRAW nmcdhdr
;
603 DWORD dwDrawStage
,dwItemSpec
;
609 ZeroMemory(&item
,sizeof(item
));
611 item
.mask
= LVIF_PARAM
;
612 ListView_GetItemW(infoPtr
->hwndSelf
,&item
);
614 dwDrawStage
=CDDS_ITEM
| uItemDrawState
;
618 if (LISTVIEW_GetItemState(infoPtr
, iItem
, LVIS_SELECTED
)) uItemState
|= CDIS_SELECTED
;
619 if (LISTVIEW_GetItemState(infoPtr
, iItem
, LVIS_FOCUSED
)) uItemState
|= CDIS_FOCUS
;
620 if (iItem
== infoPtr
->nHotItem
) uItemState
|= CDIS_HOT
;
622 itemRect
.left
= LVIR_BOUNDS
;
623 LISTVIEW_GetItemRect(infoPtr
, iItem
, &itemRect
);
625 nmcd
= & nmcdhdr
.nmcd
;
626 nmcd
->hdr
.hwndFrom
= infoPtr
->hwndSelf
;
627 nmcd
->hdr
.idFrom
= GetWindowLongW( infoPtr
->hwndSelf
, GWL_ID
);
628 nmcd
->hdr
.code
= NM_CUSTOMDRAW
;
629 nmcd
->dwDrawStage
= dwDrawStage
;
631 nmcd
->rc
.left
= itemRect
.left
;
632 nmcd
->rc
.right
= itemRect
.right
;
633 nmcd
->rc
.bottom
= itemRect
.bottom
;
634 nmcd
->rc
.top
= itemRect
.top
;
635 nmcd
->dwItemSpec
= dwItemSpec
;
636 nmcd
->uItemState
= uItemState
;
637 nmcd
->lItemlParam
= item
.lParam
;
638 nmcdhdr
.clrText
= infoPtr
->clrText
;
639 nmcdhdr
.clrTextBk
= infoPtr
->clrBk
;
640 nmcdhdr
.iSubItem
=iSubItem
;
642 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
643 nmcd
->dwDrawStage
, nmcd
->hdc
, nmcd
->dwItemSpec
,
644 nmcd
->uItemState
, nmcd
->lItemlParam
);
646 retval
=SendMessageW (GetParent (infoPtr
->hwndSelf
), WM_NOTIFY
,
647 (WPARAM
)nmcd
->hdr
.idFrom
, (LPARAM
)&nmcdhdr
);
649 infoPtr
->clrText
=nmcdhdr
.clrText
;
650 infoPtr
->clrBk
=nmcdhdr
.clrTextBk
;
651 return (BOOL
) retval
;
654 static inline int LISTVIEW_GetType(LISTVIEW_INFO
*infoPtr
)
656 return GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & LVS_TYPEMASK
;
661 * Retrieves the number of items that can fit vertically in the client area.
664 * [I] infoPtr : valid pointer to the listview structure
667 * Number of items per row.
669 static inline INT
LISTVIEW_GetCountPerRow(LISTVIEW_INFO
*infoPtr
)
671 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
673 return max(nListWidth
/infoPtr
->nItemWidth
, 1);
678 * Retrieves the number of items that can fit horizontally in the client
682 * [I] infoPtr : valid pointer to the listview structure
685 * Number of items per column.
687 static inline INT
LISTVIEW_GetCountPerColumn(LISTVIEW_INFO
*infoPtr
)
689 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
691 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
696 * Retrieves the count of visible items. Note that the returned count may
697 * be a bit larger than the actual count.
700 * [I] infoPtr : valid pointer to the listview structure
703 * maximum visible item count
705 static INT
LISTVIEW_GetVisibleCount(LISTVIEW_INFO
*infoPtr
)
707 UINT uView
= LISTVIEW_GetType(infoPtr
);
708 INT nPerCol
, nPerRow
;
710 if (uView
== LVS_REPORT
)
712 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
715 else if (uView
== LVS_LIST
)
717 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
718 nPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
722 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
723 nPerRow
= LISTVIEW_GetCountPerRow(infoPtr
) + 1;
726 return nPerCol
* nPerRow
;
730 /*************************************************************************
731 * LISTVIEW_ProcessLetterKeys
733 * Processes keyboard messages generated by pressing the letter keys
735 * What this does is perform a case insensitive search from the
736 * current position with the following quirks:
737 * - If two chars or more are pressed in quick succession we search
738 * for the corresponding string (e.g. 'abc').
739 * - If there is a delay we wipe away the current search string and
740 * restart with just that char.
741 * - If the user keeps pressing the same character, whether slowly or
742 * fast, so that the search string is entirely composed of this
743 * character ('aaaaa' for instance), then we search for first item
744 * that starting with that character.
745 * - If the user types the above character in quick succession, then
746 * we must also search for the corresponding string ('aaaaa'), and
747 * go to that string if there is a match.
750 * [I] hwnd : handle to the window
751 * [I] charCode : the character code, the actual character
752 * [I] keyData : key data
760 * - The current implementation has a list of characters it will
761 * accept and it ignores averything else. In particular it will
762 * ignore accentuated characters which seems to match what
763 * Windows does. But I'm not sure it makes sense to follow
765 * - We don't sound a beep when the search fails.
769 * TREEVIEW_ProcessLetterKeys
771 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
777 WCHAR buffer
[MAX_PATH
];
778 DWORD timestamp
,elapsed
;
780 /* simple parameter checking */
781 if (!charCode
|| !keyData
)
787 /* only allow the valid WM_CHARs through */
788 if (!isalnum(charCode
) &&
789 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
790 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
791 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
792 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
793 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
794 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
795 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
796 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
797 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
800 nSize
=GETITEMCOUNT(infoPtr
);
801 /* if there's one item or less, there is no where to go */
805 /* compute how much time elapsed since last keypress */
806 timestamp
=GetTickCount();
807 if (timestamp
> infoPtr
->lastKeyPressTimestamp
) {
808 elapsed
=timestamp
-infoPtr
->lastKeyPressTimestamp
;
810 elapsed
=infoPtr
->lastKeyPressTimestamp
-timestamp
;
813 /* update the search parameters */
814 infoPtr
->lastKeyPressTimestamp
=timestamp
;
815 if (elapsed
< KEY_DELAY
) {
816 if (infoPtr
->nSearchParamLength
< COUNTOF(infoPtr
->szSearchParam
)) {
817 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++]=charCode
;
819 if (infoPtr
->charCode
!= charCode
) {
820 infoPtr
->charCode
=charCode
=0;
823 infoPtr
->charCode
=charCode
;
824 infoPtr
->szSearchParam
[0]=charCode
;
825 infoPtr
->nSearchParamLength
=1;
826 /* Redundant with the 1 char string */
830 /* and search from the current position */
832 if (infoPtr
->nFocusedItem
>= 0) {
833 endidx
=infoPtr
->nFocusedItem
;
835 /* if looking for single character match,
836 * then we must always move forward
838 if (infoPtr
->nSearchParamLength
== 1)
852 ZeroMemory(&item
, sizeof(item
));
853 item
.mask
= LVIF_TEXT
;
856 item
.pszText
= buffer
;
857 item
.cchTextMax
= COUNTOF(buffer
);
858 ListView_GetItemW(infoPtr
->hwndSelf
, &item
);
860 /* check for a match */
861 if (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,infoPtr
->nSearchParamLength
) == 0) {
864 } else if ( (charCode
!= 0) && (nItem
== -1) && (nItem
!= infoPtr
->nFocusedItem
) &&
865 (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,1) == 0) ) {
866 /* This would work but we must keep looking for a longer match */
870 } while (idx
!= endidx
);
873 LISTVIEW_KeySelection(infoPtr
, nItem
);
878 /*************************************************************************
879 * LISTVIEW_UpdateHeaderSize [Internal]
881 * Function to resize the header control
884 * hwnd [I] handle to a window
885 * nNewScrollPos [I] Scroll Pos to Set
892 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
897 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
898 point
[0].x
= winRect
.left
;
899 point
[0].y
= winRect
.top
;
900 point
[1].x
= winRect
.right
;
901 point
[1].y
= winRect
.bottom
;
903 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
904 point
[0].x
= -nNewScrollPos
;
905 point
[1].x
+= nNewScrollPos
;
907 SetWindowPos(infoPtr
->hwndHeader
,0,
908 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
909 SWP_NOZORDER
| SWP_NOACTIVATE
);
914 * Update the scrollbars. This functions should be called whenever
915 * the content, size or view changes.
918 * [I] infoPtr : valid pointer to the listview structure
923 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO
*infoPtr
)
925 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
926 UINT uView
= lStyle
& LVS_TYPEMASK
;
927 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
928 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
929 SCROLLINFO scrollInfo
;
931 if (lStyle
& LVS_NOSCROLL
) return;
933 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
934 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
936 if (uView
== LVS_LIST
)
938 /* update horizontal scrollbar */
939 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
940 INT nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
941 INT nNumOfItems
= GETITEMCOUNT(infoPtr
);
943 TRACE("items=%d, perColumn=%d, perRow=%d\n",
944 nNumOfItems
, nCountPerColumn
, nCountPerRow
);
946 scrollInfo
.nMax
= nNumOfItems
/ nCountPerColumn
;
947 if((nNumOfItems
% nCountPerColumn
) == 0)
949 if (scrollInfo
.nMax
< 0) scrollInfo
.nMax
= 0;
950 scrollInfo
.nPos
= ListView_GetTopIndex(infoPtr
->hwndSelf
) / nCountPerColumn
;
951 scrollInfo
.nPage
= nCountPerRow
;
952 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
953 SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
954 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
956 else if (uView
== LVS_REPORT
)
960 /* update vertical scrollbar */
962 scrollInfo
.nMax
= GETITEMCOUNT(infoPtr
) - 1;
963 scrollInfo
.nPos
= ListView_GetTopIndex(infoPtr
->hwndSelf
);
964 scrollInfo
.nPage
= LISTVIEW_GetCountPerColumn(infoPtr
);
965 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
966 test
= (scrollInfo
.nMin
>= scrollInfo
.nMax
- max((INT
)scrollInfo
.nPage
- 1, 0));
967 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
968 scrollInfo
.nMax
, scrollInfo
.nPage
, test
);
969 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
970 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, (test
) ? FALSE
: TRUE
);
972 /* update horizontal scrollbar */
973 nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
974 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
) == FALSE
975 || GETITEMCOUNT(infoPtr
) == 0)
980 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
981 scrollInfo
.nPage
= nListWidth
;
982 scrollInfo
.nMax
= max(infoPtr
->nItemWidth
, 0)-1;
983 test
= (scrollInfo
.nMin
>= scrollInfo
.nMax
- max((INT
)scrollInfo
.nPage
- 1, 0));
984 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
985 scrollInfo
.nMax
, scrollInfo
.nPage
, test
);
986 SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
987 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, (test
) ? FALSE
: TRUE
);
989 /* Update the Header Control */
990 scrollInfo
.fMask
= SIF_POS
;
991 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
);
992 LISTVIEW_UpdateHeaderSize(infoPtr
, scrollInfo
.nPos
);
999 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
))
1001 INT nViewWidth
= rcView
.right
- rcView
.left
;
1002 INT nViewHeight
= rcView
.bottom
- rcView
.top
;
1004 /* Update Horizontal Scrollbar */
1005 scrollInfo
.fMask
= SIF_POS
;
1006 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
) == FALSE
1007 || GETITEMCOUNT(infoPtr
) == 0)
1009 scrollInfo
.nPos
= 0;
1011 scrollInfo
.nMax
= max(nViewWidth
, 0)-1;
1012 scrollInfo
.nMin
= 0;
1013 scrollInfo
.nPage
= nListWidth
;
1014 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
1015 TRACE("LVS_ICON/SMALLICON Horz.\n");
1016 SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
1018 /* Update Vertical Scrollbar */
1019 nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1020 scrollInfo
.fMask
= SIF_POS
;
1021 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
) == FALSE
1022 || GETITEMCOUNT(infoPtr
) == 0)
1024 scrollInfo
.nPos
= 0;
1026 scrollInfo
.nMax
= max(nViewHeight
,0)-1;
1027 scrollInfo
.nMin
= 0;
1028 scrollInfo
.nPage
= nListHeight
;
1029 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
1030 TRACE("LVS_ICON/SMALLICON Vert.\n");
1031 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
1038 * Toggles (draws/erase) the focus rectangle.
1040 static inline void LISTVIEW_ToggleFocusRect(LISTVIEW_INFO
*infoPtr
)
1042 /* if we have a focus rectagle, draw it */
1043 if (!IsRectEmpty(&infoPtr
->rcFocus
))
1045 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
1046 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
1047 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1052 * Invalidates all visible selected items.
1054 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO
*infoPtr
)
1056 INT i
, nTop
, nBottom
;
1058 nTop
= LISTVIEW_GetTopIndex(infoPtr
);
1059 nBottom
= nTop
+ LISTVIEW_GetVisibleCount(infoPtr
);
1060 if (nBottom
> GETITEMCOUNT(infoPtr
)) nBottom
= GETITEMCOUNT(infoPtr
);
1062 for (i
= nTop
; i
<= nBottom
; i
++)
1064 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
1067 rcItem
.left
= LVIR_BOUNDS
;
1068 LISTVIEW_GetItemRect(infoPtr
, i
, &rcItem
);
1069 InvalidateRect(infoPtr
->hwndSelf
, &rcItem
, FALSE
);
1077 * Prints a message for unsupported window styles.
1078 * A kind of TODO list for window styles.
1081 * [I] LONG : window style
1086 static void LISTVIEW_UnsupportedStyles(LONG lStyle
)
1088 if ((LVS_TYPESTYLEMASK
& lStyle
) == LVS_NOSCROLL
)
1089 FIXME(" LVS_NOSCROLL\n");
1091 if (lStyle
& LVS_NOLABELWRAP
)
1092 FIXME(" LVS_NOLABELWRAP\n");
1094 if (!(lStyle
& LVS_SHAREIMAGELISTS
))
1095 FIXME(" !LVS_SHAREIMAGELISTS\n");
1097 if (lStyle
& LVS_SORTASCENDING
)
1098 FIXME(" LVS_SORTASCENDING\n");
1100 if (lStyle
& LVS_SORTDESCENDING
)
1101 FIXME(" LVS_SORTDESCENDING\n");
1106 * Aligns the items with the top edge of the window.
1109 * [I] infoPtr : valid pointer to the listview structure
1114 static void LISTVIEW_AlignTop(LISTVIEW_INFO
*infoPtr
)
1116 UINT uView
= LISTVIEW_GetType(infoPtr
);
1117 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1120 INT i
, off_x
=0, off_y
=0;
1122 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1124 /* Since SetItemPosition uses upper-left of icon, and for
1125 style=LVS_ICON the icon is not left adjusted, get the offset */
1126 if (uView
== LVS_ICON
)
1128 off_y
= ICON_TOP_PADDING
;
1129 off_x
= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2;
1133 ZeroMemory(&rcView
, sizeof(RECT
));
1134 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1136 infoPtr
->rcList
.left
, infoPtr
->rcList
.right
);
1138 if (nListWidth
> infoPtr
->nItemWidth
)
1140 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
1142 if ((ptItem
.x
-off_x
) + infoPtr
->nItemWidth
> nListWidth
)
1145 ptItem
.y
+= infoPtr
->nItemHeight
;
1148 LISTVIEW_SetItemPosition(infoPtr
, i
, ptItem
.x
, ptItem
.y
);
1149 ptItem
.x
+= infoPtr
->nItemWidth
;
1150 rcView
.right
= max(rcView
.right
, ptItem
.x
);
1153 rcView
.right
-= off_x
;
1154 rcView
.bottom
= (ptItem
.y
-off_y
) + infoPtr
->nItemHeight
;
1158 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
1160 LISTVIEW_SetItemPosition(infoPtr
, i
, ptItem
.x
, ptItem
.y
);
1161 ptItem
.y
+= infoPtr
->nItemHeight
;
1164 rcView
.right
= infoPtr
->nItemWidth
;
1165 rcView
.bottom
= ptItem
.y
-off_y
;
1168 LISTVIEW_SetViewRect(infoPtr
, &rcView
);
1174 * Aligns the items with the left edge of the window.
1177 * [I] infoPtr : valid pointer to the listview structure
1182 static void LISTVIEW_AlignLeft(LISTVIEW_INFO
*infoPtr
)
1184 UINT uView
= LISTVIEW_GetType(infoPtr
);
1185 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1188 INT i
, off_x
=0, off_y
=0;
1190 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1192 /* Since SetItemPosition uses upper-left of icon, and for
1193 style=LVS_ICON the icon is not left adjusted, get the offset */
1194 if (uView
== LVS_ICON
)
1196 off_y
= ICON_TOP_PADDING
;
1197 off_x
= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2;
1201 ZeroMemory(&rcView
, sizeof(RECT
));
1202 TRACE("Icon off.x=%d, off.y=%d\n", off_x
, off_y
);
1204 if (nListHeight
> infoPtr
->nItemHeight
)
1206 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
1208 if (ptItem
.y
+ infoPtr
->nItemHeight
> nListHeight
)
1211 ptItem
.x
+= infoPtr
->nItemWidth
;
1214 LISTVIEW_SetItemPosition(infoPtr
, i
, ptItem
.x
, ptItem
.y
);
1215 ptItem
.y
+= infoPtr
->nItemHeight
;
1216 rcView
.bottom
= max(rcView
.bottom
, ptItem
.y
);
1219 rcView
.right
= ptItem
.x
+ infoPtr
->nItemWidth
;
1223 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
1225 LISTVIEW_SetItemPosition(infoPtr
, i
, ptItem
.x
, ptItem
.y
);
1226 ptItem
.x
+= infoPtr
->nItemWidth
;
1229 rcView
.bottom
= infoPtr
->nItemHeight
;
1230 rcView
.right
= ptItem
.x
;
1233 LISTVIEW_SetViewRect(infoPtr
, &rcView
);
1239 * Set the bounding rectangle of all the items.
1242 * [I] infoPtr : valid pointer to the listview structure
1243 * [I] LPRECT : bounding rectangle
1249 static LRESULT
LISTVIEW_SetViewRect(LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
1251 BOOL bResult
= FALSE
;
1253 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1254 lprcView
->left
, lprcView
->top
, lprcView
->right
, lprcView
->bottom
);
1256 if (lprcView
!= NULL
)
1259 infoPtr
->rcView
.left
= lprcView
->left
;
1260 infoPtr
->rcView
.top
= lprcView
->top
;
1261 infoPtr
->rcView
.right
= lprcView
->right
;
1262 infoPtr
->rcView
.bottom
= lprcView
->bottom
;
1270 * Retrieves the bounding rectangle of all the items.
1273 * [I] infoPtr : valid pointer to the listview structure
1274 * [O] LPRECT : bounding rectangle
1280 static LRESULT
LISTVIEW_GetViewRect(LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
1282 BOOL bResult
= FALSE
;
1285 TRACE("(lprcView=%p)\n", lprcView
);
1287 if (lprcView
!= NULL
)
1289 bResult
= LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
1292 lprcView
->left
= infoPtr
->rcView
.left
+ ptOrigin
.x
;
1293 lprcView
->top
= infoPtr
->rcView
.top
+ ptOrigin
.y
;
1294 lprcView
->right
= infoPtr
->rcView
.right
+ ptOrigin
.x
;
1295 lprcView
->bottom
= infoPtr
->rcView
.bottom
+ ptOrigin
.y
;
1298 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1299 lprcView
->left
, lprcView
->top
, lprcView
->right
, lprcView
->bottom
);
1307 * Retrieves the subitem pointer associated with the subitem index.
1310 * [I] HDPA : DPA handle for a specific item
1311 * [I] INT : index of subitem
1314 * SUCCESS : subitem pointer
1317 static LISTVIEW_SUBITEM
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
,
1320 LISTVIEW_SUBITEM
*lpSubItem
;
1323 /* we should binary search here if need be */
1324 for (i
= 1; i
< hdpaSubItems
->nItemCount
; i
++)
1326 lpSubItem
= (LISTVIEW_SUBITEM
*) DPA_GetPtr(hdpaSubItems
, i
);
1327 if (lpSubItem
&& (lpSubItem
->iSubItem
== nSubItem
))
1337 * Calculates the width of a specific item.
1340 * [I] infoPtr : valid pointer to the listview structure
1341 * [I] nItem : item to calculate width, or -1 for max of all
1344 * Returns the width of an item width an item.
1346 static INT
LISTVIEW_CalculateWidth(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1348 UINT uView
= LISTVIEW_GetType(infoPtr
);
1349 INT nItemWidth
= 0, i
;
1351 if (uView
== LVS_ICON
)
1352 nItemWidth
= infoPtr
->iconSpacing
.cx
;
1353 else if (uView
== LVS_REPORT
)
1355 INT nHeaderItemCount
;
1358 /* calculate width of header */
1359 nHeaderItemCount
= Header_GetItemCount(infoPtr
->hwndHeader
);
1360 for (i
= 0; i
< nHeaderItemCount
; i
++)
1361 if (Header_GetItemRect(infoPtr
->hwndHeader
, i
, &rcHeaderItem
))
1362 nItemWidth
+= (rcHeaderItem
.right
- rcHeaderItem
.left
);
1368 if (GETITEMCOUNT(infoPtr
) == 0) return DEFAULT_COLUMN_WIDTH
;
1370 /* get width of string */
1373 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
1375 nLabelWidth
= LISTVIEW_GetLabelWidth(infoPtr
, i
);
1376 nItemWidth
= max(nItemWidth
, nLabelWidth
);
1380 nItemWidth
= LISTVIEW_GetLabelWidth(infoPtr
, nItem
);
1381 if (!nItemWidth
) return DEFAULT_COLUMN_WIDTH
;
1382 nItemWidth
+= WIDTH_PADDING
;
1383 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
1384 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconSize
.cx
; /*FIXME: is this correct */
1385 if (nItem
== -1) nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
);
1388 return max(nItemWidth
, 1);
1393 * Calculates the max width of any item in the list.
1396 * [I] infoPtr : valid pointer to the listview structure
1397 * [I] LONG : window style
1400 * Returns item width.
1402 static inline INT
LISTVIEW_GetItemWidth(LISTVIEW_INFO
*infoPtr
)
1404 return LISTVIEW_CalculateWidth(infoPtr
, -1);
1409 * Retrieves and saves important text metrics info for the current
1413 * [I] infoPtr : valid pointer to the listview structure
1416 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
1419 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
1420 HFONT hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
1421 INT oldHeight
, oldACW
;
1423 GetTextMetricsW(hdc
, &tm
);
1425 oldHeight
= infoPtr
->ntmHeight
;
1426 oldACW
= infoPtr
->ntmAveCharWidth
;
1427 infoPtr
->ntmHeight
= tm
.tmHeight
;
1428 infoPtr
->ntmAveCharWidth
= tm
.tmAveCharWidth
;
1430 SelectObject(hdc
, hOldFont
);
1431 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1432 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1433 oldHeight
, infoPtr
->ntmHeight
, oldACW
, infoPtr
->ntmAveCharWidth
);
1439 * Calculates the height of an item.
1442 * [I] infoPtr : valid pointer to the listview structure
1445 * Returns item height.
1447 static INT
LISTVIEW_GetItemHeight(LISTVIEW_INFO
*infoPtr
)
1449 UINT uView
= LISTVIEW_GetType(infoPtr
);
1450 INT nItemHeight
= 0;
1452 if (uView
== LVS_ICON
)
1454 nItemHeight
= infoPtr
->iconSpacing
.cy
;
1458 if(infoPtr
->himlState
|| infoPtr
->himlSmall
)
1459 nItemHeight
= max(infoPtr
->ntmHeight
, infoPtr
->iconSize
.cy
) + HEIGHT_PADDING
;
1461 nItemHeight
= infoPtr
->ntmHeight
;
1468 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO
*infoPtr
)
1470 LISTVIEW_SELECTION
*selection
;
1471 INT topSelection
= infoPtr
->hdpaSelectionRanges
->nItemCount
;
1474 TRACE("Selections are:\n");
1475 for (i
= 0; i
< topSelection
; i
++)
1477 selection
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
,i
);
1478 TRACE(" %lu - %lu\n",selection
->lower
,selection
->upper
);
1485 * A compare function for selection ranges
1488 * [I] range1 : pointer to selection range 1;
1489 * [I] range2 : pointer to selection range 2;
1493 * >0 : if Item 1 > Item 2
1494 * <0 : if Item 2 > Item 1
1495 * 0 : if Item 1 == Item 2
1497 static INT CALLBACK
LISTVIEW_CompareSelectionRanges(LPVOID range1
, LPVOID range2
, LPARAM flags
)
1499 if (((LISTVIEW_SELECTION
*)(range1
))->upper
<
1500 ((LISTVIEW_SELECTION
*)(range2
))->lower
)
1502 if (((LISTVIEW_SELECTION
*)(range2
))->upper
<
1503 ((LISTVIEW_SELECTION
*)(range1
))->lower
)
1509 * Helper function for LISTVIEW_AddSelectionRange, and LISTVIEW_SetItem.
1511 static BOOL
add_selection_range(LISTVIEW_INFO
*infoPtr
, INT lower
, INT upper
, BOOL adj_sel_only
)
1513 LISTVIEW_SELECTION selection
;
1517 TRACE("range (%i - %i)\n", lower
, upper
);
1519 /* try find overlapping selections first */
1520 selection
.lower
= lower
- 1;
1521 selection
.upper
= upper
+ 1;
1522 index
= DPA_Search(infoPtr
->hdpaSelectionRanges
, &selection
, 0,
1523 LISTVIEW_CompareSelectionRanges
, 0, 0);
1527 LISTVIEW_SELECTION
*newsel
;
1529 /* create the brand new selection to insert */
1530 newsel
= (LISTVIEW_SELECTION
*)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION
));
1531 if(!newsel
) return FALSE
;
1532 newsel
->lower
= lower
;
1533 newsel
->upper
= upper
;
1535 /* figure out where to insert it */
1536 index
= DPA_Search(infoPtr
->hdpaSelectionRanges
, newsel
, 0,
1537 LISTVIEW_CompareSelectionRanges
, 0, DPAS_INSERTAFTER
);
1538 if (index
== -1) index
= 0;
1540 /* and get it over with */
1541 DPA_InsertPtr(infoPtr
->hdpaSelectionRanges
, index
, newsel
);
1545 LISTVIEW_SELECTION
*chksel
, *mrgsel
;
1546 INT fromindex
, mergeindex
;
1548 chksel
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
, index
);
1549 if (!chksel
) return FALSE
;
1550 TRACE("Merge with index %i (%lu - %lu)\n",
1551 index
,chksel
->lower
, chksel
->upper
);
1553 chksel
->lower
= min(lower
, chksel
->lower
);
1554 chksel
->upper
= max(upper
, chksel
->upper
);
1556 TRACE("New range %i (%lu - %lu)\n",
1557 index
,chksel
->lower
, chksel
->upper
);
1559 /* merge now common selection ranges */
1561 selection
.lower
= chksel
->lower
- 1;
1562 selection
.upper
= chksel
->upper
+ 1;
1566 mergeindex
= DPA_Search(infoPtr
->hdpaSelectionRanges
, &selection
, fromindex
,
1567 LISTVIEW_CompareSelectionRanges
, 0, 0);
1568 if (mergeindex
== -1) break;
1569 if (mergeindex
== index
)
1571 fromindex
= index
+ 1;
1575 TRACE("Merge with index %i\n", mergeindex
);
1577 mrgsel
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
, mergeindex
);
1578 if (!mrgsel
) return FALSE
;
1580 chksel
->lower
= min(chksel
->lower
, mrgsel
->lower
);
1581 chksel
->upper
= max(chksel
->upper
, mrgsel
->upper
);
1582 COMCTL32_Free(mrgsel
);
1583 DPA_DeletePtr(infoPtr
->hdpaSelectionRanges
, mergeindex
);
1584 if (mergeindex
< index
) index
--;
1588 /* FIXME: do we really need it? */
1589 DPA_Sort(infoPtr
->hdpaSelectionRanges
, LISTVIEW_CompareSelectionRanges
, 0);
1591 if (adj_sel_only
) return TRUE
;
1593 /* set the selection on items */
1594 ZeroMemory(&lvItem
, sizeof(lvItem
));
1595 lvItem
.state
= LVIS_SELECTED
;
1596 lvItem
.stateMask
= LVIS_SELECTED
;
1597 for(i
= lower
; i
<= upper
; i
++)
1598 LISTVIEW_SetItemState(infoPtr
, i
, &lvItem
);
1604 * Helper function for LISTVIEW_RemoveSelectionRange, and LISTVIEW_SetItem.
1606 static BOOL
remove_selection_range(LISTVIEW_INFO
*infoPtr
, INT lower
, INT upper
, BOOL adj_sel_only
)
1608 LISTVIEW_SELECTION remsel
, tmpsel
, *chksel
;
1613 ZeroMemory(&lvItem
, sizeof(lvItem
));
1614 lvItem
.stateMask
= LVIS_SELECTED
;
1616 remsel
.lower
= lower
;
1617 remsel
.upper
= upper
;
1619 TRACE("range: (%lu - %lu)\n", remsel
.lower
, remsel
.upper
);
1623 index
= DPA_Search(infoPtr
->hdpaSelectionRanges
, &remsel
, 0,
1624 LISTVIEW_CompareSelectionRanges
, 0, 0);
1625 if (index
== -1) return TRUE
;
1627 chksel
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
, index
);
1628 if (!chksel
) return FALSE
;
1630 TRACE("Matches range index %i (%lu-%lu)\n",
1631 index
, chksel
->lower
, chksel
->upper
);
1633 /* case 1: Same range */
1634 if ( (chksel
->upper
== remsel
.upper
) &&
1635 (chksel
->lower
== remsel
.lower
) )
1637 DPA_DeletePtr(infoPtr
->hdpaSelectionRanges
, index
);
1640 /* case 2: engulf */
1641 else if ( (chksel
->upper
<= remsel
.upper
) &&
1642 (chksel
->lower
>= remsel
.lower
) )
1644 DPA_DeletePtr(infoPtr
->hdpaSelectionRanges
, index
);
1646 /* case 3: overlap upper */
1647 else if ( (chksel
->upper
< remsel
.upper
) &&
1648 (chksel
->lower
< remsel
.lower
) )
1650 chksel
->upper
= remsel
.lower
- 1;
1652 /* case 4: overlap lower */
1653 else if ( (chksel
->upper
> remsel
.upper
) &&
1654 (chksel
->lower
> remsel
.lower
) )
1656 chksel
->lower
= remsel
.upper
+ 1;
1658 /* case 5: fully internal */
1661 LISTVIEW_SELECTION
*newsel
=
1662 (LISTVIEW_SELECTION
*)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION
));
1663 if (!newsel
) return FALSE
;
1665 newsel
->lower
= chksel
->lower
;
1666 newsel
->upper
= remsel
.lower
- 1;
1667 chksel
->lower
= remsel
.upper
+ 1;
1668 DPA_InsertPtr(infoPtr
->hdpaSelectionRanges
, index
, newsel
);
1669 /*FIXME: why do we need the sort??? */
1670 DPA_Sort(infoPtr
->hdpaSelectionRanges
, LISTVIEW_CompareSelectionRanges
, 0);
1674 if (adj_sel_only
) continue;
1676 /* here, chksel holds the selection to delete */
1677 for (i
= chksel
->lower
; i
<= chksel
->upper
; i
++)
1678 LISTVIEW_SetItemState(infoPtr
, i
, &lvItem
);
1687 * Adds a selection range.
1690 * [I] infoPtr : valid pointer to the listview structure
1691 * [I] lower : lower item index
1692 * [I] upper : upper item index
1698 static inline BOOL
LISTVIEW_AddSelectionRange(LISTVIEW_INFO
*infoPtr
, INT lower
, INT upper
)
1700 return add_selection_range(infoPtr
, lower
, upper
, FALSE
);
1705 * Removes a range selections.
1708 * [I] infoPtr : valid pointer to the listview structure
1709 * [I] lower : lower item index
1710 * [I] upper : upper item index
1716 static inline BOOL
LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO
*infoPtr
, INT lower
, INT upper
)
1718 return remove_selection_range(infoPtr
, lower
, upper
, FALSE
);
1723 * Removes all selection ranges
1726 * [I] infoPtr : valid pointer to the listview structure
1732 static LRESULT
LISTVIEW_RemoveAllSelections(LISTVIEW_INFO
*infoPtr
)
1734 LISTVIEW_SELECTION
*sel
;
1735 static BOOL removing_all_selections
= FALSE
;
1737 if (removing_all_selections
) return TRUE
;
1739 removing_all_selections
= TRUE
;
1745 sel
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
,0);
1746 if (sel
) LISTVIEW_RemoveSelectionRange(infoPtr
, sel
->lower
, sel
->upper
);
1748 while (infoPtr
->hdpaSelectionRanges
->nItemCount
> 0);
1750 removing_all_selections
= FALSE
;
1757 * Manages the item focus.
1760 * [I] infoPtr : valid pointer to the listview structure
1761 * [I] INT : item index
1764 * TRUE : focused item changed
1765 * FALSE : focused item has NOT changed
1767 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1769 INT oldFocus
= infoPtr
->nFocusedItem
;
1772 ZeroMemory(&lvItem
, sizeof(lvItem
));
1773 lvItem
.state
= LVIS_FOCUSED
;
1774 lvItem
.stateMask
= LVIS_FOCUSED
;
1775 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
1777 return oldFocus
!= infoPtr
->nFocusedItem
;
1782 * Updates the various indices after an item has been inserted or deleted.
1785 * [I] infoPtr : valid pointer to the listview structure
1786 * [I] INT : item index
1787 * [I] INT : Direction of shift, +1 or -1.
1792 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
1794 LISTVIEW_SELECTION selection
,*checkselection
;
1797 TRACE("Shifting %iu, %i steps\n",nItem
,direction
);
1799 selection
.upper
= nItem
;
1800 selection
.lower
= nItem
;
1802 index
= DPA_Search(infoPtr
->hdpaSelectionRanges
, &selection
, 0,
1803 LISTVIEW_CompareSelectionRanges
,
1804 0,DPAS_SORTED
|DPAS_INSERTAFTER
);
1806 while ((index
< infoPtr
->hdpaSelectionRanges
->nItemCount
)&&(index
!= -1))
1808 checkselection
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
,index
);
1809 if ((checkselection
->lower
>= nItem
)&&
1810 ((int)(checkselection
->lower
+ direction
) >= 0))
1811 checkselection
->lower
+= direction
;
1812 if ((checkselection
->upper
>= nItem
)&&
1813 ((int)(checkselection
->upper
+ direction
) >= 0))
1814 checkselection
->upper
+= direction
;
1818 /* Note that the following will fail if direction != +1 and -1 */
1819 if (infoPtr
->nSelectionMark
> nItem
)
1820 infoPtr
->nSelectionMark
+= direction
;
1821 else if (infoPtr
->nSelectionMark
== nItem
)
1824 infoPtr
->nSelectionMark
+= direction
;
1825 else if (infoPtr
->nSelectionMark
>= GETITEMCOUNT(infoPtr
))
1826 infoPtr
->nSelectionMark
= GETITEMCOUNT(infoPtr
) - 1;
1829 if (infoPtr
->nFocusedItem
> nItem
)
1830 infoPtr
->nFocusedItem
+= direction
;
1831 else if (infoPtr
->nFocusedItem
== nItem
)
1834 infoPtr
->nFocusedItem
+= direction
;
1837 if (infoPtr
->nFocusedItem
>= GETITEMCOUNT(infoPtr
))
1838 infoPtr
->nFocusedItem
= GETITEMCOUNT(infoPtr
) - 1;
1839 if (infoPtr
->nFocusedItem
>= 0)
1840 LISTVIEW_SetItemFocus(infoPtr
, infoPtr
->nFocusedItem
);
1843 /* But we are not supposed to modify nHotItem! */
1849 * Adds a block of selections.
1852 * [I] infoPtr : valid pointer to the listview structure
1853 * [I] INT : item index
1858 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1860 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
1861 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
1868 ZeroMemory(&item
,sizeof(item
));
1869 item
.stateMask
= LVIS_SELECTED
;
1870 item
.state
= LVIS_SELECTED
;
1872 /* FIXME: this is not correct LVS_OWNERDATA
1873 * See docu for LVN_ITEMCHANGED. Is there something similar for
1874 * RemoveGroupSelection (is there such a thing?)?
1876 for (i
= nFirst
; i
<= nLast
; i
++)
1877 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
1879 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
1880 infoPtr
->nSelectionMark
= nItem
;
1886 * Adds a single selection.
1889 * [I] infoPtr : valid pointer to the listview structure
1890 * [I] INT : item index
1895 static void LISTVIEW_AddSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1899 ZeroMemory(&item
,sizeof(item
));
1900 item
.state
= LVIS_SELECTED
;
1901 item
.stateMask
= LVIS_SELECTED
;
1903 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
1905 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
1906 infoPtr
->nSelectionMark
= nItem
;
1911 * Selects items based on view coordinates.
1914 * [I] infoPtr : valid pointer to the listview structure
1915 * [I] rcSelRect : selection rectangle
1920 static void LISTVIEW_SetSelectionRect(LISTVIEW_INFO
*infoPtr
, RECT rcSelRect
)
1926 ZeroMemory(&item
,sizeof(item
));
1927 item
.stateMask
= LVIS_SELECTED
;
1929 /* FIXME: shouldn't we iterate over visible items only? */
1930 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
1932 LISTVIEW_GetItemPosition(infoPtr
, i
, &ptItem
);
1934 item
.state
= PtInRect(&rcSelRect
, ptItem
) ? LVIS_SELECTED
: 0;
1935 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
1941 * Sets a single group selection.
1944 * [I] infoPtr : valid pointer to the listview structure
1945 * [I] INT : item index
1950 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1952 UINT uView
= LISTVIEW_GetType(infoPtr
);
1955 ZeroMemory(&item
,sizeof(item
));
1956 item
.stateMask
= LVIS_SELECTED
;
1958 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
1963 if (infoPtr
->nSelectionMark
== -1)
1965 infoPtr
->nSelectionMark
= nFirst
= nLast
= nItem
;
1969 nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
1970 nLast
= max(infoPtr
->nSelectionMark
, nItem
);
1973 for (i
= 0; i
<= GETITEMCOUNT(infoPtr
); i
++)
1975 if ((i
< nFirst
) || (i
> nLast
))
1978 item
.state
= LVIS_SELECTED
;
1979 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
1987 LISTVIEW_GetItemBoundBox(infoPtr
, nItem
, &rcItem
);
1988 LISTVIEW_GetItemBoundBox(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
);
1989 rcSel
.left
= min(rcSelMark
.left
, rcItem
.left
);
1990 rcSel
.top
= min(rcSelMark
.top
, rcItem
.top
);
1991 rcSel
.right
= max(rcSelMark
.right
, rcItem
.right
);
1992 rcSel
.bottom
= max(rcSelMark
.bottom
, rcItem
.bottom
);
1993 LISTVIEW_SetSelectionRect(infoPtr
, rcSel
);
1994 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
1995 nItem
, rcItem
.left
, rcItem
.top
, rcItem
.right
, rcItem
.bottom
,
1996 infoPtr
->nSelectionMark
,
1997 rcSelMark
.left
, rcSelMark
.top
, rcSelMark
.right
, rcSelMark
.bottom
,
1998 rcSel
.left
, rcSel
.top
, rcSel
.right
, rcSel
.bottom
);
2002 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
2007 * Sets a single selection.
2010 * [I] infoPtr : valid pointer to the listview structure
2011 * [I] INT : item index
2016 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2020 LISTVIEW_RemoveAllSelections(infoPtr
);
2022 ZeroMemory(&lvItem
, sizeof(lvItem
));
2023 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
2024 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
2025 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
2027 infoPtr
->nSelectionMark
= nItem
;
2032 * Set selection(s) with keyboard.
2035 * [I] infoPtr : valid pointer to the listview structure
2036 * [I] INT : item index
2039 * SUCCESS : TRUE (needs to be repainted)
2040 * FAILURE : FALSE (nothing has changed)
2042 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2044 /* FIXME: pass in the state */
2045 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
2046 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
2047 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
2048 BOOL bResult
= FALSE
;
2050 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
2052 if (lStyle
& LVS_SINGLESEL
)
2055 LISTVIEW_SetSelection(infoPtr
, nItem
);
2056 ListView_EnsureVisible(infoPtr
->hwndSelf
, nItem
, FALSE
);
2063 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
2067 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
2072 LISTVIEW_SetSelection(infoPtr
, nItem
);
2073 ListView_EnsureVisible(infoPtr
->hwndSelf
, nItem
, FALSE
);
2078 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
2084 * Selects an item based on coordinates.
2087 * [I] infoPtr : valid pointer to the listview structure
2088 * [I] pt : mouse click ccordinates
2091 * SUCCESS : item index
2094 static INT
LISTVIEW_GetItemAtPt(LISTVIEW_INFO
*infoPtr
, POINT pt
)
2096 INT i
, nTop
, nBottom
;
2099 nTop
= LISTVIEW_GetTopIndex(infoPtr
);
2100 nBottom
= nTop
+ LISTVIEW_GetVisibleCount(infoPtr
);
2101 if (nBottom
> GETITEMCOUNT(infoPtr
)) nBottom
= GETITEMCOUNT(infoPtr
);
2103 for (i
= nTop
; i
<= nBottom
; i
++)
2105 rcItem
.left
= LVIR_SELECTBOUNDS
;
2106 if (LISTVIEW_GetItemRect(infoPtr
, i
, &rcItem
))
2107 if (PtInRect(&rcItem
, pt
)) return i
;
2114 * Called when the mouse is being actively tracked and has hovered for a specified
2118 * [I] infoPtr : valid pointer to the listview structure
2119 * [I] fwKeys : key indicator
2120 * [I] pts : mouse position
2123 * 0 if the message was processed, non-zero if there was an error
2126 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2127 * over the item for a certain period of time.
2130 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, WORD fwKyes
, POINTS pts
)
2132 POINT pt
= { pts
.x
, pts
.y
};
2134 if(infoPtr
->dwExStyle
& LVS_EX_TRACKSELECT
)
2135 /* FIXME: select the item!!! */
2136 LISTVIEW_GetItemAtPt(infoPtr
, pt
);
2143 * Called whenever WM_MOUSEMOVE is received.
2146 * [I] infoPtr : valid pointer to the listview structure
2147 * [I] fwKeys : key indicator
2148 * [I] pts : mouse position
2151 * 0 if the message is processed, non-zero if there was an error
2153 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, POINTS pts
)
2155 TRACKMOUSEEVENT trackinfo
;
2157 /* see if we are supposed to be tracking mouse hovering */
2158 if(infoPtr
->dwExStyle
& LVS_EX_TRACKSELECT
) {
2159 /* fill in the trackinfo struct */
2160 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
2161 trackinfo
.dwFlags
= TME_QUERY
;
2162 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
2163 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
2165 /* see if we are already tracking this hwnd */
2166 _TrackMouseEvent(&trackinfo
);
2168 if(!(trackinfo
.dwFlags
& TME_HOVER
)) {
2169 trackinfo
.dwFlags
= TME_HOVER
;
2171 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2172 _TrackMouseEvent(&trackinfo
);
2184 * [IO] HDPA : dynamic pointer array handle
2185 * [I] INT : column index (subitem index)
2191 static BOOL
LISTVIEW_RemoveColumn(HDPA hdpaItems
, INT nSubItem
)
2193 BOOL bResult
= TRUE
;
2197 for (i
= 0; i
< hdpaItems
->nItemCount
; i
++)
2199 hdpaSubItems
= (HDPA
)DPA_GetPtr(hdpaItems
, i
);
2200 if (hdpaSubItems
!= NULL
)
2202 if (!LISTVIEW_RemoveSubItem(hdpaSubItems
, nSubItem
))
2214 * Removes a subitem at a given position.
2217 * [IO] HDPA : dynamic pointer array handle
2218 * [I] INT : subitem index
2224 static BOOL
LISTVIEW_RemoveSubItem(HDPA hdpaSubItems
, INT nSubItem
)
2226 LISTVIEW_SUBITEM
*lpSubItem
;
2229 for (i
= 1; i
< hdpaSubItems
->nItemCount
; i
++)
2231 lpSubItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, i
);
2232 if (lpSubItem
!= NULL
)
2234 if (lpSubItem
->iSubItem
== nSubItem
)
2237 if (is_textW(lpSubItem
->hdr
.pszText
))
2238 COMCTL32_Free(lpSubItem
->hdr
.pszText
);
2241 COMCTL32_Free(lpSubItem
);
2243 /* free dpa memory */
2244 if (DPA_DeletePtr(hdpaSubItems
, i
) == NULL
)
2247 else if (lpSubItem
->iSubItem
> nSubItem
)
2257 * Tests wheather the item is assignable to a list with style lStyle
2259 static inline BOOL
is_assignable_item(LPLVITEMW lpLVItem
, LONG lStyle
)
2261 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
2262 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
2263 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
2270 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2273 * [I] infoPtr : valid pointer to the listview structure
2274 * [I] lpLVItem : valid pointer to new item atttributes
2275 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2281 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
2283 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
2284 UINT uView
= lStyle
& LVS_TYPEMASK
;
2286 LISTVIEW_ITEM
*lpItem
;
2290 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
2293 if (lStyle
& LVS_OWNERDATA
)
2297 /* a virtual livst view stores only state for the mai item */
2298 if (lpLVItem
->iSubItem
|| !(lpLVItem
->mask
& LVIF_STATE
)) return FALSE
;
2300 oldState
= LISTVIEW_GetItemState(infoPtr
, lpLVItem
->iItem
, LVIS_FOCUSED
| LVIS_SELECTED
);
2302 /* we're done if we don't need to change anything we handle */
2303 if ( (oldState
^ lpLVItem
->state
) & lpLVItem
->stateMask
&
2304 ~infoPtr
->uCallbackMask
& (LVIS_FOCUSED
| LVIS_SELECTED
)) return FALSE
;
2307 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2308 * by LVS_OWERNDATA list controls
2311 /* if we handle the focus, and we're asked to change it, do it now */
2312 if ( lpLVItem
->stateMask
& LVIS_FOCUSED
)
2314 if (lpLVItem
->state
& LVIS_FOCUSED
)
2315 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
2316 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
2317 infoPtr
->nFocusedItem
= -1;
2320 /* and the selection is the only other state a virtual list may hold */
2321 if (lpLVItem
->stateMask
& LVIS_SELECTED
)
2323 if (lpLVItem
->state
& LVIS_SELECTED
)
2325 if (lStyle
& LVS_SINGLESEL
) LISTVIEW_RemoveAllSelections(infoPtr
);
2326 add_selection_range(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iItem
, TRUE
);
2329 remove_selection_range(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iItem
, TRUE
);
2332 /* notify the parent now that things have changed */
2333 ZeroMemory(&nmlv
, sizeof(nmlv
));
2334 nmlv
.iItem
= lpLVItem
->iItem
;
2335 nmlv
.uNewState
= lpLVItem
->state
;
2336 nmlv
.uOldState
= oldState
;
2337 nmlv
.uChanged
= LVIF_STATE
;
2338 listview_notify(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
2343 /* sanity checks first */
2344 if (!is_assignable_item(lpLVItem
, lStyle
)) return FALSE
;
2346 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
2347 if (!hdpaSubItems
&& hdpaSubItems
!= (HDPA
)-1) return FALSE
;
2349 lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
2350 if (!lpItem
) return FALSE
;
2352 /* determine what fields will change */
2353 if ((lpLVItem
->mask
& LVIF_STATE
) &&
2354 ((lpItem
->state
^ lpLVItem
->state
) & lpLVItem
->stateMask
))
2355 uChanged
|= LVIF_STATE
;
2357 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
2358 uChanged
|= LVIF_IMAGE
;
2360 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
2361 uChanged
|= LVIF_PARAM
;
2363 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
2364 uChanged
|= LVIF_INDENT
;
2366 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
2367 uChanged
|= LVIF_TEXT
;
2369 if (!uChanged
) return TRUE
;
2371 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
2372 nmlv
.iItem
= lpLVItem
->iItem
;
2373 nmlv
.uNewState
= lpLVItem
->state
& lpLVItem
->stateMask
;
2374 nmlv
.uOldState
= lpItem
->state
& lpLVItem
->stateMask
;
2375 nmlv
.uChanged
= uChanged
;
2376 nmlv
.lParam
= lpItem
->lParam
;
2378 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2379 if(lpItem
->valid
&& listview_notify(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
2382 /* copy information */
2383 if (lpLVItem
->mask
& LVIF_TEXT
)
2384 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
2386 if (lpLVItem
->mask
& LVIF_IMAGE
)
2387 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
2389 if (lpLVItem
->mask
& LVIF_PARAM
)
2390 lpItem
->lParam
= lpLVItem
->lParam
;
2392 if (lpLVItem
->mask
& LVIF_INDENT
)
2393 lpItem
->iIndent
= lpLVItem
->iIndent
;
2395 if (uChanged
& LVIF_STATE
)
2397 lpItem
->state
&= ~lpLVItem
->stateMask
;
2398 lpItem
->state
|= (lpLVItem
->state
& lpLVItem
->stateMask
);
2399 if (nmlv
.uNewState
& LVIS_SELECTED
)
2401 if (lStyle
& LVS_SINGLESEL
) LISTVIEW_RemoveAllSelections(infoPtr
);
2402 add_selection_range(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iItem
, TRUE
);
2404 else if (lpLVItem
->stateMask
& LVIS_SELECTED
)
2405 remove_selection_range(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iItem
, TRUE
);
2407 /* if we are asked to change focus, and we manage it, do it */
2408 if (nmlv
.uNewState
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
2410 if (lpLVItem
->state
& LVIS_FOCUSED
)
2412 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
2413 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, FALSE
);
2415 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
2416 infoPtr
->nFocusedItem
= -1;
2420 /* if LVS_LIST or LVS_SMALLICON, update the width of the items */
2421 if((uChanged
& LVIF_TEXT
) && ((uView
== LVS_LIST
) || (uView
== LVS_SMALLICON
)))
2423 int item_width
= LISTVIEW_CalculateWidth(infoPtr
, lpLVItem
->iItem
);
2424 if(item_width
> infoPtr
->nItemWidth
) infoPtr
->nItemWidth
= item_width
;
2427 /* if we're inserting the item, we're done */
2428 if (!lpItem
->valid
) return TRUE
;
2430 /* send LVN_ITEMCHANGED notification */
2431 nmlv
.lParam
= lpItem
->lParam
;
2432 listview_notify(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
2439 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2442 * [I] infoPtr : valid pointer to the listview structure
2443 * [I] lpLVItem : valid pointer to new subitem atttributes
2444 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2450 static BOOL
set_sub_item(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
2452 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
2454 LISTVIEW_SUBITEM
*lpSubItem
;
2456 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
2458 if (lStyle
& LVS_OWNERDATA
) return FALSE
;
2460 /* set subitem only if column is present */
2461 if (Header_GetItemCount(infoPtr
->hwndHeader
) <= lpLVItem
->iSubItem
)
2464 /* First do some sanity checks */
2465 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
)) return FALSE
;
2467 if (!is_assignable_item(lpLVItem
, lStyle
)) return FALSE
;
2469 /* get the subitem structure, and create it if not there */
2470 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
2471 if (!hdpaSubItems
) return FALSE
;
2473 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
2476 LISTVIEW_SUBITEM
*tmpSubItem
;
2479 lpSubItem
= (LISTVIEW_SUBITEM
*)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM
));
2480 if (!lpSubItem
) return FALSE
;
2481 ZeroMemory(lpSubItem
, sizeof(LISTVIEW_SUBITEM
));
2483 /* we could binary search here, if need be...*/
2484 for (i
= 1; i
< hdpaSubItems
->nItemCount
; i
++)
2486 tmpSubItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, i
);
2487 if (tmpSubItem
&& tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
2489 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
2491 COMCTL32_Free(lpSubItem
);
2496 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
2498 if (lpLVItem
->mask
& LVIF_IMAGE
)
2499 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
2501 if (lpLVItem
->mask
& LVIF_TEXT
)
2502 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
2509 * Sets item attributes.
2512 * [I] infoPtr : valid pointer to the listview structure
2513 * [I] LPLVITEM : new item atttributes
2514 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2520 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
2522 INT oldFocus
= infoPtr
->nFocusedItem
;
2523 LPWSTR pszText
= NULL
;
2527 if (!lpLVItem
|| lpLVItem
->iItem
< 0 ||
2528 lpLVItem
->iItem
>=GETITEMCOUNT(infoPtr
))
2531 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
2532 if ((lpLVItem
->mask
& LVIF_TEXT
) && lpLVItem
->pszText
)
2534 pszText
= lpLVItem
->pszText
;
2535 lpLVItem
->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
2537 /* actually set the fields */
2538 if (lpLVItem
->iSubItem
)
2539 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
);
2541 bResult
= set_main_item(infoPtr
, lpLVItem
, TRUE
);
2542 /* redraw item, if necessary */
2543 if (bResult
&& !infoPtr
->bIsDrawing
)
2545 if (oldFocus
!= infoPtr
->nFocusedItem
&& infoPtr
->bFocus
)
2546 LISTVIEW_ToggleFocusRect(infoPtr
);
2547 rcItem
.left
= LVIR_BOUNDS
;
2548 LISTVIEW_GetItemRect(infoPtr
, lpLVItem
->iItem
, &rcItem
);
2549 InvalidateRect(infoPtr
->hwndSelf
, &rcItem
, TRUE
);
2554 textfreeT(lpLVItem
->pszText
, isW
);
2555 lpLVItem
->pszText
= pszText
;
2563 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2566 * [I] infoPtr : valid pointer to the listview structure
2571 static INT
LISTVIEW_GetTopIndex(LISTVIEW_INFO
*infoPtr
)
2573 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
2574 UINT uView
= lStyle
& LVS_TYPEMASK
;
2576 SCROLLINFO scrollInfo
;
2578 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
2579 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
2580 scrollInfo
.fMask
= SIF_POS
;
2582 if (uView
== LVS_LIST
)
2584 if ((lStyle
& WS_HSCROLL
) && GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
2585 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
2587 else if (uView
== LVS_REPORT
)
2589 if ((lStyle
& WS_VSCROLL
) && GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
2590 nItem
= scrollInfo
.nPos
;
2602 * [I] infoPtr : valid pointer to the listview structure
2603 * [I] HDC : device context handle
2604 * [I] INT : item index
2605 * [I] INT : subitem index
2606 * [I] RECT * : clipping rectangle
2611 static void LISTVIEW_DrawSubItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, INT nSubItem
,
2612 RECT rcItem
, BOOL Selected
)
2614 WCHAR szDispText
[DISP_TEXT_SIZE
];
2617 UINT textoutOptions
= ETO_CLIPPED
| ETO_OPAQUE
;
2620 INT nLabelWidth
= 0;
2622 TRACE("(hdc=%x, nItem=%d, nSubItem=%d)\n", hdc
,
2625 /* get information needed for drawing the item */
2626 ZeroMemory(&lvItem
, sizeof(lvItem
));
2627 lvItem
.mask
= LVIF_TEXT
;
2628 lvItem
.iItem
= nItem
;
2629 lvItem
.iSubItem
= nSubItem
;
2630 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2631 lvItem
.pszText
= szDispText
;
2632 *lvItem
.pszText
= '\0';
2633 LISTVIEW_GetItemW(infoPtr
, &lvItem
, TRUE
);
2634 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
2636 ZeroMemory(&lvColumn
, sizeof(lvColumn
));
2637 lvColumn
.mask
= LVCF_FMT
;
2638 LISTVIEW_GetColumnT(infoPtr
, nSubItem
, &lvColumn
, TRUE
);
2639 textLeft
= rcItem
.left
;
2640 TRACE("lvColumn.fmt=%d\n", lvColumn
.fmt
);
2641 if (lvColumn
.fmt
!= LVCFMT_LEFT
)
2643 if ((nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
)))
2645 if (lvColumn
.fmt
== LVCFMT_RIGHT
)
2646 textLeft
= rcItem
.right
- nLabelWidth
;
2648 textLeft
= rcItem
.left
+ (rcItem
.right
-rcItem
.left
-nLabelWidth
)/2;
2653 /* redraw the background of the item */
2655 if(infoPtr
->nColumnCount
== (nSubItem
+ 1))
2656 rcTemp
.right
= infoPtr
->rcList
.right
;
2658 rcTemp
.right
+= WIDTH_PADDING
;
2660 if (infoPtr
->clrBk
!= CLR_NONE
) FillRect(hdc
, &rcTemp
, infoPtr
->hBkBrush
);
2662 /* set item colors */
2663 if (ListView_GetItemState(infoPtr
->hwndSelf
,nItem
,LVIS_SELECTED
) && Selected
)
2665 if (infoPtr
->bFocus
)
2667 SetBkColor(hdc
, comctl32_color
.clrHighlight
);
2668 SetTextColor(hdc
, comctl32_color
.clrHighlightText
);
2672 SetBkColor(hdc
, comctl32_color
.clr3dFace
);
2673 SetTextColor(hdc
, comctl32_color
.clrBtnText
);
2678 if ( (infoPtr
->clrTextBk
== CLR_DEFAULT
) || (infoPtr
->clrTextBk
== CLR_NONE
) )
2680 SetBkMode(hdc
, TRANSPARENT
);
2681 textoutOptions
&= ~ETO_OPAQUE
;
2685 SetBkMode(hdc
, OPAQUE
);
2686 SetBkColor(hdc
, infoPtr
->clrTextBk
);
2689 SetTextColor(hdc
, infoPtr
->clrText
);
2692 TRACE("drawing text %s, l=%d, t=%d, rect=(%d,%d)-(%d,%d)\n",
2693 debugstr_w(lvItem
.pszText
), textLeft
, rcItem
.top
,
2694 rcItem
.left
, rcItem
.top
, rcItem
.right
, rcItem
.bottom
);
2695 ExtTextOutW(hdc
, textLeft
, rcItem
.top
, textoutOptions
,
2696 &rcItem
, lvItem
.pszText
, lstrlenW(lvItem
.pszText
), NULL
);
2700 /* fill in the gap */
2702 if (nSubItem
< Header_GetItemCount(infoPtr
->hwndHeader
)-1)
2704 CopyRect(&rec
,&rcItem
);
2705 rec
.left
= rec
.right
;
2706 rec
.right
= rec
.left
+REPORT_MARGINX
;
2707 ExtTextOutW(hdc
, rec
.left
, rec
.top
, textoutOptions
,
2708 &rec
, NULL
, 0, NULL
);
2710 CopyRect(&rec
,&rcItem
);
2711 rec
.right
= rec
.left
;
2712 rec
.left
= rec
.left
- REPORT_MARGINX
;
2713 ExtTextOutW(hdc
, rec
.left
, rec
.top
, textoutOptions
,
2714 &rec
, NULL
, 0, NULL
);
2724 * [I] infoPtr : valid pointer to the listview structure
2725 * [I] hdc : device context handle
2726 * [I] nItem : item index
2727 * [I] rcItem : item rectangle
2728 * [I] bFullSelect : TRUE if all item is selected
2729 * [O] lprcFocus : pointer to rectangle to receive focus rect
2734 static void LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, RECT rcItem
, BOOL bFullSelect
, RECT
* lprcFocus
)
2736 WCHAR szDispText
[DISP_TEXT_SIZE
];
2741 DWORD dwTextColor
,dwTextX
;
2742 BOOL bImage
= FALSE
;
2744 UINT textoutOptions
= ETO_OPAQUE
| ETO_CLIPPED
;
2747 TRACE("(hdc=%x, nItem=%d)\n", hdc
, nItem
);
2749 /* get information needed for drawing the item */
2750 ZeroMemory(&lvItem
, sizeof(lvItem
));
2751 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
| LVIF_INDENT
;
2752 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_STATEIMAGEMASK
;
2753 lvItem
.iItem
= nItem
;
2754 lvItem
.iSubItem
= 0;
2755 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2756 lvItem
.pszText
= szDispText
;
2757 *lvItem
.pszText
= '\0';
2758 LISTVIEW_GetItemW(infoPtr
, &lvItem
, TRUE
);
2759 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
2761 /* redraw the background of the item */
2763 if(infoPtr
->nColumnCount
== (nItem
+ 1))
2764 rcTemp
.right
= infoPtr
->rcList
.right
;
2766 rcTemp
.right
+=WIDTH_PADDING
;
2768 if (infoPtr
->clrBk
!= CLR_NONE
) FillRect(hdc
, &rcTemp
, infoPtr
->hBkBrush
);
2771 if (lvItem
.iIndent
>0 && infoPtr
->iconSize
.cx
> 0)
2773 rcItem
.left
+= infoPtr
->iconSize
.cx
* lvItem
.iIndent
;
2777 if (infoPtr
->himlState
!= NULL
)
2779 UINT uStateImage
= (lvItem
.state
& LVIS_STATEIMAGEMASK
) >> 12;
2780 if (uStateImage
> 0)
2781 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
, rcItem
.left
,
2782 rcItem
.top
, ILD_NORMAL
);
2784 rcItem
.left
+= infoPtr
->iconSize
.cx
;
2789 if (infoPtr
->himlSmall
!= NULL
)
2791 if ((lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
) &&
2794 ImageList_SetBkColor(infoPtr
->himlSmall
, CLR_NONE
);
2795 ImageList_Draw(infoPtr
->himlSmall
, lvItem
.iImage
, hdc
, rcItem
.left
,
2796 rcItem
.top
, ILD_SELECTED
);
2798 else if (lvItem
.iImage
>=0)
2800 ImageList_SetBkColor(infoPtr
->himlSmall
, CLR_NONE
);
2801 ImageList_Draw(infoPtr
->himlSmall
, lvItem
.iImage
, hdc
, rcItem
.left
,
2802 rcItem
.top
, ILD_NORMAL
);
2805 rcItem
.left
+= infoPtr
->iconSize
.cx
;
2809 /* Don't bother painting item being edited */
2810 if (infoPtr
->bEditing
&& lprcFocus
&& !bFullSelect
)
2812 SetRectEmpty(lprcFocus
);
2816 if ((lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
))
2818 /* set item colors */
2819 dwBkColor
= SetBkColor(hdc
, comctl32_color
.clrHighlight
);
2820 dwTextColor
= SetTextColor(hdc
, comctl32_color
.clrHighlightText
);
2821 /* set raster mode */
2822 nMixMode
= SetROP2(hdc
, R2_XORPEN
);
2824 else if ((GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & LVS_SHOWSELALWAYS
) &&
2825 (lvItem
.state
& LVIS_SELECTED
) && (!infoPtr
->bFocus
))
2827 dwBkColor
= SetBkColor(hdc
, comctl32_color
.clr3dFace
);
2828 dwTextColor
= SetTextColor(hdc
, comctl32_color
.clrBtnText
);
2829 /* set raster mode */
2830 nMixMode
= SetROP2(hdc
, R2_COPYPEN
);
2834 /* set item colors */
2835 if ( (infoPtr
->clrTextBk
== CLR_DEFAULT
) || (infoPtr
->clrTextBk
== CLR_NONE
) )
2837 dwBkColor
= GetBkColor(hdc
);
2838 iBkMode
= SetBkMode(hdc
, TRANSPARENT
);
2839 textoutOptions
&= ~ETO_OPAQUE
;
2843 dwBkColor
= SetBkColor(hdc
, infoPtr
->clrTextBk
);
2844 iBkMode
= SetBkMode(hdc
, OPAQUE
);
2847 dwTextColor
= SetTextColor(hdc
, infoPtr
->clrText
);
2848 /* set raster mode */
2849 nMixMode
= SetROP2(hdc
, R2_COPYPEN
);
2852 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
2853 if (rcItem
.left
+ nLabelWidth
< rcItem
.right
)
2856 rcItem
.right
= rcItem
.left
+ nLabelWidth
+ TRAILING_PADDING
;
2858 rcItem
.right
+= IMAGE_PADDING
;
2862 dwTextX
= rcItem
.left
+ 1;
2864 dwTextX
+= IMAGE_PADDING
;
2866 /* compute the focus rectangle */
2871 *lprcFocus
= rcItem
;
2872 lprcFocus
->right
-= 2;
2874 else SetRectEmpty(lprcFocus
);
2879 TRACE("drawing text rect=(%d,%d)-(%d,%d)\n",
2880 rcItem
.left
, rcItem
.top
, rcItem
.right
, rcItem
.bottom
);
2881 ExtTextOutW(hdc
, dwTextX
, rcItem
.top
, textoutOptions
,
2882 &rcItem
, lvItem
.pszText
, lstrlenW(lvItem
.pszText
), NULL
);
2885 if ((bFullSelect
) && (Header_GetItemCount(infoPtr
->hwndHeader
) > 1))
2887 /* fill in the gap */
2889 CopyRect(&rec
,&rcItem
);
2890 rec
.left
= rec
.right
;
2891 rec
.right
= rec
.left
+REPORT_MARGINX
;
2892 ExtTextOutW(hdc
, rec
.left
, rec
.top
, textoutOptions
,
2893 &rec
, NULL
, 0, NULL
);
2898 SetROP2(hdc
, R2_COPYPEN
);
2899 SetBkColor(hdc
, dwBkColor
);
2900 SetTextColor(hdc
, dwTextColor
);
2902 SetBkMode(hdc
, iBkMode
);
2908 * Draws an item when in large icon display mode.
2911 * [I] infoPtr : valid pointer to the listview structure
2912 * [I] hdc : device context handle
2913 * [I] nItem : item index
2914 * [I] rcItem : clipping rectangle
2915 * [O] lprcFocus : the text rectangle about which to draw the focus
2920 static void LISTVIEW_DrawLargeItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, RECT rcItem
,
2923 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2925 UINT uFormat
= LISTVIEW_DTFLAGS
;
2928 TRACE("(hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
2929 hdc
, nItem
, rcItem
.left
, rcItem
.top
, rcItem
.right
, rcItem
.bottom
);
2931 /* get information needed for drawing the item */
2932 ZeroMemory(&lvItem
, sizeof(lvItem
));
2933 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
;
2934 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
2935 lvItem
.iItem
= nItem
;
2936 lvItem
.iSubItem
= 0;
2937 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2938 lvItem
.pszText
= szDispText
;
2939 *lvItem
.pszText
= '\0';
2940 LISTVIEW_GetItemW(infoPtr
, &lvItem
, FALSE
);
2941 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
2945 TRACE("background rect (%d,%d)-(%d,%d)\n",
2946 rcFill
.left
, rcFill
.top
, rcFill
.right
, rcFill
.bottom
);
2948 if (infoPtr
->clrBk
!= CLR_NONE
) FillRect(hdc
, &rcFill
, infoPtr
->hBkBrush
);
2950 /* Figure out text colours etc. depending on state
2951 * At least the following states exist; there may be more.
2952 * Many items may be selected
2953 * At most one item may have the focus
2954 * The application may not actually be active currently
2955 * 1. The item is not selected in any way
2956 * 2. The cursor is flying over the icon or text and the text is being
2957 * expanded because it is not fully displayed currently.
2958 * 3. The item is selected and is focussed, i.e. the user has not clicked
2959 * in the blank area of the window, and the window (or application?)
2960 * still has the focus.
2961 * 4. As 3 except that a different window has the focus
2962 * 5. The item is the selected item of all the items, but the user has
2963 * clicked somewhere else on the window.
2964 * Only a few of these are handled currently. In particular 2 is not yet
2965 * handled since we do not support the functionality currently (or at least
2966 * we didn't when I wrote this)
2969 if (lvItem
.state
& LVIS_SELECTED
)
2971 /* set item colors */
2972 SetBkColor(hdc
, comctl32_color
.clrHighlight
);
2973 SetTextColor(hdc
, comctl32_color
.clrHighlightText
);
2974 SetBkMode (hdc
, OPAQUE
);
2975 /* set raster mode */
2976 SetROP2(hdc
, R2_XORPEN
);
2977 /* When exactly is it in XOR? while being dragged? */
2981 /* set item colors */
2982 if ( (infoPtr
->clrTextBk
== CLR_DEFAULT
) || (infoPtr
->clrTextBk
== CLR_NONE
) )
2984 SetBkMode(hdc
, TRANSPARENT
);
2988 SetBkMode(hdc
, OPAQUE
);
2989 SetBkColor(hdc
, infoPtr
->clrTextBk
);
2992 SetTextColor(hdc
, infoPtr
->clrText
);
2993 /* set raster mode */
2994 SetROP2(hdc
, R2_COPYPEN
);
2997 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
2998 * wrapping and long words split.
2999 * In cases 1 and 4 only a portion of the text is displayed with word
3000 * wrapping and both word and end ellipsis. (I don't yet know about path
3003 uFormat
|= lprcFocus
? DT_NOCLIP
: DT_WORD_ELLIPSIS
| DT_END_ELLIPSIS
;
3006 if (infoPtr
->himlNormal
!= NULL
)
3008 if (lvItem
.iImage
>= 0)
3010 ImageList_Draw (infoPtr
->himlNormal
, lvItem
.iImage
, hdc
, rcItem
.left
,
3012 (lvItem
.state
& LVIS_SELECTED
) ? ILD_SELECTED
: ILD_NORMAL
);
3013 TRACE("icon %d at (%d,%d)\n",
3014 lvItem
.iImage
, rcItem
.left
, rcItem
.top
);
3018 /* Draw the text below the icon */
3020 /* Don't bother painting item being edited */
3021 if ((infoPtr
->bEditing
&& lprcFocus
) || !lvItem
.pszText
|| !lstrlenW(lvItem
.pszText
))
3023 if(lprcFocus
) SetRectEmpty(lprcFocus
);
3027 /* Since rcItem.left is left point of icon, compute left point of item box */
3028 rcItem
.left
-= ((infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2);
3029 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
3030 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
3031 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3032 rcItem
.left
, rcItem
.top
, rcItem
.right
, rcItem
.bottom
,
3033 infoPtr
->iconSize
.cx
, infoPtr
->nItemWidth
);
3034 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3035 infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
3036 infoPtr
->rcList
.right
, infoPtr
->rcList
.bottom
,
3037 infoPtr
->rcView
.left
, infoPtr
->rcView
.top
,
3038 infoPtr
->rcView
.right
, infoPtr
->rcView
.bottom
);
3040 InflateRect(&rcItem
, -(2*CAPTION_BORDER
), 0);
3041 rcItem
.top
+= infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
3046 /* I am sure of most of the uFormat values. However I am not sure about
3047 * whether we need or do not need the following:
3048 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3049 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3050 * We certainly do not need
3051 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3052 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3055 /* If the text is being drawn without clipping (i.e. the full text) then we
3056 * need to jump through a few hoops to ensure that it all gets displayed and
3057 * that the background is complete
3059 if (uFormat
& DT_NOCLIP
)
3062 HBRUSH hBrush
= CreateSolidBrush(GetBkColor (hdc
));
3063 int dx
, dy
, old_wid
, new_wid
;
3064 DrawTextW (hdc
, lvItem
.pszText
, -1, &rcItem
, uFormat
| DT_CALCRECT
);
3065 /* Microsoft, in their great wisdom, have decided that the rectangle
3066 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3067 * not the location. So we have to do the centring ourselves (and take
3068 * responsibility for agreeing off-by-one consistency with them).
3070 old_wid
= rcItem
.right
-rcItem
.left
;
3071 new_wid
= rcBack
.right
- rcBack
.left
;
3072 dx
= rcBack
.left
- rcItem
.left
+ (new_wid
-old_wid
)/2;
3073 dy
= rcBack
.top
- rcItem
.top
;
3074 OffsetRect (&rcItem
, dx
, dy
);
3075 FillRect(hdc
, &rcItem
, hBrush
);
3076 DeleteObject(hBrush
);
3078 /* else ? What if we are losing the focus? will we not get a complete
3081 DrawTextW (hdc
, lvItem
.pszText
, -1, &rcItem
, uFormat
);
3082 TRACE("text at (%d,%d)-(%d,%d) is %s\n",
3083 rcItem
.left
, rcItem
.top
, rcItem
.right
, rcItem
.bottom
,
3084 debugstr_w(lvItem
.pszText
));
3086 if(lprcFocus
) CopyRect(lprcFocus
, &rcItem
);
3091 * Draws listview items when in report display mode.
3094 * [I] infoPtr : valid pointer to the listview structure
3095 * [I] HDC : device context handle
3100 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD cdmode
)
3102 SCROLLINFO scrollInfo
;
3103 INT nDrawPosY
= infoPtr
->rcList
.top
;
3105 RECT rcItem
, rcFull
, *lprcFocus
;
3110 DWORD cditemmode
= CDRF_DODEFAULT
;
3111 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
3116 nItem
= ListView_GetTopIndex(infoPtr
->hwndSelf
);
3118 /* add 1 for displaying a partial item at the bottom */
3119 nLast
= min(nItem
+ LISTVIEW_GetCountPerColumn(infoPtr
) + 1, GETITEMCOUNT(infoPtr
));
3121 /* send cache hint notification */
3122 if (lStyle
& LVS_OWNERDATA
)
3126 nmlv
.hdr
.hwndFrom
= infoPtr
->hwndSelf
;
3127 nmlv
.hdr
.idFrom
= GetWindowLongW(infoPtr
->hwndSelf
,GWL_ID
);
3128 nmlv
.hdr
.code
= LVN_ODCACHEHINT
;
3132 SendMessageW(GetParent(infoPtr
->hwndSelf
), WM_NOTIFY
, (WPARAM
)nmlv
.hdr
.idFrom
,
3136 nColumnCount
= Header_GetItemCount(infoPtr
->hwndHeader
);
3137 infoPtr
->nColumnCount
= nColumnCount
; /* update nColumnCount */
3138 FullSelected
= infoPtr
->dwExStyle
& LVS_EX_FULLROWSELECT
;
3140 /* nothing to draw */
3141 if(GETITEMCOUNT(infoPtr
) == 0)
3144 /* if we have focus, precompute full select rect, if need be */
3145 if (infoPtr
->bFocus
&& FullSelected
)
3149 Header_GetItemRect(infoPtr
->hwndHeader
, 0, &rcFull
);
3150 Header_GetItemRect(infoPtr
->hwndHeader
, nColumnCount
-1, &br
);
3152 rcFull
.left
+= REPORT_MARGINX
;
3153 rcFull
.right
= max(rcFull
.left
, br
.right
- REPORT_MARGINX
);
3156 /* Get scroll bar info once before loop */
3157 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
3158 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3159 scrollInfo
.fMask
= SIF_POS
;
3160 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
);
3161 scrollOffset
= scrollInfo
.nPos
;
3163 for (; nItem
< nLast
; nItem
++)
3166 if (lStyle
& LVS_OWNERDRAWFIXED
)
3168 UINT uID
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_ID
);
3173 TRACE("Owner Drawn\n");
3174 dis
.CtlType
= ODT_LISTVIEW
;
3177 dis
.itemAction
= ODA_DRAWENTIRE
;
3180 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
3181 dis
.itemState
|= ODS_SELECTED
;
3182 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
3183 dis
.itemState
|= ODS_FOCUS
;
3185 dis
.hwndItem
= infoPtr
->hwndSelf
;
3188 Header_GetItemRect(infoPtr
->hwndHeader
, nColumnCount
-1, &br
);
3190 dis
.rcItem
.left
= -scrollOffset
;
3191 dis
.rcItem
.right
= max(dis
.rcItem
.left
, br
.right
- scrollOffset
);
3192 dis
.rcItem
.top
= nDrawPosY
;
3193 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
3195 ZeroMemory(&item
,sizeof(item
));
3197 item
.mask
= LVIF_PARAM
;
3198 ListView_GetItemW(infoPtr
->hwndSelf
, &item
);
3200 dis
.itemData
= item
.lParam
;
3202 if (SendMessageW(GetParent(infoPtr
->hwndSelf
),WM_DRAWITEM
,(WPARAM
)uID
,(LPARAM
)&dis
))
3204 nDrawPosY
+= infoPtr
->nItemHeight
;
3209 /* if we have focus, calculate focus rect */
3210 if (infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
3211 lprcFocus
= &infoPtr
->rcFocus
;
3215 for (j
= 0; j
< nColumnCount
; j
++)
3217 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3218 cditemmode
= LISTVIEW_SendCustomDrawItemNotify (infoPtr
, hdc
, nItem
, j
,
3220 if (cditemmode
& CDRF_SKIPDEFAULT
)
3223 Header_GetItemRect(infoPtr
->hwndHeader
, j
, &rcItem
);
3225 rcItem
.left
+= REPORT_MARGINX
;
3226 rcItem
.right
= max(rcItem
.left
, rcItem
.right
- REPORT_MARGINX
);
3227 rcItem
.top
= nDrawPosY
;
3228 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
3230 /* Offset the Scroll Bar Pos */
3231 rcItem
.left
-= scrollOffset
;
3232 rcItem
.right
-= scrollOffset
;
3235 LISTVIEW_DrawItem(infoPtr
, hdc
, nItem
, rcItem
, FullSelected
, lprcFocus
);
3237 LISTVIEW_DrawSubItem(infoPtr
, hdc
, nItem
, j
, rcItem
, FullSelected
);
3239 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3240 LISTVIEW_SendCustomDrawItemNotify(infoPtr
, hdc
, nItem
, 0,
3241 CDDS_ITEMPOSTPAINT
);
3244 /* Adjust focus if we have it, and we are in full select */
3245 if (lprcFocus
&& FullSelected
)
3247 rcFull
.top
= nDrawPosY
;
3248 rcFull
.bottom
= rcFull
.top
+ infoPtr
->nItemHeight
;
3249 infoPtr
->rcFocus
= rcFull
;
3252 nDrawPosY
+= infoPtr
->nItemHeight
;
3258 * Draws listview items when in list display mode.
3261 * [I] infoPtr : valid pointer to the listview structure
3262 * [I] HDC : device context handle
3267 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD cdmode
)
3269 RECT rcItem
, *lprcFocus
;
3273 INT nCountPerColumn
;
3274 INT nItemWidth
= infoPtr
->nItemWidth
;
3275 INT nItemHeight
= infoPtr
->nItemHeight
;
3276 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
3277 DWORD cditemmode
= CDRF_DODEFAULT
;
3279 /* get number of fully visible columns */
3280 nColumnCount
= nListWidth
/ nItemWidth
;
3281 if (nListWidth
% nItemWidth
) nColumnCount
++;
3282 infoPtr
->nColumnCount
= nColumnCount
;
3283 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
3284 nItem
= ListView_GetTopIndex(infoPtr
->hwndSelf
);
3285 TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3286 nColumnCount
, nCountPerColumn
, nItem
);
3288 /* nothing to draw, return here */
3289 if(GETITEMCOUNT(infoPtr
) == 0)
3292 for (i
= 0; i
< nColumnCount
; i
++)
3294 for (j
= 0; j
< nCountPerColumn
; j
++, nItem
++)
3296 if (nItem
>= GETITEMCOUNT(infoPtr
))
3299 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3300 cditemmode
= LISTVIEW_SendCustomDrawItemNotify (infoPtr
, hdc
, nItem
, 0,
3302 if (cditemmode
& CDRF_SKIPDEFAULT
)
3305 rcItem
.top
= j
* nItemHeight
;
3306 rcItem
.left
= i
* nItemWidth
;
3307 rcItem
.bottom
= rcItem
.top
+ nItemHeight
;
3308 rcItem
.right
= rcItem
.left
+ nItemWidth
;
3310 /* if we have focus, calculate focus rect */
3311 if (infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
3312 lprcFocus
= &infoPtr
->rcFocus
;
3316 LISTVIEW_DrawItem(infoPtr
, hdc
, nItem
, rcItem
, FALSE
, lprcFocus
);
3318 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3319 LISTVIEW_SendCustomDrawItemNotify(infoPtr
, hdc
, nItem
, 0,
3320 CDDS_ITEMPOSTPAINT
);
3328 * Draws listview items when in icon or small icon display mode.
3331 * [I] infoPtr : valid pointer to the listview structure
3332 * [I] HDC : device context handle
3337 static void LISTVIEW_RefreshIcon(LISTVIEW_INFO
*infoPtr
, HDC hdc
, BOOL bSmall
, DWORD cdmode
)
3341 RECT rcItem
, *lprcFocus
;
3343 DWORD cditemmode
= CDRF_DODEFAULT
;
3346 infoPtr
->nColumnCount
= 1; /* set this to an arbitrary value to prevent */
3347 /* DrawItem from erasing the incorrect background area */
3349 /* nothing to draw, return here */
3350 if(GETITEMCOUNT(infoPtr
) == 0)
3353 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
3354 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
3356 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3357 cditemmode
= LISTVIEW_SendCustomDrawItemNotify (infoPtr
, hdc
, i
, 0,
3359 if (cditemmode
& CDRF_SKIPDEFAULT
)
3362 LISTVIEW_GetItemPosition(infoPtr
, i
, &ptPosition
);
3363 ptPosition
.x
+= ptOrigin
.x
;
3364 ptPosition
.y
+= ptOrigin
.y
;
3366 if (ptPosition
.y
+ infoPtr
->nItemHeight
> infoPtr
->rcList
.top
)
3368 if (ptPosition
.x
+ infoPtr
->nItemWidth
> infoPtr
->rcList
.left
)
3370 if (ptPosition
.y
< infoPtr
->rcList
.bottom
)
3372 if (ptPosition
.x
< infoPtr
->rcList
.right
)
3374 rcItem
.top
= ptPosition
.y
;
3375 rcItem
.left
= ptPosition
.x
;
3376 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
3377 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
3379 /* if we have focus, calculate focus rect */
3380 if (infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, i
, LVIS_FOCUSED
))
3381 lprcFocus
= &infoPtr
->rcFocus
;
3386 LISTVIEW_DrawItem(infoPtr
, hdc
, i
, rcItem
, FALSE
, lprcFocus
);
3388 LISTVIEW_DrawLargeItem(infoPtr
, hdc
, i
, rcItem
, lprcFocus
);
3393 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3394 LISTVIEW_SendCustomDrawItemNotify(infoPtr
, hdc
, i
, 0,
3395 CDDS_ITEMPOSTPAINT
);
3401 * Draws listview items.
3404 * [I] infoPtr : valid pointer to the listview structure
3405 * [I] HDC : device context handle
3410 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
3412 UINT uView
= LISTVIEW_GetType(infoPtr
);
3415 RECT rcClient
, rcTemp
;
3417 LISTVIEW_DUMP(infoPtr
);
3419 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
3421 cdmode
= LISTVIEW_SendCustomDrawNotify(infoPtr
, CDDS_PREPAINT
, hdc
, rcClient
);
3422 if (cdmode
== CDRF_SKIPDEFAULT
) return;
3424 infoPtr
->bIsDrawing
= TRUE
;
3427 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
3429 /* select transparent brush (for drawing the focus box) */
3430 SelectObject(hdc
, GetStockObject(NULL_BRUSH
));
3432 /* paint the background of the control that doesn't contain any items */
3433 SubtractRect(&rcTemp
, &infoPtr
->rcList
, &infoPtr
->rcView
);
3434 if (infoPtr
->clrBk
!= CLR_NONE
) FillRect(hdc
, &rcTemp
, infoPtr
->hBkBrush
);
3436 if (uView
== LVS_LIST
)
3437 LISTVIEW_RefreshList(infoPtr
, hdc
, cdmode
);
3438 else if (uView
== LVS_REPORT
)
3439 LISTVIEW_RefreshReport(infoPtr
, hdc
, cdmode
);
3441 LISTVIEW_RefreshIcon(infoPtr
, hdc
, uView
== LVS_SMALLICON
, cdmode
);
3443 /* if we have a focus rect, draw it */
3444 if (infoPtr
->bFocus
&& !IsRectEmpty(&infoPtr
->rcFocus
))
3445 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
3447 /* unselect objects */
3448 SelectObject(hdc
, hOldFont
);
3450 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
3451 LISTVIEW_SendCustomDrawNotify(infoPtr
, CDDS_POSTPAINT
, hdc
, rcClient
);
3453 infoPtr
->bIsDrawing
= FALSE
;
3459 * Calculates the approximate width and height of a given number of items.
3462 * [I] infoPtr : valid pointer to the listview structure
3463 * [I] INT : number of items
3468 * Returns a DWORD. The width in the low word and the height in high word.
3470 static LRESULT
LISTVIEW_ApproximateViewRect(LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
3471 WORD wWidth
, WORD wHeight
)
3473 UINT uView
= LISTVIEW_GetType(infoPtr
);
3474 INT nItemCountPerColumn
= 1;
3475 INT nColumnCount
= 0;
3476 DWORD dwViewRect
= 0;
3478 if (nItemCount
== -1)
3479 nItemCount
= GETITEMCOUNT(infoPtr
);
3481 if (uView
== LVS_LIST
)
3483 if (wHeight
== 0xFFFF)
3485 /* use current height */
3486 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
3489 if (wHeight
< infoPtr
->nItemHeight
)
3490 wHeight
= infoPtr
->nItemHeight
;
3494 if (infoPtr
->nItemHeight
> 0)
3496 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
3497 if (nItemCountPerColumn
== 0)
3498 nItemCountPerColumn
= 1;
3500 if (nItemCount
% nItemCountPerColumn
!= 0)
3501 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
3503 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
3507 /* Microsoft padding magic */
3508 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
3509 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
3511 dwViewRect
= MAKELONG(wWidth
, wHeight
);
3513 else if (uView
== LVS_REPORT
)
3514 FIXME("uView == LVS_REPORT: not implemented\n");
3515 else if (uView
== LVS_SMALLICON
)
3516 FIXME("uView == LVS_SMALLICON: not implemented\n");
3517 else if (uView
== LVS_ICON
)
3518 FIXME("uView == LVS_ICON: not implemented\n");
3525 * Arranges listview items in icon display mode.
3528 * [I] infoPtr : valid pointer to the listview structure
3529 * [I] INT : alignment code
3535 static LRESULT
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
3537 UINT uView
= LISTVIEW_GetType(infoPtr
);
3538 BOOL bResult
= FALSE
;
3540 if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
3545 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3548 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3551 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3553 case LVA_SNAPTOGRID
:
3554 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3562 /* << LISTVIEW_CreateDragImage >> */
3567 * Removes all listview items and subitems.
3570 * [I] infoPtr : valid pointer to the listview structure
3576 static LRESULT
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
)
3578 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
3579 UINT uView
= lStyle
& LVS_TYPEMASK
;
3580 LISTVIEW_ITEM
*lpItem
;
3581 LISTVIEW_SUBITEM
*lpSubItem
;
3584 BOOL bResult
= FALSE
;
3589 LISTVIEW_RemoveAllSelections(infoPtr
);
3590 infoPtr
->nSelectionMark
=-1;
3591 infoPtr
->nFocusedItem
=-1;
3592 /* But we are supposed to leave nHotItem as is! */
3594 if (lStyle
& LVS_OWNERDATA
)
3596 infoPtr
->hdpaItems
->nItemCount
= 0;
3597 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
3601 if (GETITEMCOUNT(infoPtr
) > 0)
3605 /* send LVN_DELETEALLITEMS notification */
3606 /* verify if subsequent LVN_DELETEITEM notifications should be
3608 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
3610 bSuppress
= listview_notify(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
3612 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
3614 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
3615 if (hdpaSubItems
!= NULL
)
3617 for (j
= 1; j
< hdpaSubItems
->nItemCount
; j
++)
3619 lpSubItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, j
);
3620 if (lpSubItem
!= NULL
)
3622 /* free subitem string */
3623 if (is_textW(lpSubItem
->hdr
.pszText
))
3624 COMCTL32_Free(lpSubItem
->hdr
.pszText
);
3627 COMCTL32_Free(lpSubItem
);
3631 lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0);
3636 /* send LVN_DELETEITEM notification */
3638 nmlv
.lParam
= lpItem
->lParam
;
3639 listview_notify(infoPtr
, LVN_DELETEITEM
, &nmlv
);
3642 /* free item string */
3643 if (is_textW(lpItem
->hdr
.pszText
))
3644 COMCTL32_Free(lpItem
->hdr
.pszText
);
3647 COMCTL32_Free(lpItem
);
3650 DPA_Destroy(hdpaSubItems
);
3654 /* reinitialize listview memory */
3655 bResult
= DPA_DeleteAllPtrs(infoPtr
->hdpaItems
);
3657 /* align items (set position of each item) */
3658 if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
3660 if (lStyle
& LVS_ALIGNLEFT
)
3662 LISTVIEW_AlignLeft(infoPtr
);
3666 LISTVIEW_AlignTop(infoPtr
);
3670 LISTVIEW_UpdateScroll(infoPtr
);
3672 /* invalidate client area (optimization needed) */
3673 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
3681 * Removes a column from the listview control.
3684 * [I] infoPtr : valid pointer to the listview structure
3685 * [I] INT : column index
3691 static LRESULT
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
3693 UINT uView
= LISTVIEW_GetType(infoPtr
);
3694 UINT uOwnerData
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & LVS_OWNERDATA
;
3695 BOOL bResult
= FALSE
;
3697 if (Header_DeleteItem(infoPtr
->hwndHeader
, nColumn
))
3699 bResult
= uOwnerData
? TRUE
: LISTVIEW_RemoveColumn(infoPtr
->hdpaItems
, nColumn
);
3701 /* Need to reset the item width when deleting a column */
3702 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(infoPtr
);
3704 /* reset scroll parameters */
3705 if (uView
== LVS_REPORT
)
3707 /* update scrollbar(s) */
3708 LISTVIEW_UpdateScroll(infoPtr
);
3710 /* refresh client area */
3711 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
3720 * Removes an item from the listview control.
3723 * [I] infoPtr : valid pointer to the listview structure
3724 * [I] INT : item index
3730 static LRESULT
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3732 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
3733 UINT uView
= lStyle
& LVS_TYPEMASK
;
3734 LONG lCtrlId
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_ID
);
3736 BOOL bResult
= FALSE
;
3738 LISTVIEW_ITEM
*lpItem
;
3739 LISTVIEW_SUBITEM
*lpSubItem
;
3743 TRACE("(nItem=%d)\n", nItem
);
3746 /* First, send LVN_DELETEITEM notification. */
3747 memset(&nmlv
, 0, sizeof (NMLISTVIEW
));
3748 nmlv
.hdr
.hwndFrom
= infoPtr
->hwndSelf
;
3749 nmlv
.hdr
.idFrom
= lCtrlId
;
3750 nmlv
.hdr
.code
= LVN_DELETEITEM
;
3752 SendMessageW(GetParent(infoPtr
->hwndSelf
), WM_NOTIFY
, (WPARAM
)lCtrlId
,
3756 /* remove it from the selection range */
3757 ZeroMemory(&item
,sizeof(item
));
3758 item
.stateMask
= LVIS_SELECTED
;
3759 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
3761 if (lStyle
& LVS_OWNERDATA
)
3763 infoPtr
->hdpaItems
->nItemCount
--;
3764 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
3768 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
3770 /* initialize memory */
3771 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
3773 hdpaSubItems
= (HDPA
)DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
3774 if (hdpaSubItems
!= NULL
)
3776 for (i
= 1; i
< hdpaSubItems
->nItemCount
; i
++)
3778 lpSubItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, i
);
3779 if (lpSubItem
!= NULL
)
3781 /* free item string */
3782 if (is_textW(lpSubItem
->hdr
.pszText
))
3783 COMCTL32_Free(lpSubItem
->hdr
.pszText
);
3786 COMCTL32_Free(lpSubItem
);
3790 lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0);
3793 /* free item string */
3794 if (is_textW(lpItem
->hdr
.pszText
))
3795 COMCTL32_Free(lpItem
->hdr
.pszText
);
3798 COMCTL32_Free(lpItem
);
3801 bResult
= DPA_Destroy(hdpaSubItems
);
3804 LISTVIEW_ShiftIndices(infoPtr
,nItem
,-1);
3806 /* align items (set position of each item) */
3807 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
3809 if (lStyle
& LVS_ALIGNLEFT
)
3810 LISTVIEW_AlignLeft(infoPtr
);
3812 LISTVIEW_AlignTop(infoPtr
);
3815 LISTVIEW_UpdateScroll(infoPtr
);
3817 /* refresh client area */
3818 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
3827 * Callback implementation for editlabel control
3830 * [I] infoPtr : valid pointer to the listview structure
3831 * [I] pszText : modified text
3832 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
3838 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, LPWSTR pszText
, BOOL isW
)
3840 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
3841 NMLVDISPINFOW dispInfo
;
3842 LISTVIEW_ITEM
*lpItem
;
3844 LISTVIEW_ITEM lvItemRef
;
3846 BOOL bResult
= TRUE
;
3847 INT nItem
= infoPtr
->nEditLabelItem
;
3849 TRACE("(pszText=%s, nItem=%d, isW=%d)\n", debugtext_t(pszText
, isW
), nItem
, isW
);
3851 infoPtr
->bEditing
= FALSE
;
3852 if (!(lStyle
& LVS_OWNERDATA
))
3854 if (!(hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
)))
3857 if (!(lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0)))
3862 ZeroMemory(&lvItemRef
,sizeof(LISTVIEW_ITEM
));
3863 ZeroMemory(&item
,sizeof(item
));
3866 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
3867 ListView_GetItemW(infoPtr
->hwndSelf
, &item
);
3868 lvItemRef
.hdr
.iImage
= item
.iImage
;
3869 lvItemRef
.state
= item
.state
;
3870 lvItemRef
.lParam
= item
.lParam
;
3871 lpItem
= &lvItemRef
;
3874 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
3875 dispInfo
.item
.mask
= 0;
3876 dispInfo
.item
.iItem
= nItem
;
3877 dispInfo
.item
.state
= lpItem
->state
;
3878 dispInfo
.item
.stateMask
= 0;
3879 dispInfo
.item
.pszText
= pszText
;
3880 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
3881 dispInfo
.item
.iImage
= lpItem
->hdr
.iImage
;
3882 dispInfo
.item
.lParam
= lpItem
->lParam
;
3884 /* Do we need to update the Item Text */
3885 if(dispinfo_notifyT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
))
3886 if (lpItem
->hdr
.pszText
!= LPSTR_TEXTCALLBACKW
&& !(lStyle
& LVS_OWNERDATA
))
3887 bResult
= textsetptrT(&lpItem
->hdr
.pszText
, pszText
, isW
);
3894 * Begin in place editing of specified list view item
3897 * [I] infoPtr : valid pointer to the listview structure
3898 * [I] INT : item index
3899 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
3905 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
3907 NMLVDISPINFOW dispInfo
;
3909 LISTVIEW_ITEM
*lpItem
;
3912 WCHAR szDispText
[DISP_TEXT_SIZE
];
3914 LISTVIEW_ITEM lvItemRef
;
3915 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
3917 if (~GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & LVS_EDITLABELS
)
3920 infoPtr
->nEditLabelItem
= nItem
;
3922 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
3924 /* Is the EditBox still there, if so remove it */
3925 if(infoPtr
->hwndEdit
!= 0)
3927 SetFocus(infoPtr
->hwndSelf
);
3928 infoPtr
->hwndEdit
= 0;
3931 LISTVIEW_SetSelection(infoPtr
, nItem
);
3932 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3934 if (!(lStyle
& LVS_OWNERDATA
))
3936 if (NULL
== (hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
)))
3939 if (NULL
== (lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0)))
3945 ZeroMemory(&lvItemRef
,sizeof(LISTVIEW_ITEM
));
3946 ZeroMemory(&item
, sizeof(item
));
3949 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
3950 ListView_GetItemW(infoPtr
->hwndSelf
, &item
);
3951 lvItemRef
.hdr
.iImage
= item
.iImage
;
3952 lvItemRef
.state
= item
.state
;
3953 lvItemRef
.lParam
= item
.lParam
;
3954 lpItem
= &lvItemRef
;
3957 /* get information needed for drawing the item */
3958 ZeroMemory(&lvItem
, sizeof(lvItem
));
3959 lvItem
.mask
= LVIF_TEXT
;
3960 lvItem
.iItem
= nItem
;
3961 lvItem
.iSubItem
= 0;
3962 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3963 lvItem
.pszText
= szDispText
;
3964 *lvItem
.pszText
= '\0';
3965 LISTVIEW_GetItemT(infoPtr
, &lvItem
, FALSE
, isW
);
3967 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
3968 dispInfo
.item
.mask
= 0;
3969 dispInfo
.item
.iItem
= nItem
;
3970 dispInfo
.item
.state
= lpItem
->state
;
3971 dispInfo
.item
.stateMask
= 0;
3972 dispInfo
.item
.pszText
= lvItem
.pszText
;
3973 dispInfo
.item
.cchTextMax
= lstrlenW(lvItem
.pszText
);
3974 dispInfo
.item
.iImage
= lpItem
->hdr
.iImage
;
3975 dispInfo
.item
.lParam
= lpItem
->lParam
;
3977 if (dispinfo_notifyT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
3980 rect
.left
= LVIR_LABEL
;
3981 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
))
3984 if (!(hedit
= CreateEditLabelT(infoPtr
, szDispText
, WS_VISIBLE
,
3985 rect
.left
-2, rect
.top
-1, 0, rect
.bottom
- rect
.top
+2, isW
)))
3988 infoPtr
->hwndEdit
= hedit
;
3990 ShowWindow(infoPtr
->hwndEdit
,SW_NORMAL
);
3991 infoPtr
->bEditing
= TRUE
;
3992 SetFocus(infoPtr
->hwndEdit
);
3993 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
3994 return infoPtr
->hwndEdit
;
4000 * Ensures the specified item is visible, scrolling into view if necessary.
4003 * [I] infoPtr : valid pointer to the listview structure
4004 * [I] nItem : item index
4005 * [I] bPartial : partially or entirely visible
4011 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
4013 UINT uView
= LISTVIEW_GetType(infoPtr
);
4014 INT nScrollPosHeight
= 0;
4015 INT nScrollPosWidth
= 0;
4016 SCROLLINFO scrollInfo
;
4018 BOOL bRedraw
= FALSE
;
4020 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
4021 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
4022 scrollInfo
.fMask
= SIF_POS
;
4024 /* ALWAYS bPartial == FALSE, FOR NOW! */
4026 rcItem
.left
= LVIR_BOUNDS
;
4027 if (LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
))
4029 if (rcItem
.left
< infoPtr
->rcList
.left
)
4031 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
4035 if (uView
== LVS_LIST
)
4037 nScrollPosWidth
= infoPtr
->nItemWidth
;
4038 rcItem
.left
+= infoPtr
->rcList
.left
;
4040 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
4042 nScrollPosWidth
= 1;
4043 rcItem
.left
+= infoPtr
->rcList
.left
;
4046 /* When in LVS_REPORT view, the scroll position should
4048 if (nScrollPosWidth
!= 0)
4050 if (rcItem
.left
% nScrollPosWidth
== 0)
4051 scrollInfo
.nPos
+= rcItem
.left
/ nScrollPosWidth
;
4053 scrollInfo
.nPos
+= rcItem
.left
/ nScrollPosWidth
- 1;
4055 SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
4059 else if (rcItem
.right
> infoPtr
->rcList
.right
)
4061 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
4065 if (uView
== LVS_LIST
)
4067 rcItem
.right
-= infoPtr
->rcList
.right
;
4068 nScrollPosWidth
= infoPtr
->nItemWidth
;
4070 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
4072 rcItem
.right
-= infoPtr
->rcList
.right
;
4073 nScrollPosWidth
= 1;
4076 /* When in LVS_REPORT view, the scroll position should
4078 if (nScrollPosWidth
!= 0)
4080 if (rcItem
.right
% nScrollPosWidth
== 0)
4081 scrollInfo
.nPos
+= rcItem
.right
/ nScrollPosWidth
;
4083 scrollInfo
.nPos
+= rcItem
.right
/ nScrollPosWidth
+ 1;
4085 SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
4090 if (rcItem
.top
< infoPtr
->rcList
.top
)
4094 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
4096 if (uView
== LVS_REPORT
)
4098 rcItem
.top
-= infoPtr
->rcList
.top
;
4099 nScrollPosHeight
= infoPtr
->nItemHeight
;
4101 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
4103 nScrollPosHeight
= 1;
4104 rcItem
.top
+= infoPtr
->rcList
.top
;
4107 if (rcItem
.top
% nScrollPosHeight
== 0)
4108 scrollInfo
.nPos
+= rcItem
.top
/ nScrollPosHeight
;
4110 scrollInfo
.nPos
+= rcItem
.top
/ nScrollPosHeight
- 1;
4112 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
4115 else if (rcItem
.bottom
> infoPtr
->rcList
.bottom
)
4119 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
4121 if (uView
== LVS_REPORT
)
4123 rcItem
.bottom
-= infoPtr
->rcList
.bottom
;
4124 nScrollPosHeight
= infoPtr
->nItemHeight
;
4126 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
4128 nScrollPosHeight
= 1;
4129 rcItem
.bottom
-= infoPtr
->rcList
.bottom
;
4132 if (rcItem
.bottom
% nScrollPosHeight
== 0)
4133 scrollInfo
.nPos
+= rcItem
.bottom
/ nScrollPosHeight
;
4135 scrollInfo
.nPos
+= rcItem
.bottom
/ nScrollPosHeight
+ 1;
4137 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
4143 InvalidateRect(infoPtr
->hwndSelf
,NULL
,TRUE
);
4149 * Retrieves the nearest item, given a position and a direction.
4152 * [I] infoPtr : valid pointer to the listview structure
4153 * [I] POINT : start position
4154 * [I] UINT : direction
4157 * Item index if successdful, -1 otherwise.
4159 static INT
LISTVIEW_GetNearestItem(LISTVIEW_INFO
*infoPtr
, POINT pt
, UINT vkDirection
)
4165 TRACE("point %ld,%ld, direction %s\n", pt
.x
, pt
.y
,
4166 (vkDirection
== VK_DOWN
) ? "VK_DOWN" :
4167 ((vkDirection
== VK_UP
) ? "VK_UP" :
4168 ((vkDirection
== VK_LEFT
) ? "VK_LEFT" : "VK_RIGHT")));
4170 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
))
4172 ZeroMemory(&lvIntHit
, sizeof(lvIntHit
));
4173 LISTVIEW_GetOrigin(infoPtr
, &lvIntHit
.ht
.pt
);
4174 lvIntHit
.ht
.pt
.x
+= pt
.x
;
4175 lvIntHit
.ht
.pt
.y
+= pt
.y
;
4177 if (vkDirection
== VK_DOWN
)
4178 lvIntHit
.ht
.pt
.y
+= infoPtr
->nItemHeight
;
4179 else if (vkDirection
== VK_UP
)
4180 lvIntHit
.ht
.pt
.y
-= infoPtr
->nItemHeight
;
4181 else if (vkDirection
== VK_LEFT
)
4182 lvIntHit
.ht
.pt
.x
-= infoPtr
->nItemWidth
;
4183 else if (vkDirection
== VK_RIGHT
)
4184 lvIntHit
.ht
.pt
.x
+= infoPtr
->nItemWidth
;
4186 if (!PtInRect(&rcView
, lvIntHit
.ht
.pt
))
4190 nItem
= LISTVIEW_SuperHitTestItem(infoPtr
, &lvIntHit
, TRUE
);
4191 return nItem
== -1 ? lvIntHit
.iDistItem
: nItem
;
4200 * Searches for an item with specific characteristics.
4203 * [I] hwnd : window handle
4204 * [I] nStart : base item index
4205 * [I] lpFindInfo : item information to look for
4208 * SUCCESS : index of item
4211 static LRESULT
LISTVIEW_FindItemW(LISTVIEW_INFO
*infoPtr
, INT nStart
,
4212 LPLVFINDINFOW lpFindInfo
)
4215 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
4219 INT nLast
= GETITEMCOUNT(infoPtr
);
4221 if ((nItem
>= -1) && (lpFindInfo
!= NULL
))
4223 ZeroMemory(&lvItem
, sizeof(lvItem
));
4225 if (lpFindInfo
->flags
& LVFI_PARAM
)
4227 lvItem
.mask
|= LVIF_PARAM
;
4230 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
))
4232 lvItem
.mask
|= LVIF_TEXT
;
4233 lvItem
.pszText
= szDispText
;
4234 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4237 if (lpFindInfo
->flags
& LVFI_WRAP
)
4240 if (lpFindInfo
->flags
& LVFI_NEARESTXY
)
4242 ptItem
.x
= lpFindInfo
->pt
.x
;
4243 ptItem
.y
= lpFindInfo
->pt
.y
;
4248 while (nItem
< nLast
)
4250 if (lpFindInfo
->flags
& LVFI_NEARESTXY
)
4252 nItem
= LISTVIEW_GetNearestItem(infoPtr
, ptItem
,
4253 lpFindInfo
->vkDirection
);
4256 /* get position of the new item index */
4257 if (!ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &ptItem
))
4268 lvItem
.iItem
= nItem
;
4269 lvItem
.iSubItem
= 0;
4270 if (LISTVIEW_GetItemW(infoPtr
, &lvItem
, TRUE
))
4272 if (lvItem
.mask
& LVIF_TEXT
)
4274 if (lpFindInfo
->flags
& LVFI_PARTIAL
)
4276 if (strstrW(lvItem
.pszText
, lpFindInfo
->psz
) == NULL
)
4281 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0)
4286 if (lvItem
.mask
& LVIF_PARAM
)
4288 if (lpFindInfo
->lParam
!= lvItem
.lParam
)
4314 * Searches for an item with specific characteristics.
4317 * [I] hwnd : window handle
4318 * [I] nStart : base item index
4319 * [I] lpFindInfo : item information to look for
4322 * SUCCESS : index of item
4325 static LRESULT
LISTVIEW_FindItemA(LISTVIEW_INFO
*infoPtr
, INT nStart
,
4326 LPLVFINDINFOA lpFindInfo
)
4328 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
);
4332 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
4333 if (hasText
) fiw
.psz
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
4334 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
4335 if (hasText
) textfreeT((LPWSTR
)fiw
.psz
, FALSE
);
4341 * Retrieves the background image of the listview control.
4344 * [I] infoPtr : valid pointer to the listview structure
4345 * [O] LPLVMKBIMAGE : background image attributes
4351 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4353 /* FIXME (listview, "empty stub!\n"); */
4359 * Retrieves column attributes.
4362 * [I] infoPtr : valid pointer to the listview structure
4363 * [I] INT : column index
4364 * [IO] LPLVCOLUMNW : column information
4365 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4366 * otherwise it is in fact a LPLVCOLUMNA
4372 static LRESULT
LISTVIEW_GetColumnT(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVCOLUMNW lpColumn
, BOOL isW
)
4375 BOOL bResult
= FALSE
;
4377 if (lpColumn
!= NULL
)
4380 /* initialize memory */
4381 ZeroMemory(&hdi
, sizeof(hdi
));
4383 if (lpColumn
->mask
& LVCF_FMT
)
4384 hdi
.mask
|= HDI_FORMAT
;
4386 if (lpColumn
->mask
& LVCF_WIDTH
)
4387 hdi
.mask
|= HDI_WIDTH
;
4389 if (lpColumn
->mask
& LVCF_TEXT
)
4391 hdi
.mask
|= HDI_TEXT
;
4392 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
4393 hdi
.pszText
= lpColumn
->pszText
;
4396 if (lpColumn
->mask
& LVCF_IMAGE
)
4397 hdi
.mask
|= HDI_IMAGE
;
4399 if (lpColumn
->mask
& LVCF_ORDER
)
4400 hdi
.mask
|= HDI_ORDER
;
4403 bResult
= Header_GetItemW(infoPtr
->hwndHeader
, nItem
, &hdi
);
4405 bResult
= Header_GetItemA(infoPtr
->hwndHeader
, nItem
, &hdi
);
4409 if (lpColumn
->mask
& LVCF_FMT
)
4413 if (hdi
.fmt
& HDF_LEFT
)
4414 lpColumn
->fmt
|= LVCFMT_LEFT
;
4415 else if (hdi
.fmt
& HDF_RIGHT
)
4416 lpColumn
->fmt
|= LVCFMT_RIGHT
;
4417 else if (hdi
.fmt
& HDF_CENTER
)
4418 lpColumn
->fmt
|= LVCFMT_CENTER
;
4420 if (hdi
.fmt
& HDF_IMAGE
)
4421 lpColumn
->fmt
|= LVCFMT_COL_HAS_IMAGES
;
4423 if (hdi
.fmt
& HDF_BITMAP_ON_RIGHT
)
4424 lpColumn
->fmt
|= LVCFMT_BITMAP_ON_RIGHT
;
4427 if (lpColumn
->mask
& LVCF_WIDTH
)
4428 lpColumn
->cx
= hdi
.cxy
;
4430 if (lpColumn
->mask
& LVCF_IMAGE
)
4431 lpColumn
->iImage
= hdi
.iImage
;
4433 if (lpColumn
->mask
& LVCF_ORDER
)
4434 lpColumn
->iOrder
= hdi
.iOrder
;
4436 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4437 nItem
, debuglvcolumn_t(lpColumn
, isW
), isW
);
4446 static LRESULT
LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
4453 /* FIXME: little hack */
4454 for (i
= 0; i
< iCount
; i
++)
4462 * Retrieves the column width.
4465 * [I] infoPtr : valid pointer to the listview structure
4466 * [I] int : column index
4469 * SUCCESS : column width
4472 static LRESULT
LISTVIEW_GetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4474 UINT uView
= LISTVIEW_GetType(infoPtr
);
4475 INT nColumnWidth
= 0;
4478 if (uView
== LVS_LIST
)
4480 nColumnWidth
= infoPtr
->nItemWidth
;
4482 else if (uView
== LVS_REPORT
)
4484 /* get column width from header */
4485 ZeroMemory(&hdi
, sizeof(hdi
));
4486 hdi
.mask
= HDI_WIDTH
;
4487 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
))
4488 nColumnWidth
= hdi
.cxy
;
4491 return nColumnWidth
;
4496 * In list or report display mode, retrieves the number of items that can fit
4497 * vertically in the visible area. In icon or small icon display mode,
4498 * retrieves the total number of visible items.
4501 * [I] infoPtr : valid pointer to the listview structure
4504 * Number of fully visible items.
4506 static LRESULT
LISTVIEW_GetCountPerPage(LISTVIEW_INFO
*infoPtr
)
4508 UINT uView
= LISTVIEW_GetType(infoPtr
);
4511 if (uView
== LVS_LIST
)
4513 if (infoPtr
->rcList
.right
> infoPtr
->nItemWidth
)
4515 nItemCount
= LISTVIEW_GetCountPerRow(infoPtr
) *
4516 LISTVIEW_GetCountPerColumn(infoPtr
);
4519 else if (uView
== LVS_REPORT
)
4521 nItemCount
= LISTVIEW_GetCountPerColumn(infoPtr
);
4525 nItemCount
= GETITEMCOUNT(infoPtr
);
4534 * Retrieves an image list handle.
4537 * [I] infoPtr : valid pointer to the listview structure
4538 * [I] INT : image list identifier
4541 * SUCCESS : image list handle
4544 static LRESULT
LISTVIEW_GetImageList(LISTVIEW_INFO
*infoPtr
, INT nImageList
)
4546 HIMAGELIST himl
= NULL
;
4551 himl
= infoPtr
->himlNormal
;
4554 himl
= infoPtr
->himlSmall
;
4557 himl
= infoPtr
->himlState
;
4561 return (LRESULT
)himl
;
4564 /* LISTVIEW_GetISearchString */
4567 * Helper function for LISTVIEW_GetItemT *only*. Tests if an item is selected.
4568 * It is important that no other functions call this because of callbacks.
4570 static inline BOOL
is_item_selected(LISTVIEW_INFO
*infoPtr
, INT nItem
)
4572 LISTVIEW_SELECTION selection
= { nItem
, nItem
};
4574 return DPA_Search(infoPtr
->hdpaSelectionRanges
, &selection
, 0,
4575 LISTVIEW_CompareSelectionRanges
, 0, DPAS_SORTED
) != -1;
4580 * Retrieves item attributes.
4583 * [I] hwnd : window handle
4584 * [IO] lpLVItem : item info
4585 * [I] internal : if true then we will use tricks that avoid copies
4586 * but are not compatible with the regular interface
4587 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4588 * if FALSE, the lpLVItem is a LPLVITEMA.
4594 static LRESULT
LISTVIEW_GetItemT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL internal
, BOOL isW
)
4596 NMLVDISPINFOW dispInfo
;
4597 LISTVIEW_ITEM
*lpItem
;
4601 if (internal
&& !isW
)
4603 ERR("We can't have internal non-Unicode GetItem!\n");
4607 /* In the following:
4608 * lpLVItem describes the information requested by the user
4609 * lpItem is what we have
4610 * dispInfo is a structure we use to request the missing
4611 * information from the application
4614 TRACE("(lpLVItem=%s, internal=%d, isW=%d)\n",
4615 debuglvitem_t(lpLVItem
, isW
), internal
, isW
);
4617 if (!lpLVItem
|| (lpLVItem
->iItem
< 0) ||
4618 (lpLVItem
->iItem
>= GETITEMCOUNT(infoPtr
)))
4621 /* a quick optimization if all we're asked is the focus state
4622 * these queries are worth optimising since they are common,
4623 * and can be answered in constant time, without the heavy accesses */
4624 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIF_STATE
) &&
4625 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
4627 lpLVItem
->state
= 0;
4628 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
4629 lpLVItem
->state
|= LVIS_FOCUSED
;
4633 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4635 /* if the app stores all the data, handle it separately */
4636 if (GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & LVS_OWNERDATA
)
4638 dispInfo
.item
.state
= 0;
4640 /* if we need to callback, do it now */
4641 if ((lpLVItem
->mask
& ~LVIF_STATE
) || infoPtr
->uCallbackMask
)
4643 memcpy(&dispInfo
.item
, lpLVItem
, sizeof(LVITEMW
));
4644 dispInfo
.item
.stateMask
&= infoPtr
->uCallbackMask
;
4645 dispinfo_notifyT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
4646 memcpy(lpLVItem
, &dispInfo
.item
, sizeof(LVITEMW
));
4647 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
4650 /* we store only a little state, so if we're not asked, we're done */
4651 if (!(lpLVItem
->mask
& LVIF_STATE
) || lpLVItem
->iSubItem
) return FALSE
;
4653 /* if focus is handled by us, report it */
4654 if ( !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
4656 lpLVItem
->state
&= ~LVIS_FOCUSED
;
4657 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
4658 lpLVItem
->state
|= LVIS_FOCUSED
;
4661 /* and do the same for selection, if we handle it */
4662 if ( !(infoPtr
->uCallbackMask
& LVIS_SELECTED
) )
4664 lpLVItem
->state
&= ~LVIS_SELECTED
;
4665 if ((lpLVItem
->stateMask
& LVIS_SELECTED
) &&
4666 is_item_selected(infoPtr
, lpLVItem
->iItem
))
4667 lpLVItem
->state
|= LVIS_SELECTED
;
4673 /* find the item and subitem structures before we proceed */
4674 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
4675 if (hdpaSubItems
== NULL
) return FALSE
;
4677 if ( !(lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0)) )
4680 if (lpLVItem
->iSubItem
)
4682 LISTVIEW_SUBITEM
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
4683 if(!lpSubItem
) return FALSE
;
4684 pItemHdr
= &lpSubItem
->hdr
;
4687 pItemHdr
= &lpItem
->hdr
;
4689 /* Do we need to query the state from the app? */
4690 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& lpLVItem
->iSubItem
== 0)
4692 dispInfo
.item
.mask
|= LVIF_STATE
;
4693 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
4696 /* Do we need to enquire about the image? */
4697 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (pItemHdr
->iImage
==I_IMAGECALLBACK
))
4698 dispInfo
.item
.mask
|= LVIF_IMAGE
;
4700 /* Do we need to enquire about the text? */
4701 if ((lpLVItem
->mask
& LVIF_TEXT
) && !is_textW(pItemHdr
->pszText
))
4703 dispInfo
.item
.mask
|= LVIF_TEXT
;
4704 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
4705 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
4706 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
4707 *dispInfo
.item
.pszText
= '\0';
4710 /* If we don't have all the requested info, query the application */
4711 if (dispInfo
.item
.mask
!= 0)
4713 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
4714 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
;
4715 dispInfo
.item
.lParam
= lpItem
->lParam
;
4716 dispinfo_notifyT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
4717 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
4720 /* Now, handle the iImage field */
4721 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
4723 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
4724 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && (pItemHdr
->iImage
==I_IMAGECALLBACK
))
4725 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
4727 else if (lpLVItem
->mask
& LVIF_IMAGE
)
4728 lpLVItem
->iImage
= pItemHdr
->iImage
;
4730 /* The pszText field */
4731 if (dispInfo
.item
.mask
& LVIF_TEXT
)
4733 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
4734 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
4736 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
4737 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
4738 if (lpLVItem
->pszText
!= dispInfo
.item
.pszText
)
4739 textcpynT(lpLVItem
->pszText
, isW
, dispInfo
.item
.pszText
, isW
, lpLVItem
->cchTextMax
);
4741 else if (lpLVItem
->mask
& LVIF_TEXT
)
4743 if (internal
) lpLVItem
->pszText
= pItemHdr
->pszText
;
4744 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
4747 /* if this is a subitem, we're done*/
4748 if (lpLVItem
->iSubItem
) return TRUE
;
4750 /* Next is the lParam field */
4751 if (dispInfo
.item
.mask
& LVIF_PARAM
)
4753 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
4754 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
4755 lpItem
->lParam
= dispInfo
.item
.lParam
;
4757 else if (lpLVItem
->mask
& LVIF_PARAM
)
4758 lpLVItem
->lParam
= lpItem
->lParam
;
4760 /* ... the state field (this one is different due to uCallbackmask) */
4761 if (lpLVItem
->mask
& LVIF_STATE
)
4763 lpLVItem
->state
= lpItem
->state
;
4764 if (dispInfo
.item
.mask
& LVIF_STATE
)
4766 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
4767 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
4769 if ( !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
4771 lpLVItem
->state
&= ~LVIS_FOCUSED
;
4772 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
4773 lpLVItem
->state
|= LVIS_FOCUSED
;
4775 if ( !(infoPtr
->uCallbackMask
& LVIS_SELECTED
) )
4777 lpLVItem
->state
&= ~LVIS_SELECTED
;
4778 if ((lpLVItem
->stateMask
& LVIS_SELECTED
) &&
4779 is_item_selected(infoPtr
, lpLVItem
->iItem
))
4780 lpLVItem
->state
|= LVIS_SELECTED
;
4784 /* and last, but not least, the indent field */
4785 if (lpLVItem
->mask
& LVIF_INDENT
)
4786 lpLVItem
->iIndent
= lpItem
->iIndent
;
4794 * Retrieves the rectangle enclosing the item icon and text.
4797 * [I] infoPtr : valid pointer to the listview structure
4798 * [I] INT : item index
4799 * [O] LPRECT : coordinate information
4805 static BOOL
LISTVIEW_GetItemBoundBox(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lpRect
)
4807 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
4808 UINT uView
= lStyle
& LVS_TYPEMASK
;
4809 BOOL bResult
= FALSE
;
4811 LISTVIEW_ITEM
*lpItem
;
4812 INT nCountPerColumn
;
4815 TRACE("(nItem=%d,lpRect=%p)\n", nItem
, lpRect
);
4817 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)) &&
4820 if (uView
== LVS_LIST
)
4823 nItem
= nItem
- ListView_GetTopIndex(infoPtr
->hwndSelf
);
4824 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
4827 nRow
= nItem
% nCountPerColumn
;
4830 lpRect
->left
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
4835 lpRect
->left
= (nItem
/ nCountPerColumn
-1) * infoPtr
->nItemWidth
;
4836 lpRect
->top
= (nRow
+ nCountPerColumn
) * infoPtr
->nItemHeight
;
4841 lpRect
->left
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
4842 lpRect
->top
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
4845 else if (uView
== LVS_REPORT
)
4848 lpRect
->left
= REPORT_MARGINX
;
4849 lpRect
->top
= ((nItem
- ListView_GetTopIndex(infoPtr
->hwndSelf
)) *
4850 infoPtr
->nItemHeight
) + infoPtr
->rcList
.top
;
4852 if (!(lStyle
& LVS_NOSCROLL
))
4854 SCROLLINFO scrollInfo
;
4855 /* Adjust position by scrollbar offset */
4856 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
4857 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
4858 scrollInfo
.fMask
= SIF_POS
;
4859 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
);
4860 lpRect
->left
-= scrollInfo
.nPos
;
4863 else /* either LVS_ICON or LVS_SMALLICON */
4865 if ((hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
)))
4867 if ((lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0)))
4870 lpRect
->left
= lpItem
->ptPosition
.x
;
4871 lpRect
->top
= lpItem
->ptPosition
.y
;
4876 lpRect
->right
= lpRect
->left
+ infoPtr
->nItemWidth
;
4877 lpRect
->bottom
= lpRect
->top
+ infoPtr
->nItemHeight
;
4878 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult
? "TRUE" : "FALSE",
4879 lpRect
->left
, lpRect
->top
, lpRect
->right
, lpRect
->bottom
);
4885 * Retrieves the position (upper-left) of the listview control item.
4886 * Note that for LVS_ICON style, the upper-left is that of the icon
4887 * and not the bounding box.
4890 * [I] infoPtr : valid pointer to the listview structure
4891 * [I] INT : item index
4892 * [O] LPPOINT : coordinate information
4898 static BOOL
LISTVIEW_GetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
4900 UINT uView
= LISTVIEW_GetType(infoPtr
);
4901 BOOL bResult
= FALSE
;
4904 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
4906 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)) &&
4907 (lpptPosition
!= NULL
))
4909 bResult
= LISTVIEW_GetItemBoundBox(infoPtr
, nItem
, &rcBounding
);
4910 lpptPosition
->x
= rcBounding
.left
;
4911 lpptPosition
->y
= rcBounding
.top
;
4912 if (uView
== LVS_ICON
)
4914 lpptPosition
->y
+= ICON_TOP_PADDING
;
4915 lpptPosition
->x
+= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2;
4917 TRACE("result %s (%ld,%ld)\n", bResult
? "TRUE" : "FALSE",
4918 lpptPosition
->x
, lpptPosition
->y
);
4924 * Adjust a text rectangle to an integral number of text lines.
4926 static void LISTVIEW_GetIntegralLines(
4927 const LISTVIEW_INFO
*infoPtr
,
4933 * We need to have the bottom to be an intergal number of
4934 * text lines (ntmHeight) below text top that is less than
4935 * or equal to the nItemHeight.
4937 i
= infoPtr
->nItemHeight
- infoPtr
->iconSize
.cy
-
4938 ICON_TOP_PADDING
- ICON_BOTTOM_PADDING
;
4939 j
= i
/ infoPtr
->ntmHeight
;
4940 k
= j
* infoPtr
->ntmHeight
;
4941 l
= rcText
->top
+ k
;
4942 rcText
->bottom
= min(rcText
->bottom
, l
);
4943 rcText
->bottom
+= 1;
4945 TRACE("integral lines, nitemH=%d, ntmH=%d, icon.cy=%ld, i=%d, j=%d, k=%d, rect=(%d,%d)-(%d,%d)\n",
4946 infoPtr
->nItemHeight
, infoPtr
->ntmHeight
, infoPtr
->iconSize
.cy
,
4948 rcText
->left
, rcText
->top
, rcText
->right
, rcText
->bottom
);
4953 * DESCRIPTION: [INTERNAL]
4954 * Update the bounding rectangle around the text under a large icon.
4955 * This depends on whether it has the focus or not.
4956 * On entry the rectangle's top, left and right should be set.
4957 * On return the bottom will also be set and the width may have been
4961 * [I] infoPtr : pointer to the listview structure
4962 * [I] nItem : the item for which we are calculating this
4963 * [I] rect : the rectangle to be updated
4965 * This appears to be weird, even in the Microsoft implementation.
4967 static void LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO
*infoPtr
, int nItem
, RECT
*rect
)
4969 HDC hdc
= GetDC (infoPtr
->hwndSelf
);
4970 HFONT hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
4971 UINT uFormat
= LISTVIEW_DTFLAGS
| DT_CALCRECT
;
4972 RECT rcText
= *rect
;
4973 RECT rcBack
= *rect
;
4974 BOOL focused
, selected
;
4975 int dx
, dy
, old_wid
, new_wid
;
4977 TRACE("%s, focus item=%d, cur item=%d\n",
4978 (infoPtr
->bFocus
) ? "Window has focus" : "Window not focused",
4979 infoPtr
->nFocusedItem
, nItem
);
4982 focused
= infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
);
4983 selected
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
4985 uFormat
|= (focused
) ? DT_NOCLIP
: DT_WORD_ELLIPSIS
| DT_END_ELLIPSIS
;
4987 if (focused
|| selected
)
4989 /* We (aim to) display the full text. In Windows 95 it appears to
4990 * calculate the size assuming the specified font and then it draws
4991 * the text in that region with the specified font except scaled to
4992 * 10 point (or the height of the system font or ...). Thus if the
4993 * window has 24 point Helvetica the highlit rectangle will be
4994 * taller than the text and if it is 7 point Helvetica then the text
4996 * For now we will simply say that it is the correct size to display
4997 * the text in the specified font.
5000 lvItem
.mask
= LVIF_TEXT
;
5001 lvItem
.iItem
= nItem
;
5002 lvItem
.iSubItem
= 0;
5003 /* We will specify INTERNAL and so will receive back a const
5004 * pointer to the text, rather than specifying a buffer to which
5007 LISTVIEW_GetItemW (infoPtr
, &lvItem
, TRUE
);
5009 InflateRect(&rcText
, -2, 0);
5010 DrawTextW (hdc
, lvItem
.pszText
, -1, &rcText
, uFormat
);
5011 /* Microsoft, in their great wisdom, have decided that the rectangle
5012 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
5013 * not the location. So we have to do the centring ourselves (and take
5014 * responsibility for agreeing off-by-one consistency with them).
5017 old_wid
= rcText
.right
- rcText
.left
;
5018 new_wid
= rcBack
.right
- rcBack
.left
;
5019 dx
= rcBack
.left
- rcText
.left
+ (new_wid
-old_wid
)/2;
5020 dy
= rcBack
.top
- rcText
.top
;
5021 OffsetRect (&rcText
, dx
, dy
);
5025 LISTVIEW_GetIntegralLines(infoPtr
, &rcText
);
5029 rcText
.bottom
+= LABEL_VERT_PADDING
- 2;
5032 rect
->bottom
= rcText
.bottom
;
5036 /* As far as I can see the text region seems to be trying to be
5037 * "tall enough for two lines of text". Once again (comctl32.dll ver
5038 * 5.81?) it measures this on the basis of the selected font and then
5039 * draws it with the same font except in 10 point size. This can lead
5040 * to more or less than the two rows appearing.
5041 * Question; are we supposed to be including DT_EXTERNALLEADING?
5042 * Question; should the width be shrunk to the space required to
5043 * display the two lines?
5045 LISTVIEW_GetIntegralLines(infoPtr
, &rcText
);
5046 rect
->bottom
= rcText
.bottom
;
5049 TRACE("%s and %s, bounding rect=(%d,%d)-(%d,%d)\n",
5050 (focused
) ? "focused(full text)" : "not focused",
5051 (selected
) ? "selected" : "not selected",
5052 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
5054 SelectObject (hdc
, hOldFont
);
5055 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
5060 * Retrieves the bounding rectangle for a listview control item.
5063 * [I] infoPtr : valid pointer to the listview structure
5064 * [I] INT : item index
5065 * [IO] LPRECT : 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 LRESULT
LISTVIEW_GetItemRect(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5121 UINT uView
= LISTVIEW_GetType(infoPtr
);
5122 BOOL bResult
= FALSE
;
5131 TRACE("(hwnd=%x, nItem=%d, lprc=%p, uview=%d)\n",
5132 infoPtr
->hwndSelf
, nItem
, lprc
, uView
);
5134 if (uView
& LVS_REPORT
)
5136 ZeroMemory(&lvItem
, sizeof(lvItem
));
5137 lvItem
.mask
= LVIF_INDENT
;
5138 lvItem
.iItem
= nItem
;
5139 lvItem
.iSubItem
= 0;
5140 LISTVIEW_GetItemW(infoPtr
, &lvItem
, TRUE
);
5143 if (lvItem
.iIndent
>0 && infoPtr
->iconSize
.cx
> 0)
5144 nIndent
= infoPtr
->iconSize
.cx
* lvItem
.iIndent
;
5151 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)) && (lprc
!= NULL
))
5156 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &ptItem
)) break;
5157 if (uView
== LVS_ICON
)
5159 if (infoPtr
->himlNormal
!= NULL
)
5161 if (LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
))
5164 lprc
->left
= ptItem
.x
+ ptOrigin
.x
- 8;
5165 lprc
->top
= ptItem
.y
+ ptOrigin
.y
- ICON_TOP_PADDING
;
5166 lprc
->right
= lprc
->left
+ infoPtr
->iconSize
.cx
+ 16;
5167 lprc
->bottom
= lprc
->top
+ infoPtr
->iconSize
.cy
+
5172 else if (uView
== LVS_SMALLICON
)
5174 if (LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
))
5177 lprc
->left
= ptItem
.x
+ ptOrigin
.x
;
5178 lprc
->top
= ptItem
.y
+ ptOrigin
.y
;
5179 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5181 if (infoPtr
->himlState
!= NULL
)
5182 lprc
->left
+= infoPtr
->iconSize
.cx
;
5184 if (infoPtr
->himlSmall
!= NULL
)
5185 lprc
->right
= lprc
->left
+ infoPtr
->iconSize
.cx
;
5187 lprc
->right
= lprc
->left
;
5193 lprc
->left
= ptItem
.x
;
5194 if (uView
& LVS_REPORT
)
5195 lprc
->left
+= nIndent
;
5196 lprc
->top
= ptItem
.y
;
5197 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5199 if (infoPtr
->himlState
!= NULL
)
5200 lprc
->left
+= infoPtr
->iconSize
.cx
;
5202 if (infoPtr
->himlSmall
!= NULL
)
5203 lprc
->right
= lprc
->left
+ infoPtr
->iconSize
.cx
;
5205 lprc
->right
= lprc
->left
;
5210 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &ptItem
)) break;
5211 if (uView
== LVS_ICON
)
5213 if (infoPtr
->himlNormal
!= NULL
)
5215 if (LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
))
5219 /* Correct ptItem to icon upper-left */
5220 ptItem
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
)/2;
5221 ptItem
.y
-= ICON_TOP_PADDING
;
5223 lprc
->left
= ptItem
.x
+ ptOrigin
.x
;
5224 lprc
->top
= ptItem
.y
+ ptOrigin
.y
+ infoPtr
->iconSize
.cy
+
5226 nLabelWidth
= LISTVIEW_GetLabelWidth(infoPtr
, nItem
);
5227 if (infoPtr
->iconSpacing
.cx
- nLabelWidth
> 1)
5229 lprc
->left
+= (infoPtr
->iconSpacing
.cx
- nLabelWidth
) / 2;
5230 lprc
->right
= lprc
->left
+ nLabelWidth
;
5231 lprc
->bottom
= lprc
->top
+ infoPtr
->ntmHeight
+ 1;
5232 InflateRect(lprc
, 2, 0);
5236 lprc
->right
= lprc
->left
+ infoPtr
->iconSpacing
.cx
- 1;
5237 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5238 LISTVIEW_UpdateLargeItemLabelRect (infoPtr
, nItem
, lprc
);
5240 lprc
->bottom
+= HEIGHT_PADDING
;
5244 else if (uView
== LVS_SMALLICON
)
5246 if (LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
))
5249 nLeftPos
= lprc
->left
= ptItem
.x
+ ptOrigin
.x
;
5250 lprc
->top
= ptItem
.y
+ ptOrigin
.y
;
5251 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5253 if (infoPtr
->himlState
!= NULL
)
5254 lprc
->left
+= infoPtr
->iconSize
.cx
;
5256 if (infoPtr
->himlSmall
!= NULL
)
5257 lprc
->left
+= infoPtr
->iconSize
.cx
;
5259 nLabelWidth
= LISTVIEW_GetLabelWidth(infoPtr
, nItem
);
5260 nLabelWidth
+= TRAILING_PADDING
;
5261 if (lprc
->left
+ nLabelWidth
< nLeftPos
+ infoPtr
->nItemWidth
)
5262 lprc
->right
= lprc
->left
+ nLabelWidth
;
5264 lprc
->right
= nLeftPos
+ infoPtr
->nItemWidth
;
5270 if (uView
== LVS_REPORT
)
5271 nLeftPos
= lprc
->left
= ptItem
.x
+ nIndent
;
5273 nLeftPos
= lprc
->left
= ptItem
.x
;
5274 lprc
->top
= ptItem
.y
;
5275 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5277 if (infoPtr
->himlState
!= NULL
)
5278 lprc
->left
+= infoPtr
->iconSize
.cx
;
5280 if (infoPtr
->himlSmall
!= NULL
)
5281 lprc
->left
+= infoPtr
->iconSize
.cx
;
5283 if (uView
!= LVS_REPORT
)
5285 nLabelWidth
= LISTVIEW_GetLabelWidth(infoPtr
, nItem
);
5286 nLabelWidth
+= TRAILING_PADDING
;
5287 if (infoPtr
->himlSmall
)
5288 nLabelWidth
+= IMAGE_PADDING
;
5291 nLabelWidth
= LISTVIEW_GetColumnWidth(infoPtr
, 0)-lprc
->left
;
5292 if (lprc
->left
+ nLabelWidth
< nLeftPos
+ infoPtr
->nItemWidth
)
5293 lprc
->right
= lprc
->left
+ nLabelWidth
;
5295 lprc
->right
= nLeftPos
+ infoPtr
->nItemWidth
;
5300 if (!LISTVIEW_GetItemBoundBox(infoPtr
, nItem
, &rcInternal
)) break;
5301 ptItem
.x
= rcInternal
.left
;
5302 ptItem
.y
= rcInternal
.top
;
5303 if (uView
== LVS_ICON
)
5305 if (infoPtr
->himlNormal
!= NULL
)
5307 if (LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
))
5309 RECT label_rect
, icon_rect
;
5311 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &ptItem
)) break;
5313 /* make icon rectangle */
5314 icon_rect
.left
= ptItem
.x
+ ptOrigin
.x
- 8;
5315 icon_rect
.top
= ptItem
.y
+ ptOrigin
.y
- ICON_TOP_PADDING
;
5316 icon_rect
.right
= icon_rect
.left
+ infoPtr
->iconSize
.cx
+ 16;
5317 icon_rect
.bottom
= icon_rect
.top
+ infoPtr
->iconSize
.cy
+
5320 /* make label rectangle */
5321 /* Correct ptItem to icon upper-left */
5322 ptItem
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
)/2;
5323 ptItem
.y
-= ICON_TOP_PADDING
;
5325 label_rect
.left
= ptItem
.x
+ ptOrigin
.x
;
5326 label_rect
.top
= ptItem
.y
+ ptOrigin
.y
+ infoPtr
->iconSize
.cy
+
5328 nLabelWidth
= LISTVIEW_GetLabelWidth(infoPtr
, nItem
);
5329 if (infoPtr
->iconSpacing
.cx
- nLabelWidth
> 1)
5331 label_rect
.left
+= (infoPtr
->iconSpacing
.cx
- nLabelWidth
) / 2;
5332 label_rect
.right
= label_rect
.left
+ nLabelWidth
;
5333 label_rect
.bottom
= label_rect
.top
+ infoPtr
->ntmHeight
+ 1;
5334 InflateRect(&label_rect
, 2, 0);
5338 label_rect
.right
= label_rect
.left
+ infoPtr
->iconSpacing
.cx
- 1;
5339 label_rect
.bottom
= label_rect
.top
+ infoPtr
->nItemHeight
;
5340 LISTVIEW_UpdateLargeItemLabelRect (infoPtr
, nItem
, &label_rect
);
5342 label_rect
.bottom
+= HEIGHT_PADDING
;
5344 UnionRect (lprc
, &icon_rect
, &label_rect
);
5348 else if (uView
== LVS_SMALLICON
)
5350 if (LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
))
5353 lprc
->left
= ptItem
.x
+ ptOrigin
.x
;
5354 lprc
->right
= lprc
->left
;
5355 lprc
->top
= ptItem
.y
+ ptOrigin
.y
;
5356 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5357 if (infoPtr
->himlState
!= NULL
)
5358 lprc
->right
+= infoPtr
->iconSize
.cx
;
5359 if (infoPtr
->himlSmall
!= NULL
)
5360 lprc
->right
+= infoPtr
->iconSize
.cx
;
5362 nLabelWidth
= LISTVIEW_GetLabelWidth(infoPtr
, nItem
);
5363 nLabelWidth
+= TRAILING_PADDING
;
5364 if (infoPtr
->himlSmall
)
5365 nLabelWidth
+= IMAGE_PADDING
;
5366 if (lprc
->right
+ nLabelWidth
< lprc
->left
+ infoPtr
->nItemWidth
)
5367 lprc
->right
+= nLabelWidth
;
5369 lprc
->right
= lprc
->left
+ infoPtr
->nItemWidth
;
5375 lprc
->left
= ptItem
.x
;
5376 if (!(infoPtr
->dwExStyle
&LVS_EX_FULLROWSELECT
) && uView
&LVS_REPORT
)
5377 lprc
->left
+= nIndent
;
5378 lprc
->right
= lprc
->left
;
5379 lprc
->top
= ptItem
.y
;
5380 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5382 if ((infoPtr
->dwExStyle
& LVS_EX_FULLROWSELECT
) || (uView
== LVS_REPORT
))
5385 int nColumnCount
= Header_GetItemCount(infoPtr
->hwndHeader
);
5386 Header_GetItemRect(infoPtr
->hwndHeader
, nColumnCount
-1, &br
);
5388 lprc
->right
= max(lprc
->left
, br
.right
- REPORT_MARGINX
);
5392 if (infoPtr
->himlState
!= NULL
)
5393 lprc
->right
+= infoPtr
->iconSize
.cx
;
5395 if (infoPtr
->himlSmall
!= NULL
)
5396 lprc
->right
+= infoPtr
->iconSize
.cx
;
5398 nLabelWidth
= LISTVIEW_GetLabelWidth(infoPtr
, nItem
);
5399 nLabelWidth
+= TRAILING_PADDING
;
5400 if (lprc
->right
+ nLabelWidth
< lprc
->left
+ infoPtr
->nItemWidth
)
5401 lprc
->right
+= nLabelWidth
;
5403 lprc
->right
= lprc
->left
+ infoPtr
->nItemWidth
;
5408 case LVIR_SELECTBOUNDS
:
5409 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &ptItem
)) break;
5410 if (uView
== LVS_ICON
)
5412 if (infoPtr
->himlNormal
!= NULL
)
5414 if (LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
))
5417 lprc
->left
= ptItem
.x
+ ptOrigin
.x
;
5418 lprc
->top
= ptItem
.y
+ ptOrigin
.y
;
5419 lprc
->right
= lprc
->left
+ infoPtr
->iconSpacing
.cx
;
5420 lprc
->bottom
= lprc
->top
+ infoPtr
->iconSpacing
.cy
;
5424 else if (uView
== LVS_SMALLICON
)
5426 if (LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
))
5429 nLeftPos
= lprc
->left
= ptItem
.x
+ ptOrigin
.x
;
5430 lprc
->top
= ptItem
.y
+ ptOrigin
.y
;
5431 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5433 if (infoPtr
->himlState
!= NULL
)
5434 lprc
->left
+= infoPtr
->iconSize
.cx
;
5436 lprc
->right
= lprc
->left
;
5438 if (infoPtr
->himlSmall
!= NULL
)
5439 lprc
->right
+= infoPtr
->iconSize
.cx
;
5441 nLabelWidth
= LISTVIEW_GetLabelWidth(infoPtr
, nItem
);
5442 nLabelWidth
+= TRAILING_PADDING
;
5443 if (lprc
->right
+ nLabelWidth
< nLeftPos
+ infoPtr
->nItemWidth
)
5444 lprc
->right
+= nLabelWidth
;
5446 lprc
->right
= nLeftPos
+ infoPtr
->nItemWidth
;
5452 if (!(infoPtr
->dwExStyle
&LVS_EX_FULLROWSELECT
) && (uView
&LVS_REPORT
))
5453 nLeftPos
= lprc
->left
= ptItem
.x
+ nIndent
;
5455 nLeftPos
= lprc
->left
= ptItem
.x
;
5456 lprc
->top
= ptItem
.y
;
5457 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5459 if (infoPtr
->himlState
!= NULL
)
5460 lprc
->left
+= infoPtr
->iconSize
.cx
;
5462 lprc
->right
= lprc
->left
;
5464 if (infoPtr
->dwExStyle
& LVS_EX_FULLROWSELECT
)
5467 int nColumnCount
= Header_GetItemCount(infoPtr
->hwndHeader
);
5468 Header_GetItemRect(infoPtr
->hwndHeader
, nColumnCount
-1, &br
);
5470 lprc
->right
= max(lprc
->left
, br
.right
- REPORT_MARGINX
);
5474 if (infoPtr
->himlSmall
!= NULL
)
5475 lprc
->right
+= infoPtr
->iconSize
.cx
;
5477 nLabelWidth
= LISTVIEW_GetLabelWidth(infoPtr
, nItem
);
5478 nLabelWidth
+= TRAILING_PADDING
;
5479 if (infoPtr
->himlSmall
)
5480 nLabelWidth
+= IMAGE_PADDING
;
5481 if (lprc
->right
+ nLabelWidth
< nLeftPos
+ infoPtr
->nItemWidth
)
5482 lprc
->right
+= nLabelWidth
;
5484 lprc
->right
= nLeftPos
+ infoPtr
->nItemWidth
;
5491 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult
? "TRUE" : "FALSE",
5492 lprc
->left
, lprc
->top
, lprc
->right
, lprc
->bottom
);
5498 static LRESULT
LISTVIEW_GetSubItemRect(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
, INT
5501 UINT uView
= LISTVIEW_GetType(infoPtr
);
5504 TRACE("(nItem=%d, nSubItem=%d lprc=%p)\n", nItem
, nSubItem
,
5507 if (!(uView
& LVS_REPORT
))
5510 if (flags
& LVIR_ICON
)
5512 FIXME("Unimplemented LVIR_ICON\n");
5517 int top
= min(infoPtr
->nColumnCount
, nSubItem
- 1);
5519 LISTVIEW_GetItemRect(infoPtr
,nItem
,lprc
);
5520 for (count
= 0; count
< top
; count
++)
5521 lprc
->left
+= LISTVIEW_GetColumnWidth(infoPtr
,count
);
5523 lprc
->right
= LISTVIEW_GetColumnWidth(infoPtr
,(nSubItem
-1)) +
5532 * Retrieves the width of a label.
5535 * [I] infoPtr : valid pointer to the listview structure
5538 * SUCCESS : string width (in pixels)
5541 static INT
LISTVIEW_GetLabelWidth(LISTVIEW_INFO
*infoPtr
, INT nItem
)
5543 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5544 INT nLabelWidth
= 0;
5547 TRACE("(nItem=%d)\n", nItem
);
5549 ZeroMemory(&lvItem
, sizeof(lvItem
));
5550 lvItem
.mask
= LVIF_TEXT
;
5551 lvItem
.iItem
= nItem
;
5552 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5553 lvItem
.pszText
= szDispText
;
5554 if (LISTVIEW_GetItemW(infoPtr
, &lvItem
, TRUE
))
5555 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
5562 * Retrieves the spacing between listview control items.
5565 * [I] infoPtr : valid pointer to the listview structure
5566 * [I] BOOL : flag for small or large icon
5569 * Horizontal + vertical spacing
5571 static LRESULT
LISTVIEW_GetItemSpacing(LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
5577 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
5581 if (LISTVIEW_GetType(infoPtr
) == LVS_ICON
)
5582 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
5584 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
5591 * Retrieves the state of a listview control item.
5594 * [I] infoPtr : valid pointer to the listview structure
5595 * [I] INT : item index
5596 * [I] UINT : state mask
5599 * State specified by the mask.
5601 static LRESULT
LISTVIEW_GetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
5606 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
5608 ZeroMemory(&lvItem
, sizeof(lvItem
));
5609 lvItem
.iItem
= nItem
;
5610 lvItem
.stateMask
= uMask
;
5611 lvItem
.mask
= LVIF_STATE
;
5612 if (LISTVIEW_GetItemW(infoPtr
, &lvItem
, TRUE
))
5613 uState
= lvItem
.state
& uMask
;
5621 * Retrieves the text of a listview control item or subitem.
5624 * [I] hwnd : window handle
5625 * [I] nItem : item index
5626 * [IO] lpLVItem : item information
5627 * [I] isW : TRUE if lpLVItem is Unicode
5630 * SUCCESS : string length
5633 static LRESULT
LISTVIEW_GetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
5637 if (lpLVItem
!= NULL
)
5639 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
5641 lpLVItem
->mask
= LVIF_TEXT
;
5642 lpLVItem
->iItem
= nItem
;
5643 if (LISTVIEW_GetItemT(infoPtr
, lpLVItem
, FALSE
, isW
))
5644 nLength
= textlenT(lpLVItem
->pszText
, isW
);
5653 * Searches for an item based on properties + relationships.
5656 * [I] infoPtr : valid pointer to the listview structure
5657 * [I] INT : item index
5658 * [I] INT : relationship flag
5661 * SUCCESS : item index
5664 static LRESULT
LISTVIEW_GetNextItem(LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
5666 UINT uView
= LISTVIEW_GetType(infoPtr
);
5668 LVFINDINFOW lvFindInfo
;
5669 INT nCountPerColumn
;
5672 if ((nItem
>= -1) && (nItem
< GETITEMCOUNT(infoPtr
)))
5674 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
5676 if (uFlags
& LVNI_CUT
)
5679 if (uFlags
& LVNI_DROPHILITED
)
5680 uMask
|= LVIS_DROPHILITED
;
5682 if (uFlags
& LVNI_FOCUSED
)
5683 uMask
|= LVIS_FOCUSED
;
5685 if (uFlags
& LVNI_SELECTED
)
5686 uMask
|= LVIS_SELECTED
;
5688 if (uFlags
& LVNI_ABOVE
)
5690 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
5695 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5701 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5702 lvFindInfo
.vkDirection
= VK_UP
;
5703 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5704 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5706 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5711 else if (uFlags
& LVNI_BELOW
)
5713 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
5715 while (nItem
< GETITEMCOUNT(infoPtr
))
5718 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5724 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5725 lvFindInfo
.vkDirection
= VK_DOWN
;
5726 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5727 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5729 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5734 else if (uFlags
& LVNI_TOLEFT
)
5736 if (uView
== LVS_LIST
)
5738 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
5739 while (nItem
- nCountPerColumn
>= 0)
5741 nItem
-= nCountPerColumn
;
5742 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5746 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5748 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5749 lvFindInfo
.vkDirection
= VK_LEFT
;
5750 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5751 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5753 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5758 else if (uFlags
& LVNI_TORIGHT
)
5760 if (uView
== LVS_LIST
)
5762 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
5763 while (nItem
+ nCountPerColumn
< GETITEMCOUNT(infoPtr
))
5765 nItem
+= nCountPerColumn
;
5766 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5770 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5772 lvFindInfo
.flags
= LVFI_NEARESTXY
;
5773 lvFindInfo
.vkDirection
= VK_RIGHT
;
5774 ListView_GetItemPosition(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
.pt
);
5775 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
5777 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
5786 /* search by index */
5787 for (i
= nItem
; i
< GETITEMCOUNT(infoPtr
); i
++)
5789 if ((ListView_GetItemState(infoPtr
->hwndSelf
, i
, uMask
) & uMask
) == uMask
)
5798 /* LISTVIEW_GetNumberOfWorkAreas */
5802 * Retrieves the origin coordinates when in icon or small icon display mode.
5805 * [I] infoPtr : valid pointer to the listview structure
5806 * [O] LPPOINT : coordinate information
5812 static LRESULT
LISTVIEW_GetOrigin(LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
5814 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
5815 UINT uView
= lStyle
& LVS_TYPEMASK
;
5816 BOOL bResult
= FALSE
;
5818 TRACE("(lpptOrigin=%p)\n", lpptOrigin
);
5820 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5822 SCROLLINFO scrollInfo
;
5823 ZeroMemory(lpptOrigin
, sizeof(POINT
));
5824 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
5825 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
5827 if (lStyle
& WS_HSCROLL
)
5829 scrollInfo
.fMask
= SIF_POS
;
5830 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
5831 lpptOrigin
->x
= -scrollInfo
.nPos
;
5834 if (lStyle
& WS_VSCROLL
)
5836 scrollInfo
.fMask
= SIF_POS
;
5837 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
5838 lpptOrigin
->y
= -scrollInfo
.nPos
;
5843 TRACE("(pt=(%ld,%ld))\n", lpptOrigin
->x
, lpptOrigin
->y
);
5852 * Retrieves the number of items that are marked as selected.
5855 * [I] infoPtr : valid pointer to the listview structure
5858 * Number of items selected.
5860 static LRESULT
LISTVIEW_GetSelectedCount(LISTVIEW_INFO
*infoPtr
)
5863 INT nSelectedCount
= 0;
5866 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
5868 if (ListView_GetItemState(infoPtr
->hwndSelf
, i
, LVIS_SELECTED
) & LVIS_SELECTED
)
5872 return nSelectedCount
;
5877 * Retrieves the width of a string.
5880 * [I] hwnd : window handle
5881 * [I] lpszText : text string to process
5882 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5885 * SUCCESS : string width (in pixels)
5888 static LRESULT
LISTVIEW_GetStringWidthT(LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
5890 if (is_textT(lpszText
, isW
))
5892 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
5893 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
5894 HFONT hOldFont
= SelectObject(hdc
, hFont
);
5896 ZeroMemory(&stringSize
, sizeof(SIZE
));
5898 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
5900 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
5901 SelectObject(hdc
, hOldFont
);
5902 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
5903 return stringSize
.cx
;
5910 * Retrieves the text backgound color.
5913 * [I] infoPtr : valid pointer to the listview structure
5916 * COLORREF associated with the the background.
5918 static LRESULT
LISTVIEW_GetTextBkColor(LISTVIEW_INFO
*infoPtr
)
5920 return infoPtr
->clrTextBk
;
5925 * Retrieves the text color.
5928 * [I] infoPtr : valid pointer to the listview structure
5931 * COLORREF associated with the text.
5933 static LRESULT
LISTVIEW_GetTextColor(LISTVIEW_INFO
*infoPtr
)
5935 return infoPtr
->clrText
;
5940 * Determines item if a hit or closest if not
5943 * [I] infoPtr : valid pointer to the listview structure
5944 * [IO] LPLV_INTHIT : hit test information
5945 * [I] subitem : fill out iSubItem.
5948 * SUCCESS : item index of hit
5951 static INT
LISTVIEW_SuperHitTestItem(LISTVIEW_INFO
*infoPtr
, LPLV_INTHIT lpInt
, BOOL subitem
)
5953 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
5954 UINT uView
= lStyle
& LVS_TYPEMASK
;
5955 INT i
,j
,topindex
,bottomindex
;
5956 RECT rcItem
,rcSubItem
;
5957 DWORD xterm
, yterm
, dist
;
5959 TRACE("(x=%ld, y=%ld)\n", lpInt
->ht
.pt
.x
, lpInt
->ht
.pt
.y
);
5961 topindex
= LISTVIEW_GetTopIndex(infoPtr
);
5962 if (uView
== LVS_REPORT
)
5964 bottomindex
= topindex
+ LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
5965 bottomindex
= min(bottomindex
,GETITEMCOUNT(infoPtr
));
5969 bottomindex
= GETITEMCOUNT(infoPtr
);
5972 lpInt
->distance
= 0x7fffffff;
5973 lpInt
->iDistItem
= -1;
5975 for (i
= topindex
; i
< bottomindex
; i
++)
5977 rcItem
.left
= LVIR_BOUNDS
;
5978 if (LISTVIEW_GetItemRect(infoPtr
, i
, &rcItem
))
5980 if (PtInRect(&rcItem
, lpInt
->ht
.pt
))
5983 rcItem
.left
= LVIR_ICON
;
5984 if (LISTVIEW_GetItemRect(infoPtr
, i
, &rcItem
))
5986 if (PtInRect(&rcItem
, lpInt
->ht
.pt
))
5988 lpInt
->ht
.flags
= LVHT_ONITEMICON
;
5989 lpInt
->ht
.iItem
= i
;
5994 rcItem
.left
= LVIR_LABEL
;
5995 if (LISTVIEW_GetItemRect(infoPtr
, i
, &rcItem
))
5997 if (PtInRect(&rcItem
, lpInt
->ht
.pt
))
5999 lpInt
->ht
.flags
= LVHT_ONITEMLABEL
;
6000 lpInt
->ht
.iItem
= i
;
6005 lpInt
->ht
.flags
= LVHT_ONITEMSTATEICON
;
6006 lpInt
->ht
.iItem
= i
;
6010 lpInt
->ht
.iSubItem
= 0;
6011 rcSubItem
.right
= rcSubItem
.left
;
6012 for (j
= 0; j
< infoPtr
->nColumnCount
; j
++)
6014 rcSubItem
.left
= rcSubItem
.right
;
6015 rcSubItem
.right
+= LISTVIEW_GetColumnWidth(infoPtr
, j
);
6016 if (PtInRect(&rcSubItem
, lpInt
->ht
.pt
))
6018 lpInt
->ht
.iSubItem
= j
;
6028 * Now compute distance from point to center of boundary
6029 * box. Since we are only interested in the relative
6030 * distance, we can skip the nasty square root operation
6032 xterm
= rcItem
.left
+ (rcItem
.right
- rcItem
.left
)/2 - lpInt
->ht
.pt
.x
;
6033 yterm
= rcItem
.top
+ (rcItem
.bottom
- rcItem
.top
)/2 - lpInt
->ht
.pt
.y
;
6034 dist
= xterm
* xterm
+ yterm
* yterm
;
6035 if (dist
< lpInt
->distance
)
6037 lpInt
->distance
= dist
;
6038 lpInt
->iDistItem
= i
;
6044 lpInt
->ht
.flags
= LVHT_NOWHERE
;
6045 TRACE("no hit, closest item %d, distance %ld\n", lpInt
->iDistItem
, lpInt
->distance
);
6052 * Determines which section of the item was selected (if any).
6055 * [I] infoPtr : valid pointer to the listview structure
6056 * [IO] LPLVHITTESTINFO : hit test information
6057 * [I] subitem : fill out iSubItem.
6060 * SUCCESS : item index
6063 static INT
LISTVIEW_HitTestItem(LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpHitTestInfo
, BOOL subitem
)
6066 LV_INTHIT lv_inthit
;
6068 TRACE("(x=%ld, y=%ld)\n", lpHitTestInfo
->pt
.x
,
6069 lpHitTestInfo
->pt
.y
);
6071 memcpy(&lv_inthit
, lpHitTestInfo
, sizeof(LVHITTESTINFO
));
6072 ret
= LISTVIEW_SuperHitTestItem(infoPtr
, &lv_inthit
, subitem
);
6073 memcpy(lpHitTestInfo
, &lv_inthit
, sizeof(LVHITTESTINFO
));
6079 * Determines which listview item is located at the specified position.
6082 * [I] infoPtr : valid pointer to the listview structure
6083 * [IO} LPLVHITTESTINFO : hit test information
6086 * SUCCESS : item index
6089 static LRESULT
LISTVIEW_HitTest(LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpHitTestInfo
)
6093 lpHitTestInfo
->flags
= 0;
6095 if (infoPtr
->rcList
.left
> lpHitTestInfo
->pt
.x
)
6096 lpHitTestInfo
->flags
= LVHT_TOLEFT
;
6097 else if (infoPtr
->rcList
.right
< lpHitTestInfo
->pt
.x
)
6098 lpHitTestInfo
->flags
= LVHT_TORIGHT
;
6099 if (infoPtr
->rcList
.top
> lpHitTestInfo
->pt
.y
)
6100 lpHitTestInfo
->flags
|= LVHT_ABOVE
;
6101 else if (infoPtr
->rcList
.bottom
< lpHitTestInfo
->pt
.y
)
6102 lpHitTestInfo
->flags
|= LVHT_BELOW
;
6104 if (lpHitTestInfo
->flags
== 0)
6106 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6107 * an app might pass only a structure with space up to iItem!
6108 * (MS Office 97 does that for instance in the file open dialog)
6110 nItem
= LISTVIEW_HitTestItem(infoPtr
, lpHitTestInfo
, FALSE
);
6118 * Determines which listview subitem is located at the specified position.
6121 * [I] infoPtr : valid pointer to the listview structure
6122 * [IO} LPLVHITTESTINFO : hit test information
6125 * SUCCESS : item index
6128 static LRESULT
LISTVIEW_SubItemHitTest(LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpHitTestInfo
)
6132 lpHitTestInfo
->flags
= 0;
6134 if (infoPtr
->rcList
.left
> lpHitTestInfo
->pt
.x
)
6135 lpHitTestInfo
->flags
= LVHT_TOLEFT
;
6136 else if (infoPtr
->rcList
.right
< lpHitTestInfo
->pt
.x
)
6137 lpHitTestInfo
->flags
= LVHT_TORIGHT
;
6138 if (infoPtr
->rcList
.top
> lpHitTestInfo
->pt
.y
)
6139 lpHitTestInfo
->flags
|= LVHT_ABOVE
;
6140 else if (infoPtr
->rcList
.bottom
< lpHitTestInfo
->pt
.y
)
6141 lpHitTestInfo
->flags
|= LVHT_BELOW
;
6143 if (lpHitTestInfo
->flags
== 0)
6144 nItem
= LISTVIEW_HitTestItem(infoPtr
, lpHitTestInfo
, TRUE
);
6151 * Inserts a new column.
6154 * [I] infoPtr : valid pointer to the listview structure
6155 * [I] INT : column index
6156 * [I] LPLVCOLUMNW : column information
6159 * SUCCESS : new column index
6162 static LRESULT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6163 LPLVCOLUMNW lpColumn
, BOOL isW
)
6165 INT nNewColumn
= -1;
6168 TRACE("(nColumn=%d, lpColumn=%p)\n", nColumn
, lpColumn
);
6170 if (lpColumn
!= NULL
)
6172 /* initialize memory */
6173 ZeroMemory(&hdi
, sizeof(hdi
));
6175 if (lpColumn
->mask
& LVCF_FMT
)
6177 /* format member is valid */
6178 hdi
.mask
|= HDI_FORMAT
;
6180 /* set text alignment (leftmost column must be left-aligned) */
6183 hdi
.fmt
|= HDF_LEFT
;
6187 if (lpColumn
->fmt
& LVCFMT_LEFT
)
6189 hdi
.fmt
|= HDF_LEFT
;
6191 else if (lpColumn
->fmt
& LVCFMT_RIGHT
)
6193 hdi
.fmt
|= HDF_RIGHT
;
6195 else if (lpColumn
->fmt
& LVCFMT_CENTER
)
6197 hdi
.fmt
|= HDF_CENTER
;
6201 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6203 hdi
.fmt
|= HDF_BITMAP_ON_RIGHT
;
6207 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6212 if (lpColumn
->fmt
& LVCFMT_IMAGE
)
6214 hdi
.fmt
|= HDF_IMAGE
;
6215 hdi
.iImage
= I_IMAGECALLBACK
;
6219 if (lpColumn
->mask
& LVCF_WIDTH
)
6221 hdi
.mask
|= HDI_WIDTH
;
6222 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
6224 /* make it fill the remainder of the controls width */
6229 ZeroMemory(&hdit
, sizeof(hdit
));
6231 /* get the width of every item except the current one */
6232 hdit
.mask
= HDI_WIDTH
;
6235 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++) {
6236 Header_GetItemW(infoPtr
->hwndHeader
, item_index
, (LPARAM
)(&hdit
));
6240 /* retrieve the layout of the header */
6241 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
6242 /* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6243 TRACE("start cxy=%d left=%d right=%d\n", hdi
.cxy
, rcHeader
.left
, rcHeader
.right
);
6245 hdi
.cxy
= (rcHeader
.right
- rcHeader
.left
) - hdi
.cxy
;
6248 hdi
.cxy
= lpColumn
->cx
;
6251 if (lpColumn
->mask
& LVCF_TEXT
)
6253 hdi
.mask
|= HDI_TEXT
| HDI_FORMAT
;
6254 hdi
.pszText
= lpColumn
->pszText
;
6255 hdi
.cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
6256 hdi
.fmt
|= HDF_STRING
;
6259 if (lpColumn
->mask
& LVCF_IMAGE
)
6261 hdi
.mask
|= HDI_IMAGE
;
6262 hdi
.iImage
= lpColumn
->iImage
;
6265 if (lpColumn
->mask
& LVCF_ORDER
)
6267 hdi
.mask
|= HDI_ORDER
;
6268 hdi
.iOrder
= lpColumn
->iOrder
;
6271 /* insert item in header control */
6272 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
6273 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
6274 (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6276 /* Need to reset the item width when inserting a new column */
6277 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(infoPtr
);
6279 LISTVIEW_UpdateScroll(infoPtr
);
6280 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
6286 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6287 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6288 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6289 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6290 their own sort proc. when sending LVM_SORTITEMS.
6293 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6295 LVS_SORTXXX must be specified,
6296 LVS_OWNERDRAW is not set,
6297 <item>.pszText is not LPSTR_TEXTCALLBACK.
6299 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6300 are sorted based on item text..."
6302 static INT WINAPI
LISTVIEW_InsertCompare( LPVOID first
, LPVOID second
, LPARAM lParam
)
6304 LONG lStyle
= GetWindowLongW((HWND
) lParam
, GWL_STYLE
);
6305 LISTVIEW_ITEM
* lv_first
= (LISTVIEW_ITEM
*) DPA_GetPtr( (HDPA
)first
, 0 );
6306 LISTVIEW_ITEM
* lv_second
= (LISTVIEW_ITEM
*) DPA_GetPtr( (HDPA
)second
, 0 );
6307 INT cmpv
= textcmpWT(lv_first
->hdr
.pszText
, lv_second
->hdr
.pszText
, TRUE
);
6309 /* if we're sorting descending, negate the return value */
6310 return (lStyle
& LVS_SORTDESCENDING
) ? -cmpv
: cmpv
;
6315 * Inserts a new item in the listview control.
6318 * [I] infoPtr : valid pointer to the listview structure
6319 * [I] LPLVITEMW : item information
6320 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6323 * SUCCESS : new item index
6326 static LRESULT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
6328 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
6329 UINT uView
= lStyle
& LVS_TYPEMASK
;
6333 LISTVIEW_ITEM
*lpItem
;
6336 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6338 if (lStyle
& LVS_OWNERDATA
)
6340 nItem
= infoPtr
->hdpaItems
->nItemCount
;
6341 infoPtr
->hdpaItems
->nItemCount
++;
6345 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6346 if (!lpLVItem
|| lpLVItem
->iSubItem
) return -1;
6348 if (!is_assignable_item(lpLVItem
, lStyle
)) return -1;
6350 if ( !(lpItem
= (LISTVIEW_ITEM
*)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM
))) )
6353 ZeroMemory(lpItem
, sizeof(LISTVIEW_ITEM
));
6355 /* insert item in listview control data structure */
6356 if ( (hdpaSubItems
= DPA_Create(8)) )
6357 nItem
= DPA_InsertPtr(hdpaSubItems
, 0, lpItem
);
6358 if (nItem
== -1) goto fail
;
6360 is_sorted
= (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
6361 !(lStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
6363 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
,
6364 is_sorted
? GETITEMCOUNT( infoPtr
) + 1 : lpLVItem
->iItem
,
6366 if (nItem
== -1) goto fail
;
6368 if (!LISTVIEW_SetItemT(infoPtr
, lpLVItem
, isW
)) goto fail
;
6370 /* if we're sorted, sort the list, and update the index */
6373 DPA_Sort( infoPtr
->hdpaItems
, LISTVIEW_InsertCompare
, (LPARAM
)infoPtr
->hwndSelf
);
6374 nItem
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, hdpaSubItems
);
6375 if (nItem
== -1) goto fail
;
6378 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
6380 lpItem
->valid
= TRUE
;
6382 /* send LVN_INSERTITEM notification */
6383 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
6385 nmlv
.lParam
= lpItem
->lParam
;
6386 listview_notify(infoPtr
, LVN_INSERTITEM
, &nmlv
);
6388 /* align items (set position of each item) */
6389 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6391 if (lStyle
& LVS_ALIGNLEFT
) LISTVIEW_AlignLeft(infoPtr
);
6392 else LISTVIEW_AlignTop(infoPtr
);
6395 LISTVIEW_UpdateScroll(infoPtr
);
6397 /* FIXME: refresh client area */
6398 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
6403 DPA_Destroy (hdpaSubItems
);
6404 COMCTL32_Free (lpItem
);
6410 * Redraws a range of items.
6413 * [I] infoPtr : valid pointer to the listview structure
6414 * [I] INT : first item
6415 * [I] INT : last item
6421 static LRESULT
LISTVIEW_RedrawItems(LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
6426 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
6427 max(nFirst
, nLast
) >= GETITEMCOUNT(infoPtr
))
6430 for (i
= nFirst
; i
<= nLast
; i
++)
6432 rcItem
.left
= LVIR_BOUNDS
;
6433 LISTVIEW_GetItemRect(infoPtr
, i
, &rcItem
);
6434 InvalidateRect(infoPtr
->hwndSelf
, &rcItem
, TRUE
);
6442 * Scroll the content of a listview.
6445 * [I] infoPtr : valid pointer to the listview structure
6446 * [I] INT : horizontal scroll amount in pixels
6447 * [I] INT : vertical scroll amount in pixels
6454 * If the control is in report mode (LVS_REPORT) the control can
6455 * be scrolled only in line increments. "dy" will be rounded to the
6456 * nearest number of pixels that are a whole line. Ex: if line height
6457 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6458 * is passed the the scroll will be 0. (per MSDN 7/2002)
6460 * For: (per experimentaion with native control and CSpy ListView)
6461 * LVS_ICON dy=1 = 1 pixel (vertical only)
6463 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6465 * LVS_LIST dx=1 = 1 column (horizontal only)
6466 * but will only scroll 1 column per message
6467 * no matter what the value.
6468 * dy must be 0 or FALSE returned.
6469 * LVS_REPORT dx=1 = 1 pixel
6473 static LRESULT
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
6475 switch(LISTVIEW_GetType(infoPtr
)) {
6477 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
6478 dy
/= infoPtr
->nItemHeight
;
6481 if (dy
!= 0) return FALSE
;
6488 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
, 0);
6489 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
, 0);
6496 * Sets the background color.
6499 * [I] infoPtr : valid pointer to the listview structure
6500 * [I] COLORREF : background color
6506 static LRESULT
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrBk
)
6508 if(infoPtr
->clrBk
!= clrBk
) {
6509 infoPtr
->clrBk
= clrBk
;
6510 if (infoPtr
->hBkBrush
) DeleteObject(infoPtr
->hBkBrush
);
6511 infoPtr
->hBkBrush
= clrBk
== CLR_NONE
? 0 : CreateSolidBrush(clrBk
);
6512 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
6518 /* LISTVIEW_SetBkImage */
6522 * Sets the attributes of a header item.
6525 * [I] infoPtr : valid pointer to the listview structure
6526 * [I] INT : column index
6527 * [I] LPLVCOLUMNW : column attributes
6528 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6529 * otherwise it is in fact a LPLVCOLUMNA
6535 static LRESULT
LISTVIEW_SetColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6536 LPLVCOLUMNW lpColumn
, BOOL isW
)
6538 BOOL bResult
= FALSE
;
6539 HDITEMW hdi
, hdiget
;
6541 if ((lpColumn
!= NULL
) && (nColumn
>= 0) &&
6542 (nColumn
< Header_GetItemCount(infoPtr
->hwndHeader
)))
6544 /* initialize memory */
6545 ZeroMemory(&hdi
, sizeof(hdi
));
6547 if (lpColumn
->mask
& LVCF_FMT
)
6549 /* format member is valid */
6550 hdi
.mask
|= HDI_FORMAT
;
6552 /* get current format first */
6553 hdiget
.mask
= HDI_FORMAT
;
6554 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdiget
))
6555 /* preserve HDF_STRING if present */
6556 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
6558 /* set text alignment (leftmost column must be left-aligned) */
6561 hdi
.fmt
|= HDF_LEFT
;
6565 if (lpColumn
->fmt
& LVCFMT_LEFT
)
6566 hdi
.fmt
|= HDF_LEFT
;
6567 else if (lpColumn
->fmt
& LVCFMT_RIGHT
)
6568 hdi
.fmt
|= HDF_RIGHT
;
6569 else if (lpColumn
->fmt
& LVCFMT_CENTER
)
6570 hdi
.fmt
|= HDF_CENTER
;
6573 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6574 hdi
.fmt
|= HDF_BITMAP_ON_RIGHT
;
6576 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6577 hdi
.fmt
|= HDF_IMAGE
;
6579 if (lpColumn
->fmt
& LVCFMT_IMAGE
)
6581 hdi
.fmt
|= HDF_IMAGE
;
6582 hdi
.iImage
= I_IMAGECALLBACK
;
6586 if (lpColumn
->mask
& LVCF_WIDTH
)
6588 hdi
.mask
|= HDI_WIDTH
;
6589 hdi
.cxy
= lpColumn
->cx
;
6592 if (lpColumn
->mask
& LVCF_TEXT
)
6594 hdi
.mask
|= HDI_TEXT
| HDI_FORMAT
;
6595 hdi
.pszText
= lpColumn
->pszText
;
6596 hdi
.cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
6597 hdi
.fmt
|= HDF_STRING
;
6600 if (lpColumn
->mask
& LVCF_IMAGE
)
6602 hdi
.mask
|= HDI_IMAGE
;
6603 hdi
.iImage
= lpColumn
->iImage
;
6606 if (lpColumn
->mask
& LVCF_ORDER
)
6608 hdi
.mask
|= HDI_ORDER
;
6609 hdi
.iOrder
= lpColumn
->iOrder
;
6612 /* set header item attributes */
6614 bResult
= Header_SetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
);
6616 bResult
= Header_SetItemA(infoPtr
->hwndHeader
, nColumn
, &hdi
);
6624 * Sets the column order array
6627 * [I] infoPtr : valid pointer to the listview structure
6628 * [I] INT : number of elements in column order array
6629 * [I] INT : pointer to column order array
6635 static LRESULT
LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
6637 FIXME("iCount %d lpiArray %p\n", iCount
, lpiArray
);
6648 * Sets the width of a column
6651 * [I] infoPtr : valid pointer to the listview structure
6652 * [I] INT : column index
6653 * [I] INT : column width
6659 static LRESULT
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT iCol
, INT cx
)
6663 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
6664 UINT uView
= lStyle
& LVS_TYPEMASK
;
6669 WCHAR text_buffer
[DISP_TEXT_SIZE
];
6670 INT header_item_count
;
6675 WCHAR szDispText
[DISP_TEXT_SIZE
];
6677 if (!infoPtr
->hwndHeader
) /* make sure we have a header */
6680 /* set column width only if in report or list mode */
6681 if ((uView
!= LVS_REPORT
) && (uView
!= LVS_LIST
))
6684 TRACE("(iCol=%d, cx=%d\n", iCol
, cx
);
6686 /* take care of invalid cx values */
6687 if((uView
== LVS_REPORT
) && (cx
< -2))
6688 cx
= LVSCW_AUTOSIZE
;
6689 else if (uView
== LVS_LIST
&& (cx
< 1))
6692 /* resize all columns if in LVS_LIST mode */
6693 if(uView
== LVS_LIST
) {
6694 infoPtr
->nItemWidth
= cx
;
6695 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
); /* force redraw of the listview */
6699 /* autosize based on listview items width */
6700 if(cx
== LVSCW_AUTOSIZE
)
6702 /* set the width of the column to the width of the widest item */
6703 if (iCol
== 0 || uView
== LVS_LIST
)
6706 for(item_index
= 0; item_index
< GETITEMCOUNT(infoPtr
); item_index
++)
6708 nLabelWidth
= LISTVIEW_GetLabelWidth(infoPtr
, item_index
);
6709 cx
= (nLabelWidth
>cx
)?nLabelWidth
:cx
;
6711 if (infoPtr
->himlSmall
)
6712 cx
+= infoPtr
->iconSize
.cx
+ IMAGE_PADDING
;
6716 ZeroMemory(&lvItem
, sizeof(lvItem
));
6717 lvItem
.iSubItem
= iCol
;
6718 lvItem
.mask
= LVIF_TEXT
;
6719 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6720 lvItem
.pszText
= szDispText
;
6721 *lvItem
.pszText
= '\0';
6723 for(item_index
= 0; item_index
< GETITEMCOUNT(infoPtr
); item_index
++)
6725 lvItem
.iItem
= item_index
;
6726 LISTVIEW_GetItemT(infoPtr
, &lvItem
, FALSE
, TRUE
);
6727 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
6728 cx
= (nLabelWidth
>cx
)?nLabelWidth
:cx
;
6731 cx
+= TRAILING_PADDING
;
6732 } /* autosize based on listview header width */
6733 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
6735 header_item_count
= Header_GetItemCount(infoPtr
->hwndHeader
);
6737 /* if iCol is the last column make it fill the remainder of the controls width */
6738 if(iCol
== (header_item_count
- 1)) {
6739 /* get the width of every item except the current one */
6740 hdi
.mask
= HDI_WIDTH
;
6743 for(item_index
= 0; item_index
< (header_item_count
- 1); item_index
++) {
6744 Header_GetItemW(infoPtr
->hwndHeader
, item_index
, (LPARAM
)(&hdi
));
6748 /* retrieve the layout of the header */
6749 GetWindowRect(infoPtr
->hwndHeader
, &rcHeader
);
6751 cx
= (rcHeader
.right
- rcHeader
.left
) - cx
;
6755 /* Despite what the MS docs say, if this is not the last
6756 column, then MS resizes the column to the width of the
6757 largest text string in the column, including headers
6758 and items. This is different from LVSCW_AUTOSIZE in that
6759 LVSCW_AUTOSIZE ignores the header string length.
6762 /* retrieve header font */
6763 header_font
= SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0L, 0L);
6765 /* retrieve header text */
6766 hdi
.mask
= HDI_TEXT
;
6767 hdi
.cchTextMax
= sizeof(text_buffer
)/sizeof(text_buffer
[0]);
6768 hdi
.pszText
= text_buffer
;
6770 Header_GetItemW(infoPtr
->hwndHeader
, iCol
, (LPARAM
)(&hdi
));
6772 /* determine the width of the text in the header */
6773 hdc
= GetDC(infoPtr
->hwndSelf
);
6774 old_font
= SelectObject(hdc
, header_font
); /* select the font into hdc */
6776 GetTextExtentPoint32W(hdc
, text_buffer
, lstrlenW(text_buffer
), &size
);
6778 SelectObject(hdc
, old_font
); /* restore the old font */
6779 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6781 ZeroMemory(&lvItem
, sizeof(lvItem
));
6782 lvItem
.iSubItem
= iCol
;
6783 lvItem
.mask
= LVIF_TEXT
;
6784 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6785 lvItem
.pszText
= szDispText
;
6786 *lvItem
.pszText
= '\0';
6788 for(item_index
= 0; item_index
< GETITEMCOUNT(infoPtr
); item_index
++)
6790 lvItem
.iItem
= item_index
;
6791 LISTVIEW_GetItemT(infoPtr
, &lvItem
, FALSE
, TRUE
);
6792 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
6793 nLabelWidth
+= TRAILING_PADDING
;
6794 /* While it is possible for subitems to have icons, even MS messes
6795 up the positioning, so I suspect no applications actually use
6797 if (item_index
== 0 && infoPtr
->himlSmall
)
6798 nLabelWidth
+= infoPtr
->iconSize
.cx
+ IMAGE_PADDING
;
6799 cx
= (nLabelWidth
>cx
)?nLabelWidth
:cx
;
6804 /* call header to update the column change */
6805 hdi
.mask
= HDI_WIDTH
;
6808 lret
= Header_SetItemW(infoPtr
->hwndHeader
, (WPARAM
)iCol
, (LPARAM
)&hdi
);
6810 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
); /* force redraw of the listview */
6817 * Sets the extended listview style.
6820 * [I] infoPtr : valid pointer to the listview structure
6825 * SUCCESS : previous style
6828 static LRESULT
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD dwMask
, DWORD dwStyle
)
6830 DWORD dwOldStyle
= infoPtr
->dwExStyle
;
6834 infoPtr
->dwExStyle
= (dwOldStyle
& ~dwMask
) | (dwStyle
& dwMask
);
6836 infoPtr
->dwExStyle
= dwStyle
;
6843 * Sets the new hot cursor used during hot tracking and hover selection.
6846 * [I] infoPtr : valid pointer to the listview structure
6847 * [I} hCurosr : the new hot cursor handle
6850 * Returns the previous hot cursor
6852 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
6854 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
6855 infoPtr
->hHotCursor
= hCursor
;
6862 * Sets the hot item index.
6865 * [I] infoPtr : valid pointer to the listview structure
6869 * SUCCESS : previous hot item index
6870 * FAILURE : -1 (no hot item)
6872 static LRESULT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
6874 INT iOldIndex
= infoPtr
->nHotItem
;
6875 infoPtr
->nHotItem
= iIndex
;
6882 * Sets the amount of time the cursor must hover over an item before it is selected.
6885 * [I] infoPtr : valid pointer to the listview structure
6886 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6889 * Returns the previous hover time
6891 static LRESULT
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
6893 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
6894 infoPtr
->dwHoverTime
= dwHoverTime
;
6895 return oldHoverTime
;
6900 * Sets spacing for icons of LVS_ICON style.
6903 * [I] infoPtr : valid pointer to the listview structure
6904 * [I] DWORD : MAKELONG(cx, cy)
6907 * MAKELONG(oldcx, oldcy)
6909 static LRESULT
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, DWORD spacing
)
6911 INT cy
= HIWORD(spacing
);
6912 INT cx
= LOWORD(spacing
);
6914 LONG lStyle
= GetWindowLongA(infoPtr
->hwndSelf
, GWL_STYLE
);
6915 UINT uView
= lStyle
& LVS_TYPEMASK
;
6917 oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
6918 if (cx
== -1) /* set to default */
6919 cx
= GetSystemMetrics(SM_CXICONSPACING
);
6920 if (cy
== -1) /* set to default */
6921 cy
= GetSystemMetrics(SM_CYICONSPACING
);
6924 infoPtr
->iconSpacing
.cx
= cx
;
6926 { /* if 0 then compute width */
6927 if (uView
== LVS_ICON
)
6928 FIXME("width computation not yet done\n");
6930 * Should scan each item and determine max width of
6931 * icon or label, then make that the width
6933 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
6934 infoPtr
->iconSpacing
.cx
= LISTVIEW_GetItemWidth(infoPtr
);
6937 infoPtr
->iconSpacing
.cy
= cy
;
6939 { /* if 0 then compute height */
6940 if (uView
== LVS_ICON
)
6941 infoPtr
->iconSpacing
.cy
= infoPtr
->iconSize
.cy
+ 2 * infoPtr
->ntmHeight
6942 + ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_PADDING
;
6944 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
6945 infoPtr
->iconSpacing
.cy
= LISTVIEW_GetItemHeight(infoPtr
);
6948 TRACE("old=(%d,%d), new=(%ld,%ld), iconSize=(%ld,%ld), ntmH=%d\n",
6949 LOWORD(oldspacing
), HIWORD(oldspacing
),
6950 infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
,
6951 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
6952 infoPtr
->ntmHeight
);
6954 /* these depend on the iconSpacing */
6955 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(infoPtr
);
6956 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(infoPtr
);
6966 * [I] infoPtr : valid pointer to the listview structure
6967 * [I] INT : image list type
6968 * [I] HIMAGELIST : image list handle
6971 * SUCCESS : old image list
6974 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
6976 HIMAGELIST himlOld
= 0;
6978 UINT uView
= LISTVIEW_GetType(infoPtr
);
6983 himlOld
= infoPtr
->himlNormal
;
6984 infoPtr
->himlNormal
= himl
;
6985 if(himl
&& (LVS_ICON
== uView
))
6988 ImageList_GetIconSize(himl
, &cx
, &cy
);
6989 TRACE("icon old size=(%ld,%ld), new size=(%d,%d)\n",
6990 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
, cx
, cy
);
6991 infoPtr
->iconSize
.cx
= cx
;
6992 infoPtr
->iconSize
.cy
= cy
;
6993 LISTVIEW_SetIconSpacing(infoPtr
,0);
6998 himlOld
= infoPtr
->himlSmall
;
6999 infoPtr
->himlSmall
= himl
;
7003 himlOld
= infoPtr
->himlState
;
7004 infoPtr
->himlState
= himl
;
7005 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
7009 oldHeight
= infoPtr
->nItemHeight
;
7010 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(infoPtr
);
7011 if (infoPtr
->nItemHeight
!= oldHeight
)
7012 LISTVIEW_UpdateScroll(infoPtr
);
7019 * Preallocates memory (does *not* set the actual count of items !)
7022 * [I] infoPtr : valid pointer to the listview structure
7023 * [I] INT : item count (projected number of items to allocate)
7024 * [I] DWORD : update flags
7030 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
7032 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems
, dwFlags
);
7034 if (GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & LVS_OWNERDATA
)
7036 int precount
,topvisible
;
7038 TRACE("LVS_OWNERDATA is set!\n");
7039 if (dwFlags
& (LVSICF_NOINVALIDATEALL
| LVSICF_NOSCROLL
))
7040 FIXME("flags %s %s not implemented\n",
7041 (dwFlags
& LVSICF_NOINVALIDATEALL
) ? "LVSICF_NOINVALIDATEALL"
7043 (dwFlags
& LVSICF_NOSCROLL
) ? "LVSICF_NOSCROLL" : "");
7046 * Internally remove all the selections.
7050 LISTVIEW_SELECTION
*selection
;
7051 selection
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
,0);
7053 LISTVIEW_RemoveSelectionRange(infoPtr
,selection
->lower
,
7056 while (infoPtr
->hdpaSelectionRanges
->nItemCount
>0);
7058 precount
= infoPtr
->hdpaItems
->nItemCount
;
7059 topvisible
= ListView_GetTopIndex(infoPtr
->hwndSelf
) +
7060 LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
7062 infoPtr
->hdpaItems
->nItemCount
= nItems
;
7064 infoPtr
->nItemWidth
= max(LISTVIEW_GetItemWidth(infoPtr
),
7065 DEFAULT_COLUMN_WIDTH
);
7067 LISTVIEW_UpdateSize(infoPtr
);
7068 LISTVIEW_UpdateScroll(infoPtr
);
7070 if (min(precount
,infoPtr
->hdpaItems
->nItemCount
)<topvisible
)
7071 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
7075 /* According to MSDN for non-LVS_OWNERDATA this is just
7076 * a performance issue. The control allocates its internal
7077 * data structures for the number of items specified. It
7078 * cuts down on the number of memory allocations. Therefore
7079 * we will just issue a WARN here
7081 WARN("for non-ownerdata performance option not implemented.\n");
7089 * Sets the position of an item.
7092 * [I] infoPtr : valid pointer to the listview structure
7093 * [I] INT : item index
7094 * [I] LONG : x coordinate
7095 * [I] LONG : y coordinate
7101 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
,
7102 LONG nPosX
, LONG nPosY
)
7104 UINT lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
7105 UINT uView
= lStyle
& LVS_TYPEMASK
;
7106 LISTVIEW_ITEM
*lpItem
;
7108 BOOL bResult
= FALSE
;
7110 TRACE("(nItem=%d, X=%ld, Y=%ld)\n", nItem
, nPosX
, nPosY
);
7112 if (lStyle
& LVS_OWNERDATA
)
7115 if ((nItem
>= 0) || (nItem
< GETITEMCOUNT(infoPtr
)))
7117 if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
7119 if ( (hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
)) )
7121 if ( (lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0)) )
7125 orig
= lpItem
->ptPosition
;
7126 if ((nPosX
== -1) && (nPosY
== -1))
7128 /* This point value seems to be an undocumented feature. The
7129 * best guess is that it means either at the origin, or at
7130 * the true beginning of the list. I will assume the origin.
7133 if (!LISTVIEW_GetOrigin(infoPtr
, &pt1
))
7140 if (uView
== LVS_ICON
)
7142 nPosX
+= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2;
7143 nPosY
+= ICON_TOP_PADDING
;
7145 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7149 lpItem
->ptPosition
.x
= nPosX
;
7150 lpItem
->ptPosition
.y
= nPosY
;
7151 if (uView
== LVS_ICON
)
7153 lpItem
->ptPosition
.y
-= ICON_TOP_PADDING
;
7154 lpItem
->ptPosition
.x
-= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2;
7155 if ((lpItem
->ptPosition
.y
< 0) || (lpItem
->ptPosition
.x
< 0))
7157 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7158 orig
.x
, orig
.y
, nPosX
, nPosY
, lpItem
->ptPosition
.x
, lpItem
->ptPosition
.y
);
7161 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7162 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7167 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7168 orig
.x
, orig
.y
, nPosX
, nPosY
, lpItem
->ptPosition
.x
, lpItem
->ptPosition
.y
);
7181 * Sets the state of one or many items.
7184 * [I] infoPtr : valid pointer to the listview structure
7185 * [I]INT : item index
7186 * [I] LPLVITEM : item or subitem info
7192 static LRESULT
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
)
7194 BOOL bResult
= TRUE
;
7197 TRACE("(nItem=%d, lpLVItem=%s)\n",
7198 nItem
, debuglvitem_t(lpLVItem
, TRUE
));
7200 ZeroMemory(&lvItem
, sizeof(lvItem
));
7201 lvItem
.mask
= LVIF_STATE
;
7202 lvItem
.state
= lpLVItem
->state
;
7203 lvItem
.stateMask
= lpLVItem
->stateMask
;
7204 lvItem
.iItem
= nItem
;
7208 /* apply to all items */
7209 for (lvItem
.iItem
= 0; lvItem
.iItem
< GETITEMCOUNT(infoPtr
); lvItem
.iItem
++)
7210 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) bResult
= FALSE
;
7213 bResult
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
7220 * Sets the text of an item or subitem.
7223 * [I] hwnd : window handle
7224 * [I] nItem : item index
7225 * [I] lpLVItem : item or subitem info
7226 * [I] isW : TRUE if input is Unicode
7232 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
7234 BOOL bResult
= FALSE
;
7237 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n",
7238 nItem
, debuglvitem_t(lpLVItem
, isW
), isW
);
7240 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
7242 ZeroMemory(&lvItem
, sizeof(LVITEMW
));
7243 lvItem
.mask
= LVIF_TEXT
;
7244 lvItem
.pszText
= lpLVItem
->pszText
;
7245 lvItem
.iItem
= nItem
;
7246 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
7247 if(isW
) bResult
= ListView_SetItemW(infoPtr
->hwndSelf
, &lvItem
);
7248 else bResult
= ListView_SetItemA(infoPtr
->hwndSelf
, &lvItem
);
7256 * Set item index that marks the start of a multiple selection.
7259 * [I] infoPtr : valid pointer to the listview structure
7263 * Index number or -1 if there is no selection mark.
7265 static LRESULT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
7267 INT nOldIndex
= infoPtr
->nSelectionMark
;
7269 TRACE("(nIndex=%d)\n", nIndex
);
7271 infoPtr
->nSelectionMark
= nIndex
;
7278 * Sets the text background color.
7281 * [I] infoPtr : valid pointer to the listview structure
7282 * [I] COLORREF : text background color
7288 static LRESULT
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrTextBk
)
7290 TRACE("(clrTextBk=%lx)\n", clrTextBk
);
7292 if (infoPtr
->clrTextBk
!= clrTextBk
)
7294 infoPtr
->clrTextBk
= clrTextBk
;
7295 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
7303 * Sets the text foreground color.
7306 * [I] infoPtr : valid pointer to the listview structure
7307 * [I] COLORREF : text color
7313 static LRESULT
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF clrText
)
7315 TRACE("(clrText=%lx)\n", clrText
);
7317 if (infoPtr
->clrText
!= clrText
)
7319 infoPtr
->clrText
= clrText
;
7320 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
7326 /* LISTVIEW_SetToolTips */
7327 /* LISTVIEW_SetUnicodeFormat */
7328 /* LISTVIEW_SetWorkAreas */
7332 * Callback internally used by LISTVIEW_SortItems()
7335 * [I] LPVOID : first LISTVIEW_ITEM to compare
7336 * [I] LPVOID : second LISTVIEW_ITEM to compare
7337 * [I] LPARAM : HWND of control
7340 * if first comes before second : negative
7341 * if first comes after second : positive
7342 * if first and second are equivalent : zero
7344 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
7346 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW((HWND
)lParam
, 0);
7347 LISTVIEW_ITEM
* lv_first
= (LISTVIEW_ITEM
*) DPA_GetPtr( (HDPA
)first
, 0 );
7348 LISTVIEW_ITEM
* lv_second
= (LISTVIEW_ITEM
*) DPA_GetPtr( (HDPA
)second
, 0 );
7350 /* Forward the call to the client defined callback */
7351 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
7356 * Sorts the listview items.
7359 * [I] infoPtr : valid pointer to the listview structure
7360 * [I] WPARAM : application-defined value
7361 * [I] LPARAM : pointer to comparision callback
7367 static LRESULT
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
, LPARAM lParamSort
)
7369 UINT lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
7371 LISTVIEW_ITEM
*lpItem
;
7372 LPVOID selectionMarkItem
;
7375 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
7377 if (lStyle
& LVS_OWNERDATA
) return FALSE
;
7379 if (!infoPtr
->hdpaItems
) return FALSE
;
7381 /* if there are 0 or 1 items, there is no need to sort */
7382 if (GETITEMCOUNT(infoPtr
) < 2) return TRUE
;
7384 if (infoPtr
->nFocusedItem
>= 0)
7386 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
7387 lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0);
7388 if (lpItem
) lpItem
->state
|= LVIS_FOCUSED
;
7391 infoPtr
->pfnCompare
= pfnCompare
;
7392 infoPtr
->lParamSort
= lParamSort
;
7393 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
->hwndSelf
);
7395 /* Adjust selections and indices so that they are the way they should
7396 * be after the sort (otherwise, the list items move around, but
7397 * whatever is at the item's previous original position will be
7400 selectionMarkItem
=(infoPtr
->nSelectionMark
>=0)?DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
):NULL
;
7401 for (i
=0; i
< GETITEMCOUNT(infoPtr
); i
++)
7403 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
7404 lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0);
7406 if (lpItem
->state
& LVIS_SELECTED
)
7407 LISTVIEW_AddSelectionRange(infoPtr
, i
, i
);
7409 LISTVIEW_RemoveSelectionRange(infoPtr
, i
, i
);
7410 if (lpItem
->state
& LVIS_FOCUSED
)
7412 infoPtr
->nFocusedItem
= i
;
7413 lpItem
->state
&= ~LVIS_FOCUSED
;
7416 if (selectionMarkItem
!= NULL
)
7417 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
7418 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7420 /* align the items */
7421 LISTVIEW_AlignTop(infoPtr
);
7423 /* refresh the display */
7424 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
7429 /* LISTVIEW_SubItemHitTest */
7433 * Updates an items or rearranges the listview control.
7436 * [I] infoPtr : valid pointer to the listview structure
7437 * [I] INT : item index
7443 static LRESULT
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
7445 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
7446 BOOL bResult
= FALSE
;
7449 TRACE("(nItem=%d)\n", nItem
);
7451 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
7455 /* rearrange with default alignment style */
7456 if ((lStyle
& LVS_AUTOARRANGE
) && (((lStyle
& LVS_TYPEMASK
) == LVS_ICON
) ||
7457 ((lStyle
& LVS_TYPEMASK
) == LVS_SMALLICON
)))
7459 ListView_Arrange(infoPtr
->hwndSelf
, 0);
7463 /* get item bounding rectangle */
7464 ListView_GetItemRect(infoPtr
->hwndSelf
, nItem
, &rc
, LVIR_BOUNDS
);
7465 InvalidateRect(infoPtr
->hwndSelf
, &rc
, TRUE
);
7474 * Creates the listview control.
7477 * [I] hwnd : window handle
7478 * [I] lpcs : the create parameters
7484 static LRESULT
LISTVIEW_Create(HWND hwnd
, LPCREATESTRUCTW lpcs
)
7486 LISTVIEW_INFO
*infoPtr
;
7487 UINT uView
= lpcs
->style
& LVS_TYPEMASK
;
7490 TRACE("(lpcs=%p)\n", lpcs
);
7492 /* initialize info pointer */
7493 infoPtr
= (LISTVIEW_INFO
*)COMCTL32_Alloc(sizeof(LISTVIEW_INFO
));
7494 if (!infoPtr
) return -1;
7496 SetWindowLongW(hwnd
, 0, (LONG
)infoPtr
);
7497 ZeroMemory(infoPtr
, sizeof(LISTVIEW_INFO
));
7499 infoPtr
->hwndSelf
= hwnd
;
7500 /* determine the type of structures to use */
7501 infoPtr
->notifyFormat
= SendMessageW(GetParent(infoPtr
->hwndSelf
), WM_NOTIFYFORMAT
,
7502 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
7504 /* initialize color information */
7505 infoPtr
->clrBk
= comctl32_color
.clrWindow
;
7506 infoPtr
->clrText
= comctl32_color
.clrWindowText
;
7507 infoPtr
->clrTextBk
= CLR_DEFAULT
;
7508 infoPtr
->hBkBrush
= CreateSolidBrush(infoPtr
->clrBk
);
7510 /* set default values */
7511 infoPtr
->uCallbackMask
= 0;
7512 infoPtr
->nFocusedItem
= -1;
7513 infoPtr
->nSelectionMark
= -1;
7514 infoPtr
->nHotItem
= -1;
7515 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
7516 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
7517 infoPtr
->hwndEdit
= 0;
7518 infoPtr
->bEditing
= FALSE
;
7519 infoPtr
->nEditLabelItem
= -1;
7520 infoPtr
->bIsDrawing
= FALSE
;
7522 /* get default font (icon title) */
7523 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
7524 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
7525 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
7526 LISTVIEW_SaveTextMetrics(infoPtr
);
7529 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, (LPCWSTR
)NULL
,
7530 WS_CHILD
| HDS_HORZ
| (DWORD
)((LVS_NOSORTHEADER
& lpcs
->style
)?0:HDS_BUTTONS
),
7531 0, 0, 0, 0, hwnd
, (HMENU
)0,
7532 lpcs
->hInstance
, NULL
);
7534 /* set header unicode format */
7535 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
,(WPARAM
)TRUE
,(LPARAM
)NULL
);
7537 /* set header font */
7538 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
,
7541 if (uView
== LVS_ICON
)
7543 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXICON
);
7544 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYICON
);
7546 else if (uView
== LVS_REPORT
)
7548 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
))
7550 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
7554 /* set HDS_HIDDEN flag to hide the header bar */
7555 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
,
7556 GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
) | HDS_HIDDEN
);
7560 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
7561 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
7565 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
7566 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
7569 /* display unsupported listview window styles */
7570 LISTVIEW_UnsupportedStyles(lpcs
->style
);
7572 /* allocate memory for the data structure */
7573 infoPtr
->hdpaItems
= DPA_Create(10);
7575 /* allocate memory for the selection ranges */
7576 infoPtr
->hdpaSelectionRanges
= DPA_Create(10);
7578 /* initialize size of items */
7579 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(infoPtr
);
7580 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(infoPtr
);
7582 /* initialize the hover time to -1(indicating the default system hover time) */
7583 infoPtr
->dwHoverTime
= -1;
7590 * Erases the background of the listview control.
7593 * [I] infoPtr : valid pointer to the listview structure
7594 * [I] WPARAM : device context handle
7595 * [I] LPARAM : not used
7601 static LRESULT
LISTVIEW_EraseBackground(LISTVIEW_INFO
*infoPtr
, WPARAM wParam
,
7606 TRACE("(wParam=%x, lParam=%lx)\n", wParam
, lParam
);
7608 if (infoPtr
->clrBk
== CLR_NONE
)
7609 return SendMessageW(GetParent(infoPtr
->hwndSelf
), WM_ERASEBKGND
, wParam
, lParam
);
7611 GetClientRect(infoPtr
->hwndSelf
, &rc
);
7612 FillRect((HDC
)wParam
, &rc
, infoPtr
->hBkBrush
);
7619 * Helper function for LISTVIEW_[HV]Scroll *only*.
7620 * Performs vertical/horizontal scrolling by a give amount.
7623 * [I] infoPtr : valid pointer to the listview structure
7624 * [I] dx : amount of horizontal scroll
7625 * [I] dy : amount of vertical scroll
7627 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
7629 /* if we have a focus rectagle, we have to erase it before we scroll */
7630 if (infoPtr
->bFocus
) LISTVIEW_ToggleFocusRect(infoPtr
);
7632 /* now we can scroll the list */
7633 ScrollWindowEx( infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
7634 &infoPtr
->rcList
, 0, 0, SW_INVALIDATE
);
7636 /* if we have focus, adjust rect, and redraw the rectangle */
7637 if (infoPtr
->bFocus
)
7639 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
7640 LISTVIEW_ToggleFocusRect(infoPtr
);
7646 * Performs vertical scrolling.
7649 * [I] infoPtr : valid pointer to the listview structure
7650 * [I] nScrollCode : scroll code
7651 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7652 * [I] hScrollWnd : scrollbar control window handle
7658 * SB_LINEUP/SB_LINEDOWN:
7659 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7660 * for LVS_REPORT is 1 line
7661 * for LVS_LIST cannot occur
7664 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
7665 INT nScrollDiff
, HWND hScrollWnd
)
7667 UINT uView
= LISTVIEW_GetType(infoPtr
);
7668 INT nOldScrollPos
, nNewScrollPos
;
7669 SCROLLINFO scrollInfo
;
7672 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode
, nScrollDiff
);
7674 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
7676 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
7677 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7678 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
7680 is_an_icon
= ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
));
7682 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
7684 nOldScrollPos
= scrollInfo
.nPos
;
7685 switch (nScrollCode
)
7691 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
7695 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
7699 nScrollDiff
= -scrollInfo
.nPage
;
7703 nScrollDiff
= scrollInfo
.nPage
;
7706 case SB_THUMBPOSITION
:
7708 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
7715 /* quit right away if pos isn't changing */
7716 if (nScrollDiff
== 0) return 0;
7718 /* calculate new position, and handle overflows */
7719 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
7720 if (nScrollDiff
> 0) {
7721 if (nNewScrollPos
< nOldScrollPos
||
7722 nNewScrollPos
> scrollInfo
.nMax
)
7723 nNewScrollPos
= scrollInfo
.nMax
;
7725 if (nNewScrollPos
> nOldScrollPos
||
7726 nNewScrollPos
< scrollInfo
.nMin
)
7727 nNewScrollPos
= scrollInfo
.nMin
;
7730 /* set the new position, and reread in case it changed */
7731 scrollInfo
.fMask
= SIF_POS
;
7732 scrollInfo
.nPos
= nNewScrollPos
;
7733 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
7735 /* carry on only if it really changed */
7736 if (nNewScrollPos
== nOldScrollPos
) return 0;
7738 /* now adjust to client coordinates */
7739 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
7740 if (uView
== LVS_REPORT
) nScrollDiff
*= infoPtr
->nItemHeight
;
7742 /* and scroll the window */
7743 scroll_list(infoPtr
, 0, nScrollDiff
);
7750 * Performs horizontal scrolling.
7753 * [I] infoPtr : valid pointer to the listview structure
7754 * [I] nScrollCode : scroll code
7755 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7756 * [I] hScrollWnd : scrollbar control window handle
7762 * SB_LINELEFT/SB_LINERIGHT:
7763 * for LVS_ICON, LVS_SMALLICON 1 pixel
7764 * for LVS_REPORT is 1 pixel
7765 * for LVS_LIST is 1 column --> which is a 1 because the
7766 * scroll is based on columns not pixels
7769 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
7770 INT nScrollDiff
, HWND hScrollWnd
)
7772 UINT uView
= LISTVIEW_GetType(infoPtr
);
7773 INT nOldScrollPos
, nNewScrollPos
;
7774 SCROLLINFO scrollInfo
;
7776 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode
, nScrollDiff
);
7778 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
7780 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
7781 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7782 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
7784 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
7786 nOldScrollPos
= scrollInfo
.nPos
;
7788 switch (nScrollCode
)
7802 nScrollDiff
= -scrollInfo
.nPage
;
7806 nScrollDiff
= scrollInfo
.nPage
;
7809 case SB_THUMBPOSITION
:
7811 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
7818 /* quit right away if pos isn't changing */
7819 if (nScrollDiff
== 0) return 0;
7821 /* calculate new position, and handle overflows */
7822 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
7823 if (nScrollDiff
> 0) {
7824 if (nNewScrollPos
< nOldScrollPos
||
7825 nNewScrollPos
> scrollInfo
.nMax
)
7826 nNewScrollPos
= scrollInfo
.nMax
;
7828 if (nNewScrollPos
> nOldScrollPos
||
7829 nNewScrollPos
< scrollInfo
.nMin
)
7830 nNewScrollPos
= scrollInfo
.nMin
;
7833 /* set the new position, and reread in case it changed */
7834 scrollInfo
.fMask
= SIF_POS
;
7835 scrollInfo
.nPos
= nNewScrollPos
;
7836 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
7838 /* carry on only if it really changed */
7839 if (nNewScrollPos
== nOldScrollPos
) return 0;
7841 if(uView
== LVS_REPORT
)
7842 LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
7844 /* now adjust to client coordinates */
7845 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
7846 if (uView
== LVS_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
7848 /* and scroll the window */
7849 scroll_list(infoPtr
, nScrollDiff
, 0);
7854 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
7856 UINT uView
= LISTVIEW_GetType(infoPtr
);
7857 INT gcWheelDelta
= 0;
7858 UINT pulScrollLines
= 3;
7859 SCROLLINFO scrollInfo
;
7861 TRACE("(wheelDelta=%d)\n", wheelDelta
);
7863 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
7864 gcWheelDelta
-= wheelDelta
;
7866 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
7867 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7868 scrollInfo
.fMask
= SIF_POS
| SIF_RANGE
;
7875 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7876 * should be fixed in the future.
7878 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
7879 LISTVIEW_VScroll(infoPtr
, SB_THUMBPOSITION
,
7880 scrollInfo
.nPos
+ (gcWheelDelta
< 0) ?
7881 LISTVIEW_SCROLL_ICON_LINE_SIZE
:
7882 -LISTVIEW_SCROLL_ICON_LINE_SIZE
, 0);
7886 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
7888 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
7890 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
7891 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
7892 LISTVIEW_VScroll(infoPtr
, SB_THUMBPOSITION
, scrollInfo
.nPos
+ cLineScroll
, 0);
7898 LISTVIEW_HScroll(infoPtr
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0, 0);
7909 * [I] infoPtr : valid pointer to the listview structure
7910 * [I] INT : virtual key
7911 * [I] LONG : key data
7916 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
7918 UINT uView
= LISTVIEW_GetType(infoPtr
);
7920 NMLVKEYDOWN nmKeyDown
;
7922 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey
, lKeyData
);
7924 /* send LVN_KEYDOWN notification */
7925 nmKeyDown
.wVKey
= nVirtualKey
;
7926 nmKeyDown
.flags
= 0;
7927 notify(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
7929 switch (nVirtualKey
)
7932 if ((GETITEMCOUNT(infoPtr
) > 0) && (infoPtr
->nFocusedItem
!= -1))
7934 notify_return(infoPtr
);
7935 notify_itemactivate(infoPtr
);
7940 if (GETITEMCOUNT(infoPtr
) > 0)
7945 if (GETITEMCOUNT(infoPtr
) > 0)
7946 nItem
= GETITEMCOUNT(infoPtr
) - 1;
7950 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
7954 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
7958 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
7962 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
7966 if (uView
== LVS_REPORT
)
7967 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
);
7969 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
7970 * LISTVIEW_GetCountPerRow(infoPtr
);
7971 if(nItem
< 0) nItem
= 0;
7975 if (uView
== LVS_REPORT
)
7976 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
);
7978 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
7979 * LISTVIEW_GetCountPerRow(infoPtr
);
7980 if(nItem
>= GETITEMCOUNT(infoPtr
)) nItem
= GETITEMCOUNT(infoPtr
) - 1;
7984 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
))
7985 LISTVIEW_KeySelection(infoPtr
, nItem
);
7995 * [I] infoPtr : valid pointer to the listview structure
8000 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
8004 /* if we did not have the focus, there's nothing to do */
8005 if (!infoPtr
->bFocus
) return 0;
8007 /* send NM_KILLFOCUS notification */
8008 notify_killfocus(infoPtr
);
8010 /* if we have a focus rectagle, get rid of it */
8011 LISTVIEW_ToggleFocusRect(infoPtr
);
8013 /* set window focus flag */
8014 infoPtr
->bFocus
= FALSE
;
8016 /* redraw the selected items */
8017 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8024 * Processes double click messages (left mouse button).
8027 * [I] infoPtr : valid pointer to the listview structure
8028 * [I] wKey : key flag
8029 * [I] pts : mouse coordinate
8034 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, POINTS pts
)
8036 LVHITTESTINFO htInfo
;
8039 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, pts
.x
, pts
.y
);
8041 htInfo
.pt
.x
= pts
.x
;
8042 htInfo
.pt
.y
= pts
.y
;
8044 /* send NM_DBLCLK notification */
8045 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
8046 if (LISTVIEW_HitTestItem(infoPtr
, &htInfo
, TRUE
) != -1)
8048 nmlv
.iItem
= htInfo
.iItem
;
8049 nmlv
.iSubItem
= htInfo
.iSubItem
;
8056 nmlv
.ptAction
.x
= pts
.x
;
8057 nmlv
.ptAction
.y
= pts
.y
;
8058 listview_notify(infoPtr
, NM_DBLCLK
, &nmlv
);
8061 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8062 if(nmlv
.iItem
!= -1)
8063 notify_itemactivate(infoPtr
);
8070 * Processes mouse down messages (left mouse button).
8073 * [I] infoPtr : valid pointer to the listview structure
8074 * [I] wKey : key flag
8075 * [I] pts : mouse coordinate
8080 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, POINTS pts
)
8082 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
8083 static BOOL bGroupSelect
= TRUE
;
8084 POINT pt
= { pts
.x
, pts
.y
};
8087 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, pts
.x
, pts
.y
);
8089 /* send NM_RELEASEDCAPTURE notification */
8090 notify_releasedcapture(infoPtr
);
8092 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
8094 /* set left button down flag */
8095 infoPtr
->bLButtonDown
= TRUE
;
8097 nItem
= LISTVIEW_GetItemAtPt(infoPtr
, pt
);
8098 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
8100 if (lStyle
& LVS_SINGLESEL
)
8102 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) & LVIS_SELECTED
)
8103 && infoPtr
->nEditLabelItem
== -1)
8104 infoPtr
->nEditLabelItem
= nItem
;
8106 LISTVIEW_SetSelection(infoPtr
, nItem
);
8110 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
8113 LISTVIEW_AddGroupSelection(infoPtr
, nItem
);
8115 LISTVIEW_AddSelection(infoPtr
, nItem
);
8117 else if (wKey
& MK_CONTROL
)
8121 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
8123 ZeroMemory(&item
, sizeof(item
));
8124 item
.stateMask
= LVIS_SELECTED
;
8125 if(bGroupSelect
) item
.state
= LVIS_SELECTED
;
8126 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
8128 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8129 infoPtr
->nSelectionMark
= nItem
;
8131 else if (wKey
& MK_SHIFT
)
8133 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
8138 (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) & LVIS_SELECTED
);
8140 /* set selection (clears other pre-existing selections) */
8141 LISTVIEW_SetSelection(infoPtr
, nItem
);
8143 if (was_selected
&& infoPtr
->nEditLabelItem
== -1)
8144 infoPtr
->nEditLabelItem
= nItem
;
8150 /* remove all selections */
8151 LISTVIEW_RemoveAllSelections(infoPtr
);
8154 /* redraw if we could have possibly selected something */
8155 if(!GETITEMCOUNT(infoPtr
)) InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
8162 * Processes mouse up messages (left mouse button).
8165 * [I] infoPtr : valid pointer to the listview structure
8166 * [I] wKey : key flag
8167 * [I] pts : mouse coordinate
8172 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, POINTS pts
)
8174 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, pts
.x
, pts
.y
);
8176 if (infoPtr
->bLButtonDown
)
8178 LVHITTESTINFO lvHitTestInfo
;
8181 lvHitTestInfo
.pt
.x
= pts
.x
;
8182 lvHitTestInfo
.pt
.y
= pts
.y
;
8184 /* send NM_CLICK notification */
8185 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
8186 if (LISTVIEW_HitTestItem(infoPtr
, &lvHitTestInfo
, TRUE
) != -1)
8188 nmlv
.iItem
= lvHitTestInfo
.iItem
;
8189 nmlv
.iSubItem
= lvHitTestInfo
.iSubItem
;
8196 nmlv
.ptAction
.x
= pts
.x
;
8197 nmlv
.ptAction
.y
= pts
.y
;
8198 listview_notify(infoPtr
, NM_CLICK
, &nmlv
);
8200 /* set left button flag */
8201 infoPtr
->bLButtonDown
= FALSE
;
8203 if(infoPtr
->nEditLabelItem
!= -1)
8205 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
) {
8206 LISTVIEW_EditLabelT(infoPtr
, lvHitTestInfo
.iItem
, TRUE
);
8208 infoPtr
->nEditLabelItem
= -1;
8217 * Destroys the listview control (called after WM_DESTROY).
8220 * [I] infoPtr : valid pointer to the listview structure
8225 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
8227 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
8231 /* delete all items */
8232 LISTVIEW_DeleteAllItems(infoPtr
);
8234 /* destroy data structure */
8235 DPA_Destroy(infoPtr
->hdpaItems
);
8236 DPA_Destroy(infoPtr
->hdpaSelectionRanges
);
8238 /* destroy image lists */
8239 if (!(lStyle
& LVS_SHAREIMAGELISTS
))
8242 /* FIXME: If the caller does a ImageList_Destroy and then we
8243 * do this code the area will be freed twice. Currently
8244 * this generates an "err:heap:HEAP_ValidateInUseArena
8245 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
8246 * has PREV_FREE flag" sometimes.
8248 * We will leak the memory till we figure out how to fix
8250 if (infoPtr
->himlNormal
)
8251 ImageList_Destroy(infoPtr
->himlNormal
);
8252 if (infoPtr
->himlSmall
)
8253 ImageList_Destroy(infoPtr
->himlSmall
);
8254 if (infoPtr
->himlState
)
8255 ImageList_Destroy(infoPtr
->himlState
);
8259 /* destroy font, bkgnd brush */
8261 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
8262 if (infoPtr
->hBkBrush
) DeleteObject(infoPtr
->hBkBrush
);
8264 /* free listview info pointer*/
8265 COMCTL32_Free(infoPtr
);
8267 SetWindowLongW(infoPtr
->hwndSelf
, 0, 0);
8273 * Handles notifications from children.
8276 * [I] infoPtr : valid pointer to the listview structure
8277 * [I] INT : control identifier
8278 * [I] LPNMHDR : notification information
8283 static LRESULT
LISTVIEW_Notify(LISTVIEW_INFO
*infoPtr
, INT nCtrlId
, LPNMHDR lpnmh
)
8285 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId
, lpnmh
);
8287 if (lpnmh
->hwndFrom
== infoPtr
->hwndHeader
)
8289 /* handle notification from header control */
8290 if (lpnmh
->code
== HDN_ENDTRACKW
)
8292 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(infoPtr
);
8293 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
8295 else if(lpnmh
->code
== HDN_ITEMCLICKW
|| lpnmh
->code
== HDN_ITEMCLICKA
)
8297 /* Handle sorting by Header Column */
8300 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
8302 nmlv
.iSubItem
= ((LPNMHEADERW
)lpnmh
)->iItem
;
8303 listview_notify(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
8305 else if(lpnmh
->code
== NM_RELEASEDCAPTURE
)
8307 /* Idealy this should be done in HDN_ENDTRACKA
8308 * but since SetItemBounds in Header.c is called after
8309 * the notification is sent, it is neccessary to handle the
8310 * update of the scroll bar here (Header.c works fine as it is,
8311 * no need to disturb it)
8313 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(infoPtr
);
8314 LISTVIEW_UpdateScroll(infoPtr
);
8315 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
8325 * Determines the type of structure to use.
8328 * [I] infoPtr : valid pointer to the listview structureof the sender
8329 * [I] HWND : listview window handle
8330 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8335 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
8337 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom
, nCommand
);
8339 if (nCommand
== NF_REQUERY
)
8340 infoPtr
->notifyFormat
= SendMessageW(hwndFrom
, WM_NOTIFYFORMAT
,
8341 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
8347 * Paints/Repaints the listview control.
8350 * [I] infoPtr : valid pointer to the listview structure
8351 * [I] HDC : device context handle
8356 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
8360 TRACE("(hdc=%x)\n", hdc
);
8364 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
8365 TRACE("ps erase=%s, rect=(%d,%d)-(%d,%d)\n",
8366 ps
.fErase
? "TRUE" : "FALSE",
8367 ps
.rcPaint
.left
, ps
.rcPaint
.top
, ps
.rcPaint
.right
, ps
.rcPaint
.bottom
);
8369 LISTVIEW_EraseBackground(infoPtr
, (WPARAM
)hdc
, 0);
8370 LISTVIEW_Refresh(infoPtr
, hdc
);
8371 EndPaint(infoPtr
->hwndSelf
, &ps
);
8375 LISTVIEW_Refresh(infoPtr
, hdc
);
8383 * Processes double click messages (right mouse button).
8386 * [I] infoPtr : valid pointer to the listview structure
8387 * [I] wKey : key flag
8388 * [I] pts : mouse coordinate
8393 static LRESULT
LISTVIEW_RButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, POINTS pts
)
8395 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, pts
.x
, pts
.y
);
8397 /* send NM_RELEASEDCAPTURE notification */
8398 notify_releasedcapture(infoPtr
);
8400 /* send NM_RDBLCLK notification */
8401 notify_rdblclk(infoPtr
);
8408 * Processes mouse down messages (right mouse button).
8411 * [I] infoPtr : valid pointer to the listview structure
8412 * [I] wKey : key flag
8413 * [I] pts : mouse coordinate
8418 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, POINTS pts
)
8422 LVHITTESTINFO lvHitTestInfo
;
8423 POINT pt
= { pts
.x
, pts
.y
};
8425 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, pts
.x
, pts
.y
);
8427 /* send NM_RELEASEDCAPTURE notification */
8428 notify_releasedcapture(infoPtr
);
8430 /* make sure the listview control window has the focus */
8431 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
8433 /* set right button down flag */
8434 infoPtr
->bRButtonDown
= TRUE
;
8436 /* determine the index of the selected item */
8437 nItem
= LISTVIEW_GetItemAtPt(infoPtr
, pt
);
8438 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
8440 LISTVIEW_SetItemFocus(infoPtr
,nItem
);
8441 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
8442 !LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8443 LISTVIEW_SetSelection(infoPtr
, nItem
);
8447 LISTVIEW_RemoveAllSelections(infoPtr
);
8450 lvHitTestInfo
.pt
.x
= pts
.x
;
8451 lvHitTestInfo
.pt
.y
= pts
.y
;
8453 /* Send NM_RClICK notification */
8454 ZeroMemory(&nmlv
, sizeof(nmlv
));
8455 if (LISTVIEW_HitTestItem(infoPtr
, &lvHitTestInfo
, TRUE
) != -1)
8457 nmlv
.iItem
= lvHitTestInfo
.iItem
;
8458 nmlv
.iSubItem
= lvHitTestInfo
.iSubItem
;
8465 nmlv
.ptAction
.x
= pts
.x
;
8466 nmlv
.ptAction
.y
= pts
.y
;
8467 listview_notify(infoPtr
, NM_RCLICK
, &nmlv
);
8474 * Processes mouse up messages (right mouse button).
8477 * [I] infoPtr : valid pointer to the listview structure
8478 * [I] wKey : key flag
8479 * [I] pts : mouse coordinate
8484 static LRESULT
LISTVIEW_RButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, POINTS pts
)
8486 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, pts
.x
, pts
.y
);
8488 if (infoPtr
->bRButtonDown
)
8490 POINT pt
= { pts
.x
, pts
.y
};
8492 /* set button flag */
8493 infoPtr
->bRButtonDown
= FALSE
;
8495 /* Change to screen coordinate for WM_CONTEXTMENU */
8496 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
8498 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8499 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
8500 (WPARAM
)infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
8512 * [I] infoPtr : valid pointer to the listview structure
8513 * [I] hwnd : window handle of window containing the cursor
8514 * [I] nHittest : hit-test code
8515 * [I] wMouseMsg : ideintifier of the mouse message
8518 * TRUE if cursor is set
8521 static BOOL
LISTVIEW_SetCursor(LISTVIEW_INFO
*infoPtr
, HWND hwnd
, UINT nHittest
, UINT wMouseMsg
)
8525 if(!(infoPtr
->dwExStyle
& LVS_EX_TRACKSELECT
)) return FALSE
;
8527 if(!infoPtr
->hHotCursor
) return FALSE
;
8530 if (LISTVIEW_GetItemAtPt(infoPtr
, pt
) < 0) return FALSE
;
8532 SetCursor(infoPtr
->hHotCursor
);
8542 * [I] infoPtr : valid pointer to the listview structure
8543 * [I] infoPtr : handle of previously focused window
8548 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
8550 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus
);
8552 /* if we have the focus already, there's nothing to do */
8553 if (infoPtr
->bFocus
) return 0;
8555 /* send NM_SETFOCUS notification */
8556 notify_setfocus(infoPtr
);
8558 /* put the focus rect back on */
8559 LISTVIEW_ToggleFocusRect(infoPtr
);
8561 /* set window focus flag */
8562 infoPtr
->bFocus
= TRUE
;
8564 /* redraw all visible selected items */
8565 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8575 * [I] infoPtr : valid pointer to the listview structure
8576 * [I] HFONT : font handle
8577 * [I] WORD : redraw flag
8582 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
8584 UINT uView
= LISTVIEW_GetType(infoPtr
);
8586 TRACE("(hfont=%x,redraw=%hu)\n", hFont
, fRedraw
);
8588 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
8589 LISTVIEW_SaveTextMetrics(infoPtr
);
8591 if (uView
== LVS_REPORT
)
8593 /* set header font */
8594 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
,
8595 MAKELPARAM(fRedraw
, 0));
8598 /* invalidate listview control client area */
8599 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
8601 if (fRedraw
) UpdateWindow(infoPtr
->hwndSelf
);
8608 * Message handling for WM_SETREDRAW.
8609 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8612 * [I] infoPtr : valid pointer to the listview structure
8613 * [I] bRedraw: state of redraw flag
8616 * DefWinProc return value
8618 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
8620 LRESULT lResult
= DefWindowProcW(infoPtr
->hwndSelf
, WM_SETREDRAW
, bRedraw
, 0);
8622 RedrawWindow(infoPtr
->hwndSelf
, NULL
, 0,
8623 RDW_INVALIDATE
| RDW_FRAME
| RDW_ERASE
| RDW_ALLCHILDREN
| RDW_ERASENOW
);
8629 * Resizes the listview control. This function processes WM_SIZE
8630 * messages. At this time, the width and height are not used.
8633 * [I] infoPtr : valid pointer to the listview structure
8634 * [I] WORD : new width
8635 * [I] WORD : new height
8640 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
8642 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
8643 UINT uView
= lStyle
& LVS_TYPEMASK
;
8645 TRACE("(width=%d, height=%d)\n", Width
, Height
);
8647 if (LISTVIEW_UpdateSize(infoPtr
))
8649 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
8651 if (lStyle
& LVS_ALIGNLEFT
)
8652 LISTVIEW_AlignLeft(infoPtr
);
8654 LISTVIEW_AlignTop(infoPtr
);
8657 LISTVIEW_UpdateScroll(infoPtr
);
8659 /* invalidate client area + erase background */
8660 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
8668 * Sets the size information.
8671 * [I] infoPtr : valid pointer to the listview structure
8674 * Zero if no size change
8677 static BOOL
LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
8679 LONG lStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
8680 UINT uView
= lStyle
& LVS_TYPEMASK
;
8684 GetClientRect(infoPtr
->hwndSelf
, &rcList
);
8685 CopyRect(&rcOld
,&(infoPtr
->rcList
));
8686 infoPtr
->rcList
.left
= 0;
8687 infoPtr
->rcList
.right
= max(rcList
.right
- rcList
.left
, 1);
8688 infoPtr
->rcList
.top
= 0;
8689 infoPtr
->rcList
.bottom
= max(rcList
.bottom
- rcList
.top
, 1);
8691 if (uView
== LVS_LIST
)
8693 /* Apparently the "LIST" style is supposed to have the same
8694 * number of items in a column even if there is no scroll bar.
8695 * Since if a scroll bar already exists then the bottom is already
8696 * reduced, only reduce if the scroll bar does not currently exist.
8697 * The "2" is there to mimic the native control. I think it may be
8698 * related to either padding or edges. (GLA 7/2002)
8700 if (!(lStyle
& WS_HSCROLL
))
8702 INT nHScrollHeight
= GetSystemMetrics(SM_CYHSCROLL
);
8703 if (infoPtr
->rcList
.bottom
> nHScrollHeight
)
8704 infoPtr
->rcList
.bottom
-= (nHScrollHeight
+ 2);
8708 if (infoPtr
->rcList
.bottom
> 2)
8709 infoPtr
->rcList
.bottom
-= 2;
8712 else if (uView
== LVS_REPORT
)
8719 Header_Layout(infoPtr
->hwndHeader
, &hl
);
8721 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
8723 if (!(LVS_NOCOLUMNHEADER
& lStyle
))
8724 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
8726 return (EqualRect(&rcOld
,&(infoPtr
->rcList
)));
8731 * Processes WM_STYLECHANGED messages.
8734 * [I] infoPtr : valid pointer to the listview structure
8735 * [I] WPARAM : window style type (normal or extended)
8736 * [I] LPSTYLESTRUCT : window style information
8741 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
8744 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
8745 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
8746 RECT rcList
= infoPtr
->rcList
;
8748 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8749 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
8751 if (wStyleType
== GWL_STYLE
)
8753 if (uOldView
== LVS_REPORT
)
8754 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
8756 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
8757 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
8758 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
8760 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
8761 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
8762 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
8764 /* If switching modes, then start with no scroll bars and then
8767 if (uNewView
!= uOldView
)
8768 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
8770 if (uNewView
== LVS_ICON
)
8774 /* First readjust the iconSize and if necessary the iconSpacing */
8775 oldcx
= infoPtr
->iconSize
.cx
;
8776 oldcy
= infoPtr
->iconSize
.cy
;
8777 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXICON
);
8778 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYICON
);
8779 if (infoPtr
->himlNormal
!= NULL
)
8782 ImageList_GetIconSize(infoPtr
->himlNormal
, &cx
, &cy
);
8783 infoPtr
->iconSize
.cx
= cx
;
8784 infoPtr
->iconSize
.cy
= cy
;
8786 if ((infoPtr
->iconSize
.cx
!= oldcx
) || (infoPtr
->iconSize
.cy
!= oldcy
))
8788 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8789 oldcx
, oldcy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
8790 LISTVIEW_SetIconSpacing(infoPtr
,0);
8793 /* Now update the full item width and height */
8794 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(infoPtr
);
8795 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(infoPtr
);
8796 if (lpss
->styleNew
& LVS_ALIGNLEFT
)
8797 LISTVIEW_AlignLeft(infoPtr
);
8799 LISTVIEW_AlignTop(infoPtr
);
8801 else if (uNewView
== LVS_REPORT
)
8808 Header_Layout(infoPtr
->hwndHeader
, &hl
);
8809 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
8811 if (!(LVS_NOCOLUMNHEADER
& lpss
->styleNew
))
8812 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
8814 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
8815 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
8816 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(infoPtr
);
8817 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(infoPtr
);
8819 else if (uNewView
== LVS_LIST
)
8821 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
8822 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
8823 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(infoPtr
);
8824 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(infoPtr
);
8828 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
8829 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
8830 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(infoPtr
);
8831 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(infoPtr
);
8832 if (lpss
->styleNew
& LVS_ALIGNLEFT
)
8833 LISTVIEW_AlignLeft(infoPtr
);
8835 LISTVIEW_AlignTop(infoPtr
);
8838 /* update the size of the client area */
8839 LISTVIEW_UpdateSize(infoPtr
);
8841 /* add scrollbars if needed */
8842 LISTVIEW_UpdateScroll(infoPtr
);
8844 /* invalidate client area + erase background */
8845 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
8847 /* print the list of unsupported window styles */
8848 LISTVIEW_UnsupportedStyles(lpss
->styleNew
);
8851 /* If they change the view and we have an active edit control
8852 we will need to kill the control since the redraw will
8853 misplace the edit control.
8855 if (infoPtr
->bEditing
&&
8856 ((uNewView
& (LVS_ICON
|LVS_LIST
|LVS_SMALLICON
)) !=
8857 ((LVS_ICON
|LVS_LIST
|LVS_SMALLICON
) & uOldView
)))
8859 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8867 * Window procedure of the listview control.
8870 static LRESULT WINAPI
8871 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
8873 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8875 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg
, wParam
, lParam
);
8877 if (!infoPtr
&& (uMsg
!= WM_CREATE
))
8878 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
8882 case LVM_APPROXIMATEVIEWRECT
:
8883 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
8884 LOWORD(lParam
), HIWORD(lParam
));
8886 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
8888 /* case LVN_CANCELEDITLABEL */
8890 /* case LVM_CREATEDRAGIMAGE: */
8892 case LVM_DELETEALLITEMS
:
8893 return LISTVIEW_DeleteAllItems(infoPtr
);
8895 case LVM_DELETECOLUMN
:
8896 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
8898 case LVM_DELETEITEM
:
8899 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
8901 case LVM_EDITLABELW
:
8902 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, TRUE
);
8904 case LVM_EDITLABELA
:
8905 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, FALSE
);
8907 /* case LVN_ENABLEGROUPVIEW: */
8909 case LVM_ENSUREVISIBLE
:
8910 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
8913 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
8916 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
8918 case LVM_GETBKCOLOR
:
8919 return infoPtr
->clrBk
;
8921 /* case LVM_GETBKIMAGE: */
8923 case LVM_GETCALLBACKMASK
:
8924 return infoPtr
->uCallbackMask
;
8926 case LVM_GETCOLUMNA
:
8927 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
8929 case LVM_GETCOLUMNW
:
8930 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
8932 case LVM_GETCOLUMNORDERARRAY
:
8933 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
8935 case LVM_GETCOLUMNWIDTH
:
8936 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
8938 case LVM_GETCOUNTPERPAGE
:
8939 return LISTVIEW_GetCountPerPage(infoPtr
);
8941 case LVM_GETEDITCONTROL
:
8942 return (LRESULT
)infoPtr
->hwndEdit
;
8944 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
8945 return infoPtr
->dwExStyle
;
8948 return (LRESULT
)infoPtr
->hwndHeader
;
8950 case LVM_GETHOTCURSOR
:
8951 return infoPtr
->hHotCursor
;
8953 case LVM_GETHOTITEM
:
8954 return infoPtr
->nHotItem
;
8956 case LVM_GETHOVERTIME
:
8957 return infoPtr
->dwHoverTime
;
8959 case LVM_GETIMAGELIST
:
8960 return LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
8962 /* case LVN_GETINSERTMARK: */
8964 /* case LVN_GETINSERTMARKCOLOR: */
8966 /* case LVN_GETINSERTMARKRECT: */
8968 case LVM_GETISEARCHSTRINGA
:
8969 case LVM_GETISEARCHSTRINGW
:
8970 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8974 return LISTVIEW_GetItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
, FALSE
);
8977 return LISTVIEW_GetItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
, TRUE
);
8979 case LVM_GETITEMCOUNT
:
8980 return GETITEMCOUNT(infoPtr
);
8982 case LVM_GETITEMPOSITION
:
8983 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
8985 case LVM_GETITEMRECT
:
8986 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
8988 case LVM_GETITEMSPACING
:
8989 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
8991 case LVM_GETITEMSTATE
:
8992 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
8994 case LVM_GETITEMTEXTA
:
8995 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
8997 case LVM_GETITEMTEXTW
:
8998 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9000 case LVM_GETNEXTITEM
:
9001 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
9003 case LVM_GETNUMBEROFWORKAREAS
:
9004 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9008 return LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
9010 /* case LVN_GETOUTLINECOLOR: */
9012 /* case LVM_GETSELECTEDCOLUMN: */
9014 case LVM_GETSELECTEDCOUNT
:
9015 return LISTVIEW_GetSelectedCount(infoPtr
);
9017 case LVM_GETSELECTIONMARK
:
9018 return infoPtr
->nSelectionMark
;
9020 case LVM_GETSTRINGWIDTHA
:
9021 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, FALSE
);
9023 case LVM_GETSTRINGWIDTHW
:
9024 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, TRUE
);
9026 case LVM_GETSUBITEMRECT
:
9027 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, ((LPRECT
)lParam
)->top
,
9028 ((LPRECT
)lParam
)->left
, (LPRECT
)lParam
);
9030 case LVM_GETTEXTBKCOLOR
:
9031 return LISTVIEW_GetTextBkColor(infoPtr
);
9033 case LVM_GETTEXTCOLOR
:
9034 return LISTVIEW_GetTextColor(infoPtr
);
9036 /* case LVN_GETTILEINFO: */
9038 /* case LVN_GETTILEVIEWINFO: */
9040 case LVM_GETTOOLTIPS
:
9041 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9044 case LVM_GETTOPINDEX
:
9045 return LISTVIEW_GetTopIndex(infoPtr
);
9047 /*case LVM_GETUNICODEFORMAT:
9048 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9051 case LVM_GETVIEWRECT
:
9052 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
9054 case LVM_GETWORKAREAS
:
9055 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9058 /* case LVN_HASGROUP: */
9061 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
);
9063 case LVM_INSERTCOLUMNA
:
9064 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9066 case LVM_INSERTCOLUMNW
:
9067 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9069 /* case LVN_INSERTGROUP: */
9071 /* case LVN_INSERTGROUPSORTED: */
9073 case LVM_INSERTITEMA
:
9074 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9076 case LVM_INSERTITEMW
:
9077 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9079 /* case LVN_INSERTMARKHITTEST: */
9081 /* case LVN_ISGROUPVIEWENABLED: */
9083 /* case LVN_MAPIDTOINDEX: */
9085 /* case LVN_INEDXTOID: */
9087 /* case LVN_MOVEGROUP: */
9089 /* case LVN_MOVEITEMTOGROUP: */
9091 case LVM_REDRAWITEMS
:
9092 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9094 /* case LVN_REMOVEALLGROUPS: */
9096 /* case LVN_REMOVEGROUP: */
9099 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9101 case LVM_SETBKCOLOR
:
9102 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
9104 /* case LVM_SETBKIMAGE: */
9106 case LVM_SETCALLBACKMASK
:
9107 infoPtr
->uCallbackMask
= (UINT
)wParam
;
9110 case LVM_SETCOLUMNA
:
9111 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9113 case LVM_SETCOLUMNW
:
9114 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9116 case LVM_SETCOLUMNORDERARRAY
:
9117 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9119 case LVM_SETCOLUMNWIDTH
:
9120 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, SLOWORD(lParam
));
9122 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
9123 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
9125 /* case LVN_SETGROUPINFO: */
9127 /* case LVN_SETGROUPMETRICS: */
9129 case LVM_SETHOTCURSOR
:
9130 return LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
9132 case LVM_SETHOTITEM
:
9133 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
9135 case LVM_SETHOVERTIME
:
9136 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)wParam
);
9138 case LVM_SETICONSPACING
:
9139 return LISTVIEW_SetIconSpacing(infoPtr
, (DWORD
)lParam
);
9141 case LVM_SETIMAGELIST
:
9142 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
9144 /* case LVN_SETINFOTIP: */
9146 /* case LVN_SETINSERTMARK: */
9148 /* case LVN_SETINSERTMARKCOLOR: */
9151 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9154 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9156 case LVM_SETITEMCOUNT
:
9157 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
9159 case LVM_SETITEMPOSITION
:
9160 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, (INT
)LOWORD(lParam
),
9161 (INT
)HIWORD(lParam
));
9163 case LVM_SETITEMPOSITION32
:
9164 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, ((POINT
*)lParam
)->x
,
9165 ((POINT
*)lParam
)->y
);
9167 case LVM_SETITEMSTATE
:
9168 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
9170 case LVM_SETITEMTEXTA
:
9171 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9173 case LVM_SETITEMTEXTW
:
9174 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9176 /* case LVN_SETOUTLINECOLOR: */
9178 /* case LVN_SETSELECTEDCOLUMN: */
9180 case LVM_SETSELECTIONMARK
:
9181 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
9183 case LVM_SETTEXTBKCOLOR
:
9184 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
9186 case LVM_SETTEXTCOLOR
:
9187 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
9189 /* case LVN_SETTILEINFO: */
9191 /* case LVN_SETTILEVIEWINFO: */
9193 /* case LVN_SETTILEWIDTH: */
9195 /* case LVM_SETTOOLTIPS: */
9197 /* case LVM_SETUNICODEFORMAT: */
9199 /* case LVN_SETVIEW: */
9201 /* case LVM_SETWORKAREAS: */
9203 /* case LVN_SORTGROUPS: */
9206 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
);
9208 case LVM_SUBITEMHITTEST
:
9209 return LISTVIEW_SubItemHitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
);
9212 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
9215 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
9218 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
9221 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
9224 return LISTVIEW_EraseBackground(infoPtr
, wParam
, lParam
);
9227 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
9230 return infoPtr
->hFont
;
9233 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
9236 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
9239 return LISTVIEW_KillFocus(infoPtr
);
9241 case WM_LBUTTONDBLCLK
:
9242 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
9244 case WM_LBUTTONDOWN
:
9245 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
9248 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
9251 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
9254 return LISTVIEW_MouseHover(infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
9257 return LISTVIEW_NCDestroy(infoPtr
);
9260 return LISTVIEW_Notify(infoPtr
, (INT
)wParam
, (LPNMHDR
)lParam
);
9262 case WM_NOTIFYFORMAT
:
9263 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
9266 return LISTVIEW_Paint(infoPtr
, (HDC
)wParam
);
9268 case WM_RBUTTONDBLCLK
:
9269 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
9271 case WM_RBUTTONDOWN
:
9272 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
9275 return LISTVIEW_RButtonUp(infoPtr
, (WORD
)wParam
, MAKEPOINTS(lParam
));
9278 return LISTVIEW_SetCursor(infoPtr
, (HWND
)wParam
, LOWORD(lParam
), HIWORD(lParam
));
9281 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
9284 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
9287 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
9290 return LISTVIEW_Size(infoPtr
, (int)SLOWORD(lParam
), (int)SHIWORD(lParam
));
9292 case WM_STYLECHANGED
:
9293 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
9295 case WM_SYSCOLORCHANGE
:
9296 COMCTL32_RefreshSysColors();
9299 /* case WM_TIMER: */
9302 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
9305 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
9306 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9307 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
9309 case WM_WINDOWPOSCHANGED
:
9310 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
)) {
9311 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
9312 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
9313 LISTVIEW_UpdateSize(infoPtr
);
9314 LISTVIEW_UpdateScroll(infoPtr
);
9316 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9318 /* case WM_WININICHANGE: */
9321 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
))
9322 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg
, wParam
, lParam
);
9324 /* call default window procedure */
9325 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9333 * Registers the window class.
9341 void LISTVIEW_Register(void)
9345 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
9346 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
9347 wndClass
.lpfnWndProc
= (WNDPROC
)LISTVIEW_WindowProc
;
9348 wndClass
.cbClsExtra
= 0;
9349 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
9350 wndClass
.hCursor
= LoadCursorW(0, IDC_ARROWW
);
9351 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
9352 wndClass
.lpszClassName
= WC_LISTVIEWW
;
9353 RegisterClassW(&wndClass
);
9358 * Unregisters the window class.
9366 void LISTVIEW_Unregister(void)
9368 UnregisterClassW(WC_LISTVIEWW
, (HINSTANCE
)NULL
);
9373 * Handle any WM_COMMAND messages
9379 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
9381 switch (HIWORD(wParam
))
9386 * Adjust the edit window size
9389 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
9390 HFONT hFont
, hOldFont
= 0;
9395 len
= GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
9396 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
9398 /* Select font to get the right dimension of the string */
9399 hFont
= SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
9402 hOldFont
= SelectObject(hdc
, hFont
);
9405 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
9407 TEXTMETRICW textMetric
;
9409 /* Add Extra spacing for the next character */
9410 GetTextMetricsW(hdc
, &textMetric
);
9411 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
9419 rect
.bottom
- rect
.top
,
9420 SWP_DRAWFRAME
|SWP_NOMOVE
);
9423 SelectObject(hdc
, hOldFont
);
9425 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
9431 return SendMessageW (GetParent (infoPtr
->hwndSelf
), WM_COMMAND
, wParam
, lParam
);
9440 * Subclassed edit control windproc function
9446 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
,
9447 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
9449 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(GetParent(hwnd
), 0);
9450 static BOOL bIgnoreKillFocus
= FALSE
;
9451 BOOL cancel
= FALSE
;
9453 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9454 hwnd
, uMsg
, wParam
, lParam
, isW
);
9459 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
9462 if(bIgnoreKillFocus
) return TRUE
;
9467 WNDPROC editProc
= infoPtr
->EditWndProc
;
9468 infoPtr
->EditWndProc
= 0;
9469 SetWindowLongW(hwnd
, GWL_WNDPROC
, (LONG
)editProc
);
9470 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
9474 if (VK_ESCAPE
== (INT
)wParam
)
9479 else if (VK_RETURN
== (INT
)wParam
)
9483 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
9486 if (infoPtr
->bEditing
)
9488 LPWSTR buffer
= NULL
;
9492 DWORD len
= isW
? GetWindowTextLengthW(hwnd
) : GetWindowTextLengthA(hwnd
);
9496 if ( (buffer
= COMCTL32_Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))) )
9498 if (isW
) GetWindowTextW(hwnd
, buffer
, len
+1);
9499 else GetWindowTextA(hwnd
, (CHAR
*)buffer
, len
+1);
9503 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9504 /* eg. Using a messagebox */
9505 bIgnoreKillFocus
= TRUE
;
9506 LISTVIEW_EndEditLabelT(infoPtr
, buffer
, isW
);
9508 if (buffer
) COMCTL32_Free(buffer
);
9510 bIgnoreKillFocus
= FALSE
;
9513 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
9519 * Subclassed edit control windproc function
9525 LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9527 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
9532 * Subclassed edit control windproc function
9538 LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9540 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
9545 * Creates a subclassed edit cotrol
9551 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, DWORD style
,
9552 INT x
, INT y
, INT width
, INT height
, BOOL isW
)
9554 WCHAR editName
[5] = { 'E', 'd', 'i', 't', '\0' };
9559 TEXTMETRICW textMetric
;
9560 HINSTANCE hinst
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_HINSTANCE
);
9562 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text
, isW
), isW
);
9564 style
|= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|WS_BORDER
;
9565 hdc
= GetDC(infoPtr
->hwndSelf
);
9567 /* Select the font to get appropriate metric dimensions */
9568 if(infoPtr
->hFont
!= 0)
9569 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
9571 /*Get String Lenght in pixels */
9572 GetTextExtentPoint32W(hdc
, text
, lstrlenW(text
), &sz
);
9574 /*Add Extra spacing for the next character */
9575 GetTextMetricsW(hdc
, &textMetric
);
9576 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
9578 if(infoPtr
->hFont
!= 0)
9579 SelectObject(hdc
, hOldFont
);
9581 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
9583 hedit
= CreateWindowW(editName
, text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
9585 hedit
= CreateWindowA("Edit", (LPCSTR
)text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
9587 if (!hedit
) return 0;
9589 infoPtr
->EditWndProc
= (WNDPROC
)
9590 (isW
? SetWindowLongW(hedit
, GWL_WNDPROC
, (LONG
)EditLblWndProcW
) :
9591 SetWindowLongA(hedit
, GWL_WNDPROC
, (LONG
)EditLblWndProcA
) );
9593 SendMessageW(hedit
, WM_SETFONT
, infoPtr
->hFont
, FALSE
);