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_GetHotCursor : not implemented
41 * LISTVIEW_GetISearchString : not implemented
42 * LISTVIEW_GetBkImage : not implemented
43 * LISTVIEW_SetBkImage : not implemented
44 * LISTVIEW_GetColumnOrderArray : simple hack only
45 * LISTVIEW_SetColumnOrderArray : simple hack only
46 * LISTVIEW_Arrange : empty stub
47 * LISTVIEW_ApproximateViewRect : incomplete
48 * LISTVIEW_Update : not completed
50 * Known differences in message stream from native control (not known if
51 * these differences cause problems):
52 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
53 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
54 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
55 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
56 * does *not* invoke DefWindowProc
57 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
58 * processing for "USEDOUBLECLICKTIME".
63 #include "wine/port.h"
75 #include "wine/debug.h"
77 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
79 /* Some definitions for inline edit control */
80 typedef BOOL (*EditlblCallbackW
)(HWND
, LPWSTR
, DWORD
);
81 typedef BOOL (*EditlblCallbackA
)(HWND
, LPWSTR
, DWORD
);
83 typedef struct tagLV_INTHIT
86 DWORD distance
; /* distance to closest item */
87 INT iDistItem
; /* item number that is closest */
88 } LV_INTHIT
, *LPLV_INTHIT
;
91 typedef struct tagEDITLABEL_ITEM
95 EditlblCallbackW EditLblCb
;
98 typedef struct tagLISTVIEW_SUBITEM
105 typedef struct tagLISTVIEW_ITEM
116 typedef struct tagLISTVIEW_SELECTION
120 } LISTVIEW_SELECTION
;
122 typedef struct tagLISTVIEW_INFO
128 HIMAGELIST himlNormal
;
129 HIMAGELIST himlSmall
;
130 HIMAGELIST himlState
;
134 HDPA hdpaSelectionRanges
;
148 INT ntmHeight
; /* from GetTextMetrics from above font */
149 INT ntmAveCharWidth
; /* from GetTextMetrics from above font */
151 DWORD dwExStyle
; /* extended listview style */
153 PFNLVCOMPARE pfnCompare
;
157 EDITLABEL_ITEM
*pedititem
;
159 INT nColumnCount
; /* the number of columns in this control */
161 DWORD lastKeyPressTimestamp
; /* Added */
162 WPARAM charCode
; /* Added */
163 INT nSearchParamLength
; /* Added */
164 WCHAR szSearchParam
[ MAX_PATH
]; /* Added */
172 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
173 #define SB_INTERNAL_UP -1
174 #define SB_INTERNAL_DOWN -2
175 #define SB_INTERNAL_RIGHT -3
176 #define SB_INTERNAL_LEFT -4
178 /* maximum size of a label */
179 #define DISP_TEXT_SIZE 512
181 /* padding for items in list and small icon display modes */
182 #define WIDTH_PADDING 12
184 /* padding for items in list, report and small icon display modes */
185 #define HEIGHT_PADDING 1
187 /* offset of items in report display mode */
188 #define REPORT_MARGINX 2
190 /* padding for icon in large icon display mode
191 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
192 * that HITTEST will see.
193 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
194 * ICON_TOP_PADDING - sum of the two above.
195 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
196 * LABEL_VERT_OFFSET - between bottom of text and end of box
198 #define ICON_TOP_PADDING_NOTHITABLE 2
199 #define ICON_TOP_PADDING_HITABLE 2
200 #define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE
201 #define ICON_BOTTOM_PADDING 4
202 #define LABEL_VERT_OFFSET 10
204 /* default label width for items in list and small icon display modes */
205 #define DEFAULT_LABEL_WIDTH 40
207 /* default column width for items in list display mode */
208 #define DEFAULT_COLUMN_WIDTH 128
210 /* Size of "line" scroll for V & H scrolls */
211 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
213 /* Padding betwen image and label */
214 #define IMAGE_PADDING 2
216 /* Padding behind the label */
217 #define TRAILING_PADDING 5
219 /* Border for the icon caption */
220 #define CAPTION_BORDER 2
224 /* retrieve the number of items in the listview */
225 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
226 #define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA )
228 HWND
CreateEditLabelT(LPCWSTR text
, DWORD style
, INT x
, INT y
,
229 INT width
, INT height
, HWND parent
, HINSTANCE hinst
,
230 EditlblCallbackW EditLblCb
, DWORD param
, BOOL isW
);
233 * forward declarations
235 static LRESULT
LISTVIEW_GetItemT(HWND hwnd
, LPLVITEMW lpLVItem
, BOOL internal
, BOOL isW
);
236 static INT
LISTVIEW_SuperHitTestItem(HWND
, LPLV_INTHIT
, BOOL
);
237 static INT
LISTVIEW_HitTestItem(HWND
, LPLVHITTESTINFO
, BOOL
);
238 static INT
LISTVIEW_GetCountPerRow(HWND
);
239 static INT
LISTVIEW_GetCountPerColumn(HWND
);
240 static VOID
LISTVIEW_AlignLeft(HWND
);
241 static VOID
LISTVIEW_AlignTop(HWND
);
242 static VOID
LISTVIEW_AddGroupSelection(HWND
, INT
);
243 static VOID
LISTVIEW_AddSelection(HWND
, INT
);
244 static BOOL
LISTVIEW_AddSubItemT(HWND
, LPLVITEMW
, BOOL
);
245 static INT
LISTVIEW_FindInsertPosition(HDPA
, INT
);
246 static INT
LISTVIEW_GetItemHeight(HWND
);
247 static BOOL
LISTVIEW_GetItemBoundBox(HWND
, INT
, LPRECT
);
248 static BOOL
LISTVIEW_GetItemPosition(HWND
, INT
, LPPOINT
);
249 static LRESULT
LISTVIEW_GetItemRect(HWND
, INT
, LPRECT
);
250 static LRESULT
LISTVIEW_GetSubItemRect(HWND
, INT
, INT
, INT
, LPRECT
);
251 static INT
LISTVIEW_GetItemWidth(HWND
);
252 static INT
LISTVIEW_GetLabelWidth(HWND
, INT
);
253 static LRESULT
LISTVIEW_GetOrigin(HWND
, LPPOINT
);
254 static INT
LISTVIEW_CalculateWidth(HWND hwnd
, INT nItem
);
255 static LISTVIEW_SUBITEM
* LISTVIEW_GetSubItem(HDPA
, INT
);
256 static LRESULT
LISTVIEW_GetViewRect(HWND
, LPRECT
);
257 static BOOL
LISTVIEW_InitItemT(HWND
, LISTVIEW_ITEM
*, LPLVITEMW
, BOOL
);
258 static BOOL
LISTVIEW_InitSubItemT(HWND
, LISTVIEW_SUBITEM
*, LPLVITEMW
, BOOL
);
259 static LRESULT
LISTVIEW_MouseSelection(HWND
, POINT
);
260 static BOOL
LISTVIEW_RemoveColumn(HDPA
, INT
);
261 static BOOL
LISTVIEW_RemoveSubItem(HDPA
, INT
);
262 static VOID
LISTVIEW_SetGroupSelection(HWND
, INT
);
263 static BOOL
LISTVIEW_SetItemT(HWND
, LPLVITEMW
, BOOL
);
264 static BOOL
LISTVIEW_SetItemFocus(HWND
, INT
);
265 static BOOL
LISTVIEW_SetItemPosition(HWND
, INT
, LONG
, LONG
);
266 static VOID
LISTVIEW_UpdateScroll(HWND
);
267 static VOID
LISTVIEW_SetSelection(HWND
, INT
);
268 static BOOL
LISTVIEW_UpdateSize(HWND
);
269 static BOOL
LISTVIEW_SetSubItemT(HWND
, LPLVITEMW
, BOOL
);
270 static LRESULT
LISTVIEW_SetViewRect(HWND
, LPRECT
);
271 static BOOL
LISTVIEW_ToggleSelection(HWND
, INT
);
272 static VOID
LISTVIEW_UnsupportedStyles(LONG lStyle
);
273 static HWND
LISTVIEW_EditLabelT(HWND hwnd
, INT nItem
, BOOL isW
);
274 static BOOL
LISTVIEW_EndEditLabelW(HWND hwnd
, LPWSTR pszText
, DWORD nItem
);
275 static BOOL
LISTVIEW_EndEditLabelA(HWND hwnd
, LPSTR pszText
, DWORD nItem
);
276 static LRESULT
LISTVIEW_Command(HWND hwnd
, WPARAM wParam
, LPARAM lParam
);
277 static LRESULT
LISTVIEW_SortItems(HWND hwnd
, PFNLVCOMPARE pfnCompare
, LPARAM lParamSort
);
278 static LRESULT
LISTVIEW_GetStringWidthT(HWND hwnd
, LPCWSTR lpszText
, BOOL isW
);
279 static INT
LISTVIEW_ProcessLetterKeys( HWND hwnd
, WPARAM charCode
, LPARAM keyData
);
280 static BOOL
LISTVIEW_KeySelection(HWND hwnd
, INT nItem
);
281 static LRESULT
LISTVIEW_GetItemState(HWND hwnd
, INT nItem
, UINT uMask
);
282 static LRESULT
LISTVIEW_SetItemState(HWND hwnd
, INT nItem
, LPLVITEMW lpLVItem
);
283 static BOOL
LISTVIEW_IsSelected(HWND hwnd
, INT nItem
);
284 static VOID
LISTVIEW_RemoveSelectionRange(HWND hwnd
, INT lItem
, INT uItem
);
285 static void LISTVIEW_FillBackground(HWND hwnd
, HDC hdc
, LPRECT rc
);
286 static void ListView_UpdateLargeItemLabelRect (HWND hwnd
, const LISTVIEW_INFO
* infoPtr
, int nItem
, RECT
*rect
);
287 static LRESULT
LISTVIEW_GetColumnT(HWND
, INT
, LPLVCOLUMNW
, BOOL
);
288 static LRESULT
LISTVIEW_VScroll(HWND hwnd
, INT nScrollCode
, SHORT nCurrentPos
, HWND hScrollWnd
);
289 static LRESULT
LISTVIEW_HScroll(HWND hwnd
, INT nScrollCode
, SHORT nCurrentPos
, HWND hScrollWnd
);
291 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
292 #define KEY_DELAY 450
294 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
296 static inline BOOL
is_textW(LPCWSTR text
)
298 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
301 static inline BOOL
is_textT(LPCWSTR text
, BOOL isW
)
303 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
304 return is_textW(text
);
307 static inline int textlenT(LPCWSTR text
, BOOL isW
)
309 return !is_textT(text
, isW
) ? 0 :
310 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
313 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
316 if (isSrcW
) lstrcpynW(dest
, src
, max
);
317 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
319 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
320 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
323 static inline LPCSTR
debugstr_t(LPCWSTR text
, BOOL isW
)
325 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
328 static inline LPCSTR
debugstr_tn(LPCWSTR text
, BOOL isW
, INT n
)
330 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
333 static inline LPCSTR
lv_dmp_str(LPCWSTR text
, BOOL isW
, INT n
)
335 return debugstr_tn(text
, isW
, min(n
, textlenT(text
, isW
)));
338 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
340 LPWSTR wstr
= (LPWSTR
)text
;
342 TRACE("(text=%s, isW=%d)\n", debugstr_t(text
, isW
), isW
);
345 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
346 wstr
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
347 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
349 TRACE(" wstr=%s\n", debugstr_w(wstr
));
353 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
355 if (!isW
&& wstr
) HeapFree(GetProcessHeap(), 0, wstr
);
359 * dest is a pointer to a Unicode string
360 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
362 static inline BOOL
textsetptrT(LPWSTR
*dest
, LPWSTR src
, BOOL isW
)
364 LPWSTR pszText
= textdupTtoW(src
, isW
);
366 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
367 bResult
= Str_SetPtrW(dest
, pszText
);
368 textfreeT(pszText
, isW
);
372 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
373 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
376 return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
378 return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
381 static inline BOOL
notify(HWND self
, INT code
, LPNMHDR pnmh
)
383 pnmh
->hwndFrom
= self
;
384 pnmh
->idFrom
= GetWindowLongW(self
, GWL_ID
);
386 return (BOOL
)SendMessageW(GetParent(self
), WM_NOTIFY
,
387 (WPARAM
)pnmh
->idFrom
, (LPARAM
)pnmh
);
390 static inline BOOL
hdr_notify(HWND self
, INT code
)
393 return notify(self
, code
, &nmh
);
396 static inline BOOL
listview_notify(HWND self
, INT code
, LPNMLISTVIEW plvnm
)
398 return notify(self
, code
, (LPNMHDR
)plvnm
);
401 static int tabNotification
[] = {
402 LVN_BEGINLABELEDITW
, LVN_BEGINLABELEDITA
,
403 LVN_ENDLABELEDITW
, LVN_ENDLABELEDITA
,
404 LVN_GETDISPINFOW
, LVN_GETDISPINFOA
,
405 LVN_SETDISPINFOW
, LVN_SETDISPINFOA
,
406 LVN_ODFINDITEMW
, LVN_ODFINDITEMA
,
407 LVN_GETINFOTIPW
, LVN_GETINFOTIPA
,
411 static int get_ansi_notification(INT unicodeNotificationCode
)
413 int *pTabNotif
= tabNotification
;
414 while (*pTabNotif
&& (unicodeNotificationCode
!= *pTabNotif
++));
415 if (*pTabNotif
) return *pTabNotif
;
416 ERR("unknown notification %x\n", unicodeNotificationCode
);
417 return unicodeNotificationCode
;
421 Send notification. depends on dispinfoW having same
422 structure as dispinfoA.
423 self : listview handle
424 notificationCode : *Unicode* notification code
425 pdi : dispinfo structure (can be unicode or ansi)
426 isW : TRUE if dispinfo is Unicode
428 static BOOL
dispinfo_notifyT(HWND self
, INT notificationCode
, LPNMLVDISPINFOW pdi
, BOOL isW
)
430 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(self
, 0);
431 BOOL bResult
= FALSE
;
432 BOOL convertToAnsi
= FALSE
, convertToUnicode
= FALSE
;
434 INT cchTempBufMax
= 0, savCchTextMax
= 0;
435 LPWSTR pszTempBuf
= NULL
, savPszText
= NULL
;
437 TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self
, notificationCode
, pdi
, isW
);
438 TRACE(" notifyFormat=%s\n",
439 infoPtr
->notifyFormat
== NFR_UNICODE
? "NFR_UNICODE" :
440 infoPtr
->notifyFormat
== NFR_ANSI
? "NFR_ANSI" : "(not set)");
441 if (infoPtr
->notifyFormat
== NFR_ANSI
)
442 realNotifCode
= get_ansi_notification(notificationCode
);
444 realNotifCode
= notificationCode
;
446 if (is_textT(pdi
->item
.pszText
, isW
))
448 if (isW
&& infoPtr
->notifyFormat
== NFR_ANSI
)
449 convertToAnsi
= TRUE
;
450 if (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
)
451 convertToUnicode
= TRUE
;
454 if (convertToAnsi
|| convertToUnicode
)
456 TRACE(" we have to convert the text to the correct format\n");
457 if (notificationCode
!= LVN_GETDISPINFOW
)
458 { /* length of existing text */
459 cchTempBufMax
= convertToUnicode
?
460 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
461 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
464 cchTempBufMax
= pdi
->item
.cchTextMax
;
466 pszTempBuf
= HeapAlloc(GetProcessHeap(), 0,
467 (convertToUnicode
? sizeof(WCHAR
) : sizeof(CHAR
)) * cchTempBufMax
);
468 if (!pszTempBuf
) return FALSE
;
469 if (convertToUnicode
)
470 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
471 pszTempBuf
, cchTempBufMax
);
473 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) pszTempBuf
,
474 cchTempBufMax
, NULL
, NULL
);
475 TRACE(" text=%s\n", debugstr_t(pszTempBuf
, convertToUnicode
));
476 savCchTextMax
= pdi
->item
.cchTextMax
;
477 savPszText
= pdi
->item
.pszText
;
478 pdi
->item
.pszText
= pszTempBuf
;
479 pdi
->item
.cchTextMax
= cchTempBufMax
;
482 bResult
= notify(self
, realNotifCode
, (LPNMHDR
)pdi
);
484 if (convertToUnicode
|| convertToAnsi
)
485 { /* convert back result */
486 TRACE(" returned text=%s\n", debugstr_t(pdi
->item
.pszText
, convertToUnicode
));
487 if (convertToUnicode
) /* note : pointer can be changed by app ! */
488 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) savPszText
,
489 savCchTextMax
, NULL
, NULL
);
491 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
492 savPszText
, savCchTextMax
);
493 pdi
->item
.pszText
= savPszText
; /* restores our buffer */
494 pdi
->item
.cchTextMax
= savCchTextMax
;
495 HeapFree(GetProcessHeap(), 0, pszTempBuf
);
500 static inline LRESULT
LISTVIEW_GetItemW(HWND hwnd
, LPLVITEMW lpLVItem
, BOOL internal
)
502 return LISTVIEW_GetItemT(hwnd
, lpLVItem
, internal
, TRUE
);
505 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
509 n
= min(min(n
, strlenW(s1
)), strlenW(s2
));
510 res
= CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
);
511 return res
? res
- 2 : res
;
514 static char* debuglvitem_t(LPLVITEMW lpLVItem
, BOOL isW
)
516 static int index
= 0;
517 static char buffers
[20][256];
518 char* buf
= buffers
[index
++ % 20];
519 if (lpLVItem
== NULL
) return "(null)";
520 snprintf(buf
, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
521 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
522 lpLVItem
->mask
, lpLVItem
->iItem
, lpLVItem
->iSubItem
,
523 lpLVItem
->state
, lpLVItem
->stateMask
,
524 lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
? "(callback)" :
525 lv_dmp_str(lpLVItem
->pszText
, isW
, 80),
526 lpLVItem
->cchTextMax
, lpLVItem
->iImage
, lpLVItem
->lParam
,
531 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn
, BOOL isW
)
533 static int index
= 0;
534 static char buffers
[20][256];
535 char* buf
= buffers
[index
++ % 20];
536 if (lpColumn
== NULL
) return "(null)";
537 snprintf(buf
, 256, "{mask=%x, fmt=%x, cx=%d,"
538 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
539 lpColumn
->mask
, lpColumn
->fmt
, lpColumn
->cx
,
540 lpColumn
->mask
& LVCF_TEXT
? lpColumn
->pszText
== LPSTR_TEXTCALLBACKW
? "(callback)" :
541 lv_dmp_str(lpColumn
->pszText
, isW
, 80): "",
542 lpColumn
->mask
& LVCF_TEXT
? lpColumn
->cchTextMax
: 0, lpColumn
->iSubItem
);
546 static void LISTVIEW_DumpListview(LISTVIEW_INFO
*iP
, INT line
)
548 DWORD dwStyle
= GetWindowLongW (iP
->hwndSelf
, GWL_STYLE
);
549 TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n",
550 iP
->hwndSelf
, line
, iP
->clrBk
, iP
->clrText
, iP
->clrTextBk
,
551 iP
->nItemHeight
, iP
->nItemWidth
, dwStyle
);
552 TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n",
553 iP
->hwndSelf
, line
, iP
->himlNormal
, iP
->himlSmall
, iP
->himlState
,
554 iP
->nFocusedItem
, iP
->nHotItem
, iP
->dwExStyle
);
558 LISTVIEW_SendCustomDrawNotify (HWND hwnd
, DWORD dwDrawStage
, HDC hdc
,
561 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
562 NMLVCUSTOMDRAW nmcdhdr
;
565 TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd
, dwDrawStage
, hdc
);
567 nmcd
= & nmcdhdr
.nmcd
;
568 nmcd
->hdr
.hwndFrom
= hwnd
;
569 nmcd
->hdr
.idFrom
= GetWindowLongW( hwnd
, GWL_ID
);
570 nmcd
->hdr
.code
= NM_CUSTOMDRAW
;
571 nmcd
->dwDrawStage
= dwDrawStage
;
573 nmcd
->rc
.left
= rc
.left
;
574 nmcd
->rc
.right
= rc
.right
;
575 nmcd
->rc
.bottom
= rc
.bottom
;
576 nmcd
->rc
.top
= rc
.top
;
577 nmcd
->dwItemSpec
= 0;
578 nmcd
->uItemState
= 0;
579 nmcd
->lItemlParam
= 0;
580 nmcdhdr
.clrText
= infoPtr
->clrText
;
581 nmcdhdr
.clrTextBk
= infoPtr
->clrBk
;
583 return (BOOL
)SendMessageW (GetParent (hwnd
), WM_NOTIFY
,
584 (WPARAM
) GetWindowLongW( hwnd
, GWL_ID
), (LPARAM
)&nmcdhdr
);
588 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd
, HDC hdc
,
589 UINT iItem
, UINT iSubItem
,
592 LISTVIEW_INFO
*infoPtr
;
593 NMLVCUSTOMDRAW nmcdhdr
;
595 DWORD dwDrawStage
,dwItemSpec
;
601 infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
603 ZeroMemory(&item
,sizeof(item
));
605 item
.mask
= LVIF_PARAM
;
606 ListView_GetItemW(hwnd
,&item
);
608 dwDrawStage
=CDDS_ITEM
| uItemDrawState
;
612 if (LISTVIEW_IsSelected(hwnd
,iItem
)) uItemState
|=CDIS_SELECTED
;
613 if (iItem
==infoPtr
->nFocusedItem
) uItemState
|=CDIS_FOCUS
;
614 if (iItem
==infoPtr
->nHotItem
) uItemState
|=CDIS_HOT
;
616 itemRect
.left
= LVIR_BOUNDS
;
617 LISTVIEW_GetItemRect(hwnd
, iItem
, &itemRect
);
619 nmcd
= & nmcdhdr
.nmcd
;
620 nmcd
->hdr
.hwndFrom
= hwnd
;
621 nmcd
->hdr
.idFrom
= GetWindowLongW( hwnd
, GWL_ID
);
622 nmcd
->hdr
.code
= NM_CUSTOMDRAW
;
623 nmcd
->dwDrawStage
= dwDrawStage
;
625 nmcd
->rc
.left
= itemRect
.left
;
626 nmcd
->rc
.right
= itemRect
.right
;
627 nmcd
->rc
.bottom
= itemRect
.bottom
;
628 nmcd
->rc
.top
= itemRect
.top
;
629 nmcd
->dwItemSpec
= dwItemSpec
;
630 nmcd
->uItemState
= uItemState
;
631 nmcd
->lItemlParam
= item
.lParam
;
632 nmcdhdr
.clrText
= infoPtr
->clrText
;
633 nmcdhdr
.clrTextBk
= infoPtr
->clrBk
;
634 nmcdhdr
.iSubItem
=iSubItem
;
636 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
637 nmcd
->dwDrawStage
, nmcd
->hdc
, nmcd
->dwItemSpec
,
638 nmcd
->uItemState
, nmcd
->lItemlParam
);
640 retval
=SendMessageW (GetParent (hwnd
), WM_NOTIFY
,
641 (WPARAM
) GetWindowLongW( hwnd
, GWL_ID
), (LPARAM
)&nmcdhdr
);
643 infoPtr
->clrText
=nmcdhdr
.clrText
;
644 infoPtr
->clrBk
=nmcdhdr
.clrTextBk
;
645 return (BOOL
) retval
;
649 /*************************************************************************
650 * LISTVIEW_ProcessLetterKeys
652 * Processes keyboard messages generated by pressing the letter keys
654 * What this does is perform a case insensitive search from the
655 * current position with the following quirks:
656 * - If two chars or more are pressed in quick succession we search
657 * for the corresponding string (e.g. 'abc').
658 * - If there is a delay we wipe away the current search string and
659 * restart with just that char.
660 * - If the user keeps pressing the same character, whether slowly or
661 * fast, so that the search string is entirely composed of this
662 * character ('aaaaa' for instance), then we search for first item
663 * that starting with that character.
664 * - If the user types the above character in quick succession, then
665 * we must also search for the corresponding string ('aaaaa'), and
666 * go to that string if there is a match.
674 * - The current implementation has a list of characters it will
675 * accept and it ignores averything else. In particular it will
676 * ignore accentuated characters which seems to match what
677 * Windows does. But I'm not sure it makes sense to follow
679 * - We don't sound a beep when the search fails.
683 * TREEVIEW_ProcessLetterKeys
685 static INT
LISTVIEW_ProcessLetterKeys(
686 HWND hwnd
, /* handle to the window */
687 WPARAM charCode
, /* the character code, the actual character */
688 LPARAM keyData
/* key data */
691 LISTVIEW_INFO
*infoPtr
;
696 WCHAR buffer
[MAX_PATH
];
697 DWORD timestamp
,elapsed
;
699 /* simple parameter checking */
700 if (!hwnd
|| !charCode
|| !keyData
)
703 infoPtr
=(LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
707 /* only allow the valid WM_CHARs through */
708 if (!isalnum(charCode
) &&
709 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
710 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
711 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
712 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
713 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
714 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
715 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
716 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
717 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
720 nSize
=GETITEMCOUNT(infoPtr
);
721 /* if there's one item or less, there is no where to go */
725 /* compute how much time elapsed since last keypress */
726 timestamp
=GetTickCount();
727 if (timestamp
> infoPtr
->lastKeyPressTimestamp
) {
728 elapsed
=timestamp
-infoPtr
->lastKeyPressTimestamp
;
730 elapsed
=infoPtr
->lastKeyPressTimestamp
-timestamp
;
733 /* update the search parameters */
734 infoPtr
->lastKeyPressTimestamp
=timestamp
;
735 if (elapsed
< KEY_DELAY
) {
736 if (infoPtr
->nSearchParamLength
< COUNTOF(infoPtr
->szSearchParam
)) {
737 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++]=charCode
;
739 if (infoPtr
->charCode
!= charCode
) {
740 infoPtr
->charCode
=charCode
=0;
743 infoPtr
->charCode
=charCode
;
744 infoPtr
->szSearchParam
[0]=charCode
;
745 infoPtr
->nSearchParamLength
=1;
746 /* Redundant with the 1 char string */
750 /* and search from the current position */
752 if (infoPtr
->nFocusedItem
>= 0) {
753 endidx
=infoPtr
->nFocusedItem
;
755 /* if looking for single character match,
756 * then we must always move forward
758 if (infoPtr
->nSearchParamLength
== 1)
772 ZeroMemory(&item
, sizeof(item
));
773 item
.mask
= LVIF_TEXT
;
776 item
.pszText
= buffer
;
777 item
.cchTextMax
= COUNTOF(buffer
);
778 ListView_GetItemW( hwnd
, &item
);
780 /* check for a match */
781 if (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,infoPtr
->nSearchParamLength
) == 0) {
784 } else if ( (charCode
!= 0) && (nItem
== -1) && (nItem
!= infoPtr
->nFocusedItem
) &&
785 (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,1) == 0) ) {
786 /* This would work but we must keep looking for a longer match */
790 } while (idx
!= endidx
);
793 if (LISTVIEW_KeySelection(hwnd
, nItem
) != FALSE
) {
794 /* refresh client area */
795 InvalidateRect(hwnd
, NULL
, TRUE
);
803 /*************************************************************************
804 * LISTVIEW_UpdateHeaderSize [Internal]
806 * Function to resize the header control
809 * hwnd [I] handle to a window
810 * nNewScrollPos [I] Scroll Pos to Set
817 static VOID
LISTVIEW_UpdateHeaderSize(HWND hwnd
, INT nNewScrollPos
)
819 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
823 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
824 point
[0].x
= winRect
.left
;
825 point
[0].y
= winRect
.top
;
826 point
[1].x
= winRect
.right
;
827 point
[1].y
= winRect
.bottom
;
829 MapWindowPoints(HWND_DESKTOP
, hwnd
, point
, 2);
830 point
[0].x
= -nNewScrollPos
;
831 point
[1].x
+= nNewScrollPos
;
833 SetWindowPos(infoPtr
->hwndHeader
,0,
834 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
835 SWP_NOZORDER
| SWP_NOACTIVATE
);
840 * Update the scrollbars. This functions should be called whenever
841 * the content, size or view changes.
844 * [I] HWND : window handle
849 static VOID
LISTVIEW_UpdateScroll(HWND hwnd
)
851 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
852 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
853 UINT uView
= lStyle
& LVS_TYPEMASK
;
854 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
855 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
856 SCROLLINFO scrollInfo
;
858 if (lStyle
& LVS_NOSCROLL
) return;
860 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
861 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
863 if (uView
== LVS_LIST
)
865 /* update horizontal scrollbar */
867 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(hwnd
);
868 INT nCountPerRow
= LISTVIEW_GetCountPerRow(hwnd
);
869 INT nNumOfItems
= GETITEMCOUNT(infoPtr
);
871 scrollInfo
.nMax
= nNumOfItems
/ nCountPerColumn
;
872 TRACE("items=%d, perColumn=%d, perRow=%d\n",
873 nNumOfItems
, nCountPerColumn
, nCountPerRow
);
874 if((nNumOfItems
% nCountPerColumn
) == 0)
878 scrollInfo
.nPos
= ListView_GetTopIndex(hwnd
) / nCountPerColumn
;
879 scrollInfo
.nPage
= nCountPerRow
;
880 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
882 SetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
, TRUE
);
883 ShowScrollBar(hwnd
, SB_VERT
, FALSE
);
885 else if (uView
== LVS_REPORT
)
889 /* update vertical scrollbar */
891 scrollInfo
.nMax
= GETITEMCOUNT(infoPtr
) - 1;
892 scrollInfo
.nPos
= ListView_GetTopIndex(hwnd
);
893 scrollInfo
.nPage
= LISTVIEW_GetCountPerColumn(hwnd
);
894 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
895 test
= (scrollInfo
.nMin
>= scrollInfo
.nMax
- max((INT
)scrollInfo
.nPage
- 1, 0));
896 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
897 scrollInfo
.nMax
, scrollInfo
.nPage
, test
);
898 SetScrollInfo(hwnd
, SB_VERT
, &scrollInfo
, TRUE
);
899 ShowScrollBar(hwnd
, SB_VERT
, (test
) ? FALSE
: TRUE
);
901 /* update horizontal scrollbar */
902 nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
903 if (GetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
) == FALSE
904 || GETITEMCOUNT(infoPtr
) == 0)
909 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
910 scrollInfo
.nPage
= nListWidth
;
911 scrollInfo
.nMax
= max(infoPtr
->nItemWidth
, 0)-1;
912 test
= (scrollInfo
.nMin
>= scrollInfo
.nMax
- max((INT
)scrollInfo
.nPage
- 1, 0));
913 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
914 scrollInfo
.nMax
, scrollInfo
.nPage
, test
);
915 SetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
, TRUE
);
916 ShowScrollBar(hwnd
, SB_HORZ
, (test
) ? FALSE
: TRUE
);
918 /* Update the Header Control */
919 scrollInfo
.fMask
= SIF_POS
;
920 GetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
);
921 LISTVIEW_UpdateHeaderSize(hwnd
, scrollInfo
.nPos
);
928 if (LISTVIEW_GetViewRect(hwnd
, &rcView
) != FALSE
)
930 INT nViewWidth
= rcView
.right
- rcView
.left
;
931 INT nViewHeight
= rcView
.bottom
- rcView
.top
;
933 /* Update Horizontal Scrollbar */
934 scrollInfo
.fMask
= SIF_POS
;
935 if (GetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
) == FALSE
936 || GETITEMCOUNT(infoPtr
) == 0)
940 scrollInfo
.nMax
= max(nViewWidth
, 0)-1;
942 scrollInfo
.nPage
= nListWidth
;
943 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
944 TRACE("LVS_ICON/SMALLICON Horz.\n");
945 SetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
, TRUE
);
947 /* Update Vertical Scrollbar */
948 nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
949 scrollInfo
.fMask
= SIF_POS
;
950 if (GetScrollInfo(hwnd
, SB_VERT
, &scrollInfo
) == FALSE
951 || GETITEMCOUNT(infoPtr
) == 0)
955 scrollInfo
.nMax
= max(nViewHeight
,0)-1;
957 scrollInfo
.nPage
= nListHeight
;
958 scrollInfo
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
959 TRACE("LVS_ICON/SMALLICON Vert.\n");
960 SetScrollInfo(hwnd
, SB_VERT
, &scrollInfo
, TRUE
);
967 * Prints a message for unsupported window styles.
968 * A kind of TODO list for window styles.
971 * [I] LONG : window style
976 static VOID
LISTVIEW_UnsupportedStyles(LONG lStyle
)
978 if ((LVS_TYPESTYLEMASK
& lStyle
) == LVS_NOSCROLL
)
979 FIXME(" LVS_NOSCROLL\n");
981 if (lStyle
& LVS_NOLABELWRAP
)
982 FIXME(" LVS_NOLABELWRAP\n");
984 if (!(lStyle
& LVS_SHAREIMAGELISTS
))
985 FIXME(" !LVS_SHAREIMAGELISTS\n");
987 if (lStyle
& LVS_SORTASCENDING
)
988 FIXME(" LVS_SORTASCENDING\n");
990 if (lStyle
& LVS_SORTDESCENDING
)
991 FIXME(" LVS_SORTDESCENDING\n");
996 * Aligns the items with the top edge of the window.
999 * [I] HWND : window handle
1004 static VOID
LISTVIEW_AlignTop(HWND hwnd
)
1006 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1007 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
1008 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1011 INT i
, off_x
=0, off_y
=0;
1013 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1015 /* Since SetItemPosition uses upper-left of icon, and for
1016 style=LVS_ICON the icon is not left adjusted, get the offset */
1017 if (uView
== LVS_ICON
)
1019 off_y
= ICON_TOP_PADDING
;
1020 off_x
= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2;
1024 ZeroMemory(&rcView
, sizeof(RECT
));
1025 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1027 infoPtr
->rcList
.left
, infoPtr
->rcList
.right
);
1029 if (nListWidth
> infoPtr
->nItemWidth
)
1031 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
1033 if ((ptItem
.x
-off_x
) + infoPtr
->nItemWidth
> nListWidth
)
1036 ptItem
.y
+= infoPtr
->nItemHeight
;
1039 LISTVIEW_SetItemPosition(hwnd
, i
, ptItem
.x
, ptItem
.y
);
1040 ptItem
.x
+= infoPtr
->nItemWidth
;
1041 rcView
.right
= max(rcView
.right
, ptItem
.x
);
1044 rcView
.right
-= off_x
;
1045 rcView
.bottom
= (ptItem
.y
-off_y
) + infoPtr
->nItemHeight
;
1049 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
1051 LISTVIEW_SetItemPosition(hwnd
, i
, ptItem
.x
, ptItem
.y
);
1052 ptItem
.y
+= infoPtr
->nItemHeight
;
1055 rcView
.right
= infoPtr
->nItemWidth
;
1056 rcView
.bottom
= ptItem
.y
-off_y
;
1059 LISTVIEW_SetViewRect(hwnd
, &rcView
);
1065 * Aligns the items with the left edge of the window.
1068 * [I] HWND : window handle
1073 static VOID
LISTVIEW_AlignLeft(HWND hwnd
)
1075 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1076 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
1077 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1080 INT i
, off_x
=0, off_y
=0;
1082 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1084 /* Since SetItemPosition uses upper-left of icon, and for
1085 style=LVS_ICON the icon is not left adjusted, get the offset */
1086 if (uView
== LVS_ICON
)
1088 off_y
= ICON_TOP_PADDING
;
1089 off_x
= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2;
1093 ZeroMemory(&rcView
, sizeof(RECT
));
1094 TRACE("Icon off.x=%d, off.y=%d\n", off_x
, off_y
);
1096 if (nListHeight
> infoPtr
->nItemHeight
)
1098 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
1100 if (ptItem
.y
+ infoPtr
->nItemHeight
> nListHeight
)
1103 ptItem
.x
+= infoPtr
->nItemWidth
;
1106 LISTVIEW_SetItemPosition(hwnd
, i
, ptItem
.x
, ptItem
.y
);
1107 ptItem
.y
+= infoPtr
->nItemHeight
;
1108 rcView
.bottom
= max(rcView
.bottom
, ptItem
.y
);
1111 rcView
.right
= ptItem
.x
+ infoPtr
->nItemWidth
;
1115 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
1117 LISTVIEW_SetItemPosition(hwnd
, i
, ptItem
.x
, ptItem
.y
);
1118 ptItem
.x
+= infoPtr
->nItemWidth
;
1121 rcView
.bottom
= infoPtr
->nItemHeight
;
1122 rcView
.right
= ptItem
.x
;
1125 LISTVIEW_SetViewRect(hwnd
, &rcView
);
1131 * Set the bounding rectangle of all the items.
1134 * [I] HWND : window handle
1135 * [I] LPRECT : bounding rectangle
1141 static LRESULT
LISTVIEW_SetViewRect(HWND hwnd
, LPRECT lprcView
)
1143 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1144 BOOL bResult
= FALSE
;
1146 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd
,
1147 lprcView
->left
, lprcView
->top
, lprcView
->right
, lprcView
->bottom
);
1149 if (lprcView
!= NULL
)
1152 infoPtr
->rcView
.left
= lprcView
->left
;
1153 infoPtr
->rcView
.top
= lprcView
->top
;
1154 infoPtr
->rcView
.right
= lprcView
->right
;
1155 infoPtr
->rcView
.bottom
= lprcView
->bottom
;
1163 * Retrieves the bounding rectangle of all the items.
1166 * [I] HWND : window handle
1167 * [O] LPRECT : bounding rectangle
1173 static LRESULT
LISTVIEW_GetViewRect(HWND hwnd
, LPRECT lprcView
)
1175 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1176 BOOL bResult
= FALSE
;
1179 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd
, lprcView
);
1181 if (lprcView
!= NULL
)
1183 bResult
= LISTVIEW_GetOrigin(hwnd
, &ptOrigin
);
1184 if (bResult
!= FALSE
)
1186 lprcView
->left
= infoPtr
->rcView
.left
+ ptOrigin
.x
;
1187 lprcView
->top
= infoPtr
->rcView
.top
+ ptOrigin
.y
;
1188 lprcView
->right
= infoPtr
->rcView
.right
+ ptOrigin
.x
;
1189 lprcView
->bottom
= infoPtr
->rcView
.bottom
+ ptOrigin
.y
;
1192 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1193 lprcView
->left
, lprcView
->top
, lprcView
->right
, lprcView
->bottom
);
1201 * Retrieves the subitem pointer associated with the subitem index.
1204 * [I] HDPA : DPA handle for a specific item
1205 * [I] INT : index of subitem
1208 * SUCCESS : subitem pointer
1211 static LISTVIEW_SUBITEM
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
,
1214 LISTVIEW_SUBITEM
*lpSubItem
;
1217 for (i
= 1; i
< hdpaSubItems
->nItemCount
; i
++)
1219 lpSubItem
= (LISTVIEW_SUBITEM
*) DPA_GetPtr(hdpaSubItems
, i
);
1220 if (lpSubItem
!= NULL
)
1222 if (lpSubItem
->iSubItem
== nSubItem
)
1234 * Calculates the width of an item.
1237 * [I] HWND : window handle
1238 * [I] LONG : window style
1241 * Returns item width.
1243 static INT
LISTVIEW_GetItemWidth(HWND hwnd
)
1245 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1246 LONG style
= GetWindowLongW(hwnd
, GWL_STYLE
);
1247 UINT uView
= style
& LVS_TYPEMASK
;
1248 INT nHeaderItemCount
;
1254 TRACE("(hwnd=%x)\n", hwnd
);
1256 if (uView
== LVS_ICON
)
1258 nItemWidth
= infoPtr
->iconSpacing
.cx
;
1260 else if (uView
== LVS_REPORT
)
1262 /* calculate width of header */
1263 nHeaderItemCount
= Header_GetItemCount(infoPtr
->hwndHeader
);
1264 for (i
= 0; i
< nHeaderItemCount
; i
++)
1266 if (Header_GetItemRect(infoPtr
->hwndHeader
, i
, &rcHeaderItem
) != 0)
1268 nItemWidth
+= (rcHeaderItem
.right
- rcHeaderItem
.left
);
1272 else /* for LVS_SMALLICON and LVS_LIST */
1274 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
1276 nLabelWidth
= LISTVIEW_GetLabelWidth(hwnd
, i
);
1277 nItemWidth
= max(nItemWidth
, nLabelWidth
);
1280 /* default label size */
1281 if (GETITEMCOUNT(infoPtr
) == 0)
1283 nItemWidth
= DEFAULT_COLUMN_WIDTH
;
1287 if (nItemWidth
== 0)
1289 nItemWidth
= DEFAULT_LABEL_WIDTH
;
1294 nItemWidth
+= WIDTH_PADDING
;
1296 if (infoPtr
->himlSmall
!= NULL
)
1298 nItemWidth
+= infoPtr
->iconSize
.cx
;
1301 if (infoPtr
->himlState
!= NULL
)
1303 nItemWidth
+= infoPtr
->iconSize
.cx
;
1305 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
);
1311 /* nItemWidth Cannot be Zero */
1319 * Calculates the width of a specific item.
1322 * [I] HWND : window handle
1323 * [I] LPSTR : string
1326 * Returns the width of an item width a specified string.
1328 static INT
LISTVIEW_CalculateWidth(HWND hwnd
, INT nItem
)
1330 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1331 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
1332 INT nHeaderItemCount
;
1337 TRACE("(hwnd=%x)\n", hwnd
);
1339 if (uView
== LVS_ICON
)
1341 nItemWidth
= infoPtr
->iconSpacing
.cx
;
1343 else if (uView
== LVS_REPORT
)
1345 /* calculate width of header */
1346 nHeaderItemCount
= Header_GetItemCount(infoPtr
->hwndHeader
);
1347 for (i
= 0; i
< nHeaderItemCount
; i
++)
1349 if (Header_GetItemRect(infoPtr
->hwndHeader
, i
, &rcHeaderItem
) != 0)
1351 nItemWidth
+= (rcHeaderItem
.right
- rcHeaderItem
.left
);
1357 /* get width of string */
1358 nItemWidth
= LISTVIEW_GetLabelWidth(hwnd
, nItem
);
1360 /* default label size */
1361 if (GETITEMCOUNT(infoPtr
) == 0)
1363 nItemWidth
= DEFAULT_COLUMN_WIDTH
;
1367 if (nItemWidth
== 0)
1369 nItemWidth
= DEFAULT_LABEL_WIDTH
;
1374 nItemWidth
+= WIDTH_PADDING
;
1376 if (infoPtr
->himlSmall
!= NULL
)
1378 nItemWidth
+= infoPtr
->iconSize
.cx
;
1381 if (infoPtr
->himlState
!= NULL
)
1383 nItemWidth
+= infoPtr
->iconSize
.cx
;
1394 * Retrieves and saves important text metrics info for the current
1398 * [I] HWND : window handle
1401 static VOID
LISTVIEW_SaveTextMetrics(HWND hwnd
)
1403 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1405 HDC hdc
= GetDC(hwnd
);
1406 HFONT hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
1407 INT oldHeight
, oldACW
;
1409 GetTextMetricsW(hdc
, &tm
);
1411 oldHeight
= infoPtr
->ntmHeight
;
1412 oldACW
= infoPtr
->ntmAveCharWidth
;
1413 infoPtr
->ntmHeight
= tm
.tmHeight
;
1414 infoPtr
->ntmAveCharWidth
= tm
.tmAveCharWidth
;
1416 SelectObject(hdc
, hOldFont
);
1417 ReleaseDC(hwnd
, hdc
);
1418 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1419 oldHeight
, infoPtr
->ntmHeight
, oldACW
, infoPtr
->ntmAveCharWidth
);
1425 * Calculates the height of an item.
1428 * [I] HWND : window handle
1431 * Returns item height.
1433 static INT
LISTVIEW_GetItemHeight(HWND hwnd
)
1435 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1436 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
1437 INT nItemHeight
= 0;
1439 if (uView
== LVS_ICON
)
1441 nItemHeight
= infoPtr
->iconSpacing
.cy
;
1445 if(infoPtr
->himlState
|| infoPtr
->himlSmall
)
1446 nItemHeight
= max(infoPtr
->ntmHeight
, infoPtr
->iconSize
.cy
) + HEIGHT_PADDING
;
1448 nItemHeight
= infoPtr
->ntmHeight
;
1455 static void LISTVIEW_PrintSelectionRanges(HWND hwnd
)
1457 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1458 LISTVIEW_SELECTION
*selection
;
1459 INT topSelection
= infoPtr
->hdpaSelectionRanges
->nItemCount
;
1462 TRACE("Selections are:\n");
1463 for (i
= 0; i
< topSelection
; i
++)
1465 selection
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
,i
);
1466 TRACE(" %lu - %lu\n",selection
->lower
,selection
->upper
);
1472 * A compare function for selection ranges
1475 * [I] LPVOID : Item 1;
1476 * [I] LPVOID : Item 2;
1477 * [I] LPARAM : flags
1480 * >0 : if Item 1 > Item 2
1481 * <0 : if Item 2 > Item 1
1482 * 0 : if Item 1 == Item 2
1484 static INT CALLBACK
LISTVIEW_CompareSelectionRanges(LPVOID range1
, LPVOID range2
,
1487 int l1
= ((LISTVIEW_SELECTION
*)(range1
))->lower
;
1488 int l2
= ((LISTVIEW_SELECTION
*)(range2
))->lower
;
1489 int u1
= ((LISTVIEW_SELECTION
*)(range1
))->upper
;
1490 int u2
= ((LISTVIEW_SELECTION
*)(range2
))->upper
;
1504 * Adds a selection range.
1507 * [I] HWND : window handle
1508 * [I] INT : lower item index
1509 * [I] INT : upper item index
1514 static VOID
LISTVIEW_AddSelectionRange(HWND hwnd
, INT lItem
, INT uItem
)
1516 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1517 LISTVIEW_SELECTION
*selection
;
1518 INT topSelection
= infoPtr
->hdpaSelectionRanges
->nItemCount
;
1519 BOOL lowerzero
=FALSE
;
1521 selection
= (LISTVIEW_SELECTION
*)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION
));
1522 selection
->lower
= lItem
;
1523 selection
->upper
= uItem
;
1525 TRACE("Add range %i - %i\n", lItem
, uItem
);
1528 LISTVIEW_SELECTION
*checkselection
,*checkselection2
;
1529 INT index
,mergeindex
;
1531 /* find overlapping selections */
1532 /* we want to catch adjacent ranges so expand our range by 1 */
1535 if (selection
->lower
== 0)
1540 index
= DPA_Search(infoPtr
->hdpaSelectionRanges
, selection
, 0,
1541 LISTVIEW_CompareSelectionRanges
,
1543 selection
->upper
--;
1547 selection
->lower
++;
1551 checkselection
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
,index
);
1552 TRACE("Merge with index %i (%lu - %lu)\n",index
,checkselection
->lower
,
1553 checkselection
->upper
);
1555 checkselection
->lower
= min(selection
->lower
,checkselection
->lower
);
1556 checkselection
->upper
= max(selection
->upper
,checkselection
->upper
);
1558 TRACE("New range (%lu - %lu)\n", checkselection
->lower
,
1559 checkselection
->upper
);
1561 COMCTL32_Free(selection
);
1563 /* merge now common selection ranges in the lower group*/
1566 checkselection
->upper
++;
1567 if (checkselection
->lower
== 0)
1570 checkselection
->lower
--;
1572 TRACE("search lower range (%lu - %lu)\n", checkselection
->lower
,
1573 checkselection
->upper
);
1575 /* not sorted yet */
1576 mergeindex
= DPA_Search(infoPtr
->hdpaSelectionRanges
, checkselection
, 0,
1577 LISTVIEW_CompareSelectionRanges
, 0,
1580 checkselection
->upper
--;
1584 checkselection
->lower
++;
1586 if (mergeindex
>=0 && mergeindex
!= index
)
1588 TRACE("Merge with index %i\n",mergeindex
);
1589 checkselection2
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
,
1591 checkselection
->lower
= min(checkselection
->lower
,
1592 checkselection2
->lower
);
1593 checkselection
->upper
= max(checkselection
->upper
,
1594 checkselection2
->upper
);
1595 COMCTL32_Free(checkselection2
);
1596 DPA_DeletePtr(infoPtr
->hdpaSelectionRanges
,mergeindex
);
1600 while (mergeindex
> -1 && mergeindex
<index
);
1602 /* merge now common selection ranges in the upper group*/
1605 checkselection
->upper
++;
1606 if (checkselection
->lower
== 0)
1609 checkselection
->lower
--;
1611 TRACE("search upper range %i (%lu - %lu)\n",index
,
1612 checkselection
->lower
, checkselection
->upper
);
1614 /* not sorted yet */
1615 mergeindex
= DPA_Search(infoPtr
->hdpaSelectionRanges
, checkselection
,
1617 LISTVIEW_CompareSelectionRanges
, 0,
1620 checkselection
->upper
--;
1624 checkselection
->lower
++;
1626 if (mergeindex
>=0 && mergeindex
!=index
)
1628 TRACE("Merge with index %i\n",mergeindex
);
1629 checkselection2
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
,
1631 checkselection
->lower
= min(checkselection
->lower
,
1632 checkselection2
->lower
);
1633 checkselection
->upper
= max(checkselection
->upper
,
1634 checkselection2
->upper
);
1635 COMCTL32_Free(checkselection2
);
1636 DPA_DeletePtr(infoPtr
->hdpaSelectionRanges
,mergeindex
);
1639 while (mergeindex
> -1);
1644 index
= DPA_Search(infoPtr
->hdpaSelectionRanges
, selection
, 0,
1645 LISTVIEW_CompareSelectionRanges
, 0,
1648 TRACE("Insert before index %i\n",index
);
1651 DPA_InsertPtr(infoPtr
->hdpaSelectionRanges
,index
,selection
);
1656 DPA_InsertPtr(infoPtr
->hdpaSelectionRanges
,0,selection
);
1661 DPA_Sort(infoPtr
->hdpaSelectionRanges
,LISTVIEW_CompareSelectionRanges
,0);
1662 LISTVIEW_PrintSelectionRanges(hwnd
);
1667 * check if a specified index is selected.
1670 * [I] HWND : window handle
1671 * [I] INT : item index
1676 static BOOL
LISTVIEW_IsSelected(HWND hwnd
, INT nItem
)
1678 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1679 LISTVIEW_SELECTION selection
;
1682 selection
.upper
= nItem
;
1683 selection
.lower
= nItem
;
1685 index
= DPA_Search(infoPtr
->hdpaSelectionRanges
, &selection
, 0,
1686 LISTVIEW_CompareSelectionRanges
,
1696 * Removes all selection ranges
1699 * HWND: window handle
1705 static LRESULT
LISTVIEW_RemoveAllSelections(HWND hwnd
)
1707 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1708 LISTVIEW_SELECTION
*selection
;
1712 TRACE("(0x%x)\n",hwnd
);
1714 ZeroMemory(&item
,sizeof(item
));
1715 item
.stateMask
= LVIS_SELECTED
;
1719 selection
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
,0);
1722 TRACE("Removing %lu to %lu\n",selection
->lower
, selection
->upper
);
1723 for (i
= selection
->lower
; i
<=selection
->upper
; i
++)
1724 LISTVIEW_SetItemState(hwnd
,i
,&item
);
1725 LISTVIEW_RemoveSelectionRange(hwnd
,selection
->lower
,selection
->upper
);
1728 while (infoPtr
->hdpaSelectionRanges
->nItemCount
>0);
1736 * Removes a range selections.
1739 * [I] HWND : window handle
1740 * [I] INT : lower item index
1741 * [I] INT : upper item index
1746 static VOID
LISTVIEW_RemoveSelectionRange(HWND hwnd
, INT lItem
, INT uItem
)
1748 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1749 LISTVIEW_SELECTION removeselection
,*checkselection
;
1752 removeselection
.lower
= lItem
;
1753 removeselection
.upper
= uItem
;
1755 TRACE("Remove range %lu - %lu\n",removeselection
.lower
,removeselection
.upper
);
1756 LISTVIEW_PrintSelectionRanges(hwnd
);
1758 index
= DPA_Search(infoPtr
->hdpaSelectionRanges
, &removeselection
, 0,
1759 LISTVIEW_CompareSelectionRanges
,
1766 checkselection
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
,
1769 TRACE("Matches range index %i (%lu-%lu)\n",index
,checkselection
->lower
,
1770 checkselection
->upper
);
1773 if ((checkselection
->upper
== removeselection
.upper
) &&
1774 (checkselection
->lower
== removeselection
.lower
))
1776 DPA_DeletePtr(infoPtr
->hdpaSelectionRanges
,index
);
1779 /* case 2: engulf */
1780 else if (((checkselection
->upper
< removeselection
.upper
) &&
1781 (checkselection
->lower
> removeselection
.lower
))||
1782 ((checkselection
->upper
<= removeselection
.upper
) &&
1783 (checkselection
->lower
> removeselection
.lower
)) ||
1784 ((checkselection
->upper
< removeselection
.upper
) &&
1785 (checkselection
->lower
>= removeselection
.lower
)))
1788 DPA_DeletePtr(infoPtr
->hdpaSelectionRanges
,index
);
1789 /* do it again because others may also get caught */
1791 LISTVIEW_RemoveSelectionRange(hwnd
,lItem
,uItem
);
1793 /* case 3: overlap upper */
1794 else if ((checkselection
->upper
< removeselection
.upper
) &&
1795 (checkselection
->lower
< removeselection
.lower
))
1797 checkselection
->upper
= removeselection
.lower
- 1;
1799 LISTVIEW_RemoveSelectionRange(hwnd
,lItem
,uItem
);
1801 /* case 4: overlap lower */
1802 else if ((checkselection
->upper
> removeselection
.upper
) &&
1803 (checkselection
->lower
> removeselection
.lower
))
1805 checkselection
->lower
= removeselection
.upper
+ 1;
1807 LISTVIEW_RemoveSelectionRange(hwnd
,lItem
,uItem
);
1809 /* case 5: fully internal */
1810 else if (checkselection
->upper
== removeselection
.upper
)
1811 checkselection
->upper
= removeselection
.lower
- 1;
1812 else if (checkselection
->lower
== removeselection
.lower
)
1813 checkselection
->lower
= removeselection
.upper
+ 1;
1816 /* bisect the range */
1817 LISTVIEW_SELECTION
*newselection
;
1819 newselection
= (LISTVIEW_SELECTION
*)
1820 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION
));
1821 newselection
-> lower
= checkselection
->lower
;
1822 newselection
-> upper
= removeselection
.lower
- 1;
1823 checkselection
-> lower
= removeselection
.upper
+ 1;
1824 DPA_InsertPtr(infoPtr
->hdpaSelectionRanges
,index
,newselection
);
1826 DPA_Sort(infoPtr
->hdpaSelectionRanges
,LISTVIEW_CompareSelectionRanges
,0);
1828 LISTVIEW_PrintSelectionRanges(hwnd
);
1833 * Updates the various indices after an item has been inserted or deleted.
1836 * [I] HWND : window handle
1837 * [I] INT : item index
1838 * [I] INT : Direction of shift, +1 or -1.
1843 static VOID
LISTVIEW_ShiftIndices(HWND hwnd
, INT nItem
, INT direction
)
1845 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1846 LISTVIEW_SELECTION selection
,*checkselection
;
1849 TRACE("Shifting %iu, %i steps\n",nItem
,direction
);
1851 selection
.upper
= nItem
;
1852 selection
.lower
= nItem
;
1854 index
= DPA_Search(infoPtr
->hdpaSelectionRanges
, &selection
, 0,
1855 LISTVIEW_CompareSelectionRanges
,
1856 0,DPAS_SORTED
|DPAS_INSERTAFTER
);
1858 while ((index
< infoPtr
->hdpaSelectionRanges
->nItemCount
)&&(index
!= -1))
1860 checkselection
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
,index
);
1861 if ((checkselection
->lower
>= nItem
)&&
1862 ((int)(checkselection
->lower
+ direction
) >= 0))
1863 checkselection
->lower
+= direction
;
1864 if ((checkselection
->upper
>= nItem
)&&
1865 ((int)(checkselection
->upper
+ direction
) >= 0))
1866 checkselection
->upper
+= direction
;
1870 /* Note that the following will fail if direction != +1 and -1 */
1871 if (infoPtr
->nSelectionMark
> nItem
)
1872 infoPtr
->nSelectionMark
+= direction
;
1873 else if (infoPtr
->nSelectionMark
== nItem
)
1876 infoPtr
->nSelectionMark
+= direction
;
1877 else if (infoPtr
->nSelectionMark
>= GETITEMCOUNT(infoPtr
))
1878 infoPtr
->nSelectionMark
= GETITEMCOUNT(infoPtr
) - 1;
1881 if (infoPtr
->nFocusedItem
> nItem
)
1882 infoPtr
->nFocusedItem
+= direction
;
1883 else if (infoPtr
->nFocusedItem
== nItem
)
1886 infoPtr
->nFocusedItem
+= direction
;
1889 if (infoPtr
->nFocusedItem
>= GETITEMCOUNT(infoPtr
))
1890 infoPtr
->nFocusedItem
= GETITEMCOUNT(infoPtr
) - 1;
1891 if (infoPtr
->nFocusedItem
>= 0)
1892 LISTVIEW_SetItemFocus(hwnd
, infoPtr
->nFocusedItem
);
1895 /* But we are not supposed to modify nHotItem! */
1901 * Adds a block of selections.
1904 * [I] HWND : window handle
1905 * [I] INT : item index
1910 static VOID
LISTVIEW_AddGroupSelection(HWND hwnd
, INT nItem
)
1912 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1913 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
1914 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
1921 ZeroMemory(&item
,sizeof(item
));
1922 item
.stateMask
= LVIS_SELECTED
;
1923 item
.state
= LVIS_SELECTED
;
1925 for (i
= nFirst
; i
<= nLast
; i
++)
1926 LISTVIEW_SetItemState(hwnd
,i
,&item
);
1928 LISTVIEW_SetItemFocus(hwnd
, nItem
);
1929 infoPtr
->nSelectionMark
= nItem
;
1935 * Adds a single selection.
1938 * [I] HWND : window handle
1939 * [I] INT : item index
1944 static VOID
LISTVIEW_AddSelection(HWND hwnd
, INT nItem
)
1946 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1949 ZeroMemory(&item
,sizeof(item
));
1950 item
.state
= LVIS_SELECTED
;
1951 item
.stateMask
= LVIS_SELECTED
;
1953 LISTVIEW_SetItemState(hwnd
,nItem
,&item
);
1955 LISTVIEW_SetItemFocus(hwnd
, nItem
);
1956 infoPtr
->nSelectionMark
= nItem
;
1961 * Selects or unselects an item.
1964 * [I] HWND : window handle
1965 * [I] INT : item index
1971 static BOOL
LISTVIEW_ToggleSelection(HWND hwnd
, INT nItem
)
1973 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
1977 ZeroMemory(&item
,sizeof(item
));
1978 item
.stateMask
= LVIS_SELECTED
;
1980 if (LISTVIEW_IsSelected(hwnd
,nItem
))
1982 LISTVIEW_SetItemState(hwnd
,nItem
,&item
);
1987 item
.state
= LVIS_SELECTED
;
1988 LISTVIEW_SetItemState(hwnd
,nItem
,&item
);
1992 LISTVIEW_SetItemFocus(hwnd
, nItem
);
1993 infoPtr
->nSelectionMark
= nItem
;
2000 * Selects items based on view coordinates.
2003 * [I] HWND : window handle
2004 * [I] RECT : selection rectangle
2009 static VOID
LISTVIEW_SetSelectionRect(HWND hwnd
, RECT rcSelRect
)
2011 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
2016 ZeroMemory(&item
,sizeof(item
));
2017 item
.stateMask
= LVIS_SELECTED
;
2019 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
2021 LISTVIEW_GetItemPosition(hwnd
, i
, &ptItem
);
2023 if (PtInRect(&rcSelRect
, ptItem
) != FALSE
)
2024 item
.state
= LVIS_SELECTED
;
2027 LISTVIEW_SetItemState(hwnd
,i
,&item
);
2033 * Sets a single group selection.
2036 * [I] HWND : window handle
2037 * [I] INT : item index
2042 static VOID
LISTVIEW_SetGroupSelection(HWND hwnd
, INT nItem
)
2044 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
2045 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
2048 ZeroMemory(&item
,sizeof(item
));
2049 item
.stateMask
= LVIS_SELECTED
;
2051 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
2056 if (infoPtr
->nSelectionMark
== -1)
2058 infoPtr
->nSelectionMark
= nFirst
= nLast
= nItem
;
2062 nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
2063 nLast
= max(infoPtr
->nSelectionMark
, nItem
);
2066 for (i
= 0; i
<= GETITEMCOUNT(infoPtr
); i
++)
2068 if ((i
< nFirst
) || (i
> nLast
))
2071 item
.state
= LVIS_SELECTED
;
2072 LISTVIEW_SetItemState(hwnd
,i
,&item
);
2080 LISTVIEW_GetItemBoundBox(hwnd
, nItem
, &rcItem
);
2081 LISTVIEW_GetItemBoundBox(hwnd
, infoPtr
->nSelectionMark
, &rcSelMark
);
2082 rcSel
.left
= min(rcSelMark
.left
, rcItem
.left
);
2083 rcSel
.top
= min(rcSelMark
.top
, rcItem
.top
);
2084 rcSel
.right
= max(rcSelMark
.right
, rcItem
.right
);
2085 rcSel
.bottom
= max(rcSelMark
.bottom
, rcItem
.bottom
);
2086 LISTVIEW_SetSelectionRect(hwnd
, rcSel
);
2087 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2088 nItem
, rcItem
.left
, rcItem
.top
, rcItem
.right
, rcItem
.bottom
,
2089 infoPtr
->nSelectionMark
,
2090 rcSelMark
.left
, rcSelMark
.top
, rcSelMark
.right
, rcSelMark
.bottom
,
2091 rcSel
.left
, rcSel
.top
, rcSel
.right
, rcSel
.bottom
);
2095 LISTVIEW_SetItemFocus(hwnd
, nItem
);
2100 * Manages the item focus.
2103 * [I] HWND : window handle
2104 * [I] INT : item index
2107 * TRUE : focused item changed
2108 * FALSE : focused item has NOT changed
2110 static BOOL
LISTVIEW_SetItemFocus(HWND hwnd
, INT nItem
)
2112 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
2113 BOOL bResult
= FALSE
;
2116 if (infoPtr
->nFocusedItem
!= nItem
)
2118 if (infoPtr
->nFocusedItem
>= 0)
2120 INT oldFocus
= infoPtr
->nFocusedItem
;
2122 infoPtr
->nFocusedItem
= -1;
2123 ZeroMemory(&lvItem
, sizeof(lvItem
));
2124 lvItem
.stateMask
= LVIS_FOCUSED
;
2125 ListView_SetItemState(hwnd
, oldFocus
, &lvItem
);
2129 lvItem
.state
= LVIS_FOCUSED
;
2130 lvItem
.stateMask
= LVIS_FOCUSED
;
2131 ListView_SetItemState(hwnd
, nItem
, &lvItem
);
2133 infoPtr
->nFocusedItem
= nItem
;
2134 ListView_EnsureVisible(hwnd
, nItem
, FALSE
);
2142 * Sets a single selection.
2145 * [I] HWND : window handle
2146 * [I] INT : item index
2151 static VOID
LISTVIEW_SetSelection(HWND hwnd
, INT nItem
)
2153 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
2156 ZeroMemory(&lvItem
, sizeof(lvItem
));
2157 lvItem
.stateMask
= LVIS_FOCUSED
;
2158 ListView_SetItemState(hwnd
, infoPtr
->nFocusedItem
, &lvItem
);
2160 LISTVIEW_RemoveAllSelections(hwnd
);
2162 lvItem
.state
= LVIS_FOCUSED
|LVIS_SELECTED
;
2163 lvItem
.stateMask
= LVIS_FOCUSED
|LVIS_SELECTED
;
2164 ListView_SetItemState(hwnd
, nItem
, &lvItem
);
2166 infoPtr
->nFocusedItem
= nItem
;
2167 infoPtr
->nSelectionMark
= nItem
;
2172 * Set selection(s) with keyboard.
2175 * [I] HWND : window handle
2176 * [I] INT : item index
2179 * SUCCESS : TRUE (needs to be repainted)
2180 * FAILURE : FALSE (nothing has changed)
2182 static BOOL
LISTVIEW_KeySelection(HWND hwnd
, INT nItem
)
2184 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
2185 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
2186 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
2187 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
2188 BOOL bResult
= FALSE
;
2190 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
2192 if (lStyle
& LVS_SINGLESEL
)
2195 LISTVIEW_SetSelection(hwnd
, nItem
);
2196 ListView_EnsureVisible(hwnd
, nItem
, FALSE
);
2203 LISTVIEW_SetGroupSelection(hwnd
, nItem
);
2207 bResult
= LISTVIEW_SetItemFocus(hwnd
, nItem
);
2212 LISTVIEW_SetSelection(hwnd
, nItem
);
2213 ListView_EnsureVisible(hwnd
, nItem
, FALSE
);
2223 * Called when the mouse is being actively tracked and has hovered for a specified
2227 * [I] HWND : window handle
2228 * [I] wParam : key indicator
2229 * [I] lParam : mouse position
2232 * 0 if the message was processed, non-zero if there was an error
2235 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2236 * over the item for a certain period of time.
2239 static LRESULT
LISTVIEW_MouseHover(HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2241 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
2244 pt
.x
= (INT
)LOWORD(lParam
);
2245 pt
.y
= (INT
)HIWORD(lParam
);
2247 if(infoPtr
->dwExStyle
& LVS_EX_TRACKSELECT
) {
2248 /* select the item under the cursor */
2249 LISTVIEW_MouseSelection(hwnd
, pt
);
2257 * Called whenever WM_MOUSEMOVE is received.
2260 * [I] HWND : window handle
2261 * [I] wParam : key indicators
2262 * [I] lParam : cursor position
2265 * 0 if the message is processed, non-zero if there was an error
2267 static LRESULT
LISTVIEW_MouseMove(HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
2269 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
2270 TRACKMOUSEEVENT trackinfo
;
2272 /* see if we are supposed to be tracking mouse hovering */
2273 if(infoPtr
->dwExStyle
& LVS_EX_TRACKSELECT
) {
2274 /* fill in the trackinfo struct */
2275 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
2276 trackinfo
.dwFlags
= TME_QUERY
;
2277 trackinfo
.hwndTrack
= hwnd
;
2278 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
2280 /* see if we are already tracking this hwnd */
2281 _TrackMouseEvent(&trackinfo
);
2283 if(!(trackinfo
.dwFlags
& TME_HOVER
)) {
2284 trackinfo
.dwFlags
= TME_HOVER
;
2286 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2287 _TrackMouseEvent(&trackinfo
);
2296 * Selects an item based on coordinates.
2299 * [I] HWND : window handle
2300 * [I] POINT : mouse click ccordinates
2303 * SUCCESS : item index
2306 static LRESULT
LISTVIEW_MouseSelection(HWND hwnd
, POINT pt
)
2308 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
2310 INT i
,topindex
,bottomindex
;
2311 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
2312 UINT uView
= lStyle
& LVS_TYPEMASK
;
2314 topindex
= ListView_GetTopIndex(hwnd
);
2315 if (uView
== LVS_REPORT
)
2317 bottomindex
= topindex
+ LISTVIEW_GetCountPerColumn(hwnd
) + 1;
2318 bottomindex
= min(bottomindex
,GETITEMCOUNT(infoPtr
));
2322 bottomindex
= GETITEMCOUNT(infoPtr
);
2325 for (i
= topindex
; i
< bottomindex
; i
++)
2327 rcItem
.left
= LVIR_SELECTBOUNDS
;
2328 if (LISTVIEW_GetItemRect(hwnd
, i
, &rcItem
) == TRUE
)
2330 if (PtInRect(&rcItem
, pt
) != FALSE
)
2345 * [IO] HDPA : dynamic pointer array handle
2346 * [I] INT : column index (subitem index)
2352 static BOOL
LISTVIEW_RemoveColumn(HDPA hdpaItems
, INT nSubItem
)
2354 BOOL bResult
= TRUE
;
2358 for (i
= 0; i
< hdpaItems
->nItemCount
; i
++)
2360 hdpaSubItems
= (HDPA
)DPA_GetPtr(hdpaItems
, i
);
2361 if (hdpaSubItems
!= NULL
)
2363 if (LISTVIEW_RemoveSubItem(hdpaSubItems
, nSubItem
) == FALSE
)
2375 * Removes a subitem at a given position.
2378 * [IO] HDPA : dynamic pointer array handle
2379 * [I] INT : subitem index
2385 static BOOL
LISTVIEW_RemoveSubItem(HDPA hdpaSubItems
, INT nSubItem
)
2387 LISTVIEW_SUBITEM
*lpSubItem
;
2390 for (i
= 1; i
< hdpaSubItems
->nItemCount
; i
++)
2392 lpSubItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, i
);
2393 if (lpSubItem
!= NULL
)
2395 if (lpSubItem
->iSubItem
== nSubItem
)
2398 if (is_textW(lpSubItem
->pszText
))
2399 COMCTL32_Free(lpSubItem
->pszText
);
2402 COMCTL32_Free(lpSubItem
);
2404 /* free dpa memory */
2405 if (DPA_DeletePtr(hdpaSubItems
, i
) == NULL
)
2408 else if (lpSubItem
->iSubItem
> nSubItem
)
2418 * Compares the item information.
2421 * [I] LISTVIEW_ITEM *: destination item
2422 * [I] LPLVITEM : source item
2423 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2426 * SUCCCESS : TRUE (EQUAL)
2427 * FAILURE : FALSE (NOT EQUAL)
2429 static UINT
LISTVIEW_GetItemChangesT(LISTVIEW_ITEM
*lpItem
, LPLVITEMW lpLVItem
, BOOL isW
)
2433 if ((lpItem
!= NULL
) && (lpLVItem
!= NULL
))
2435 if (lpLVItem
->mask
& LVIF_STATE
)
2437 if ((lpItem
->state
& lpLVItem
->stateMask
) !=
2438 (lpLVItem
->state
& lpLVItem
->stateMask
))
2439 uChanged
|= LVIF_STATE
;
2442 if (lpLVItem
->mask
& LVIF_IMAGE
)
2444 if (lpItem
->iImage
!= lpLVItem
->iImage
)
2445 uChanged
|= LVIF_IMAGE
;
2448 if (lpLVItem
->mask
& LVIF_PARAM
)
2450 if (lpItem
->lParam
!= lpLVItem
->lParam
)
2451 uChanged
|= LVIF_PARAM
;
2454 if (lpLVItem
->mask
& LVIF_INDENT
)
2456 if (lpItem
->iIndent
!= lpLVItem
->iIndent
)
2457 uChanged
|= LVIF_INDENT
;
2460 if (lpLVItem
->mask
& LVIF_TEXT
)
2462 if (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
)
2464 if (lpItem
->pszText
!= LPSTR_TEXTCALLBACKW
)
2465 uChanged
|= LVIF_TEXT
;
2469 if (lpItem
->pszText
== LPSTR_TEXTCALLBACKW
)
2471 uChanged
|= LVIF_TEXT
;
2475 if (lpLVItem
->pszText
)
2477 if (lpItem
->pszText
)
2479 LPWSTR pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
2480 if (pszText
&& strcmpW(pszText
, lpItem
->pszText
))
2481 uChanged
|= LVIF_TEXT
;
2482 textfreeT(pszText
, isW
);
2486 uChanged
|= LVIF_TEXT
;
2491 if (lpItem
->pszText
)
2492 uChanged
|= LVIF_TEXT
;
2503 * Initializes item attributes.
2506 * [I] HWND : window handle
2507 * [O] LISTVIEW_ITEM *: destination item
2508 * [I] LPLVITEM : source item
2509 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2515 static BOOL
LISTVIEW_InitItemT(HWND hwnd
, LISTVIEW_ITEM
*lpItem
,
2516 LPLVITEMW lpLVItem
, BOOL isW
)
2518 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
2519 BOOL bResult
= FALSE
;
2521 if ((lpItem
!= NULL
) && (lpLVItem
!= NULL
))
2525 if (lpLVItem
->mask
& LVIF_STATE
)
2527 lpItem
->state
&= ~lpLVItem
->stateMask
;
2528 lpItem
->state
|= (lpLVItem
->state
& lpLVItem
->stateMask
);
2531 if (lpLVItem
->mask
& LVIF_IMAGE
)
2532 lpItem
->iImage
= lpLVItem
->iImage
;
2534 if (lpLVItem
->mask
& LVIF_PARAM
)
2535 lpItem
->lParam
= lpLVItem
->lParam
;
2537 if (lpLVItem
->mask
& LVIF_INDENT
)
2538 lpItem
->iIndent
= lpLVItem
->iIndent
;
2540 if (lpLVItem
->mask
& LVIF_TEXT
)
2542 if (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
)
2544 if ((lStyle
& LVS_SORTASCENDING
) || (lStyle
& LVS_SORTDESCENDING
))
2547 if (is_textW(lpItem
->pszText
))
2548 COMCTL32_Free(lpItem
->pszText
);
2550 lpItem
->pszText
= LPSTR_TEXTCALLBACKW
;
2553 bResult
= textsetptrT(&lpItem
->pszText
, lpLVItem
->pszText
, isW
);
2562 * Initializes subitem attributes.
2564 * NOTE: The documentation specifies that the operation fails if the user
2565 * tries to set the indent of a subitem.
2568 * [I] HWND : window handle
2569 * [O] LISTVIEW_SUBITEM *: destination subitem
2570 * [I] LPLVITEM : source subitem
2571 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2577 static BOOL
LISTVIEW_InitSubItemT(HWND hwnd
, LISTVIEW_SUBITEM
*lpSubItem
,
2578 LPLVITEMW lpLVItem
, BOOL isW
)
2580 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
2581 BOOL bResult
= FALSE
;
2583 TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
2584 hwnd
, lpSubItem
, debuglvitem_t(lpLVItem
, isW
), isW
);
2586 if ((lpSubItem
!= NULL
) && (lpLVItem
!= NULL
))
2588 if (!(lpLVItem
->mask
& LVIF_INDENT
))
2592 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
2594 if (lpLVItem
->mask
& LVIF_IMAGE
)
2595 lpSubItem
->iImage
= lpLVItem
->iImage
;
2597 if (lpLVItem
->mask
& LVIF_TEXT
)
2599 if (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
)
2601 if ((lStyle
& LVS_SORTASCENDING
) || (lStyle
& LVS_SORTDESCENDING
))
2604 if (is_textW(lpSubItem
->pszText
))
2605 COMCTL32_Free(lpSubItem
->pszText
);
2607 lpSubItem
->pszText
= LPSTR_TEXTCALLBACKW
;
2610 bResult
= textsetptrT(&lpSubItem
->pszText
, lpLVItem
->pszText
, isW
);
2620 * Adds a subitem at a given position (column index).
2623 * [I] HWND : window handle
2624 * [I] LPLVITEM : new subitem atttributes
2625 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2631 static BOOL
LISTVIEW_AddSubItemT(HWND hwnd
, LPLVITEMW lpLVItem
, BOOL isW
)
2633 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
2634 LISTVIEW_SUBITEM
*lpSubItem
= NULL
;
2635 BOOL bResult
= FALSE
;
2637 INT nPosition
, nItem
;
2638 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
2640 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd
, debuglvitem_t(lpLVItem
, isW
), isW
);
2642 if (lStyle
& LVS_OWNERDATA
)
2645 if (lpLVItem
!= NULL
)
2647 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
2648 if (hdpaSubItems
!= NULL
)
2650 lpSubItem
= (LISTVIEW_SUBITEM
*)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM
));
2651 if (lpSubItem
!= NULL
)
2653 ZeroMemory(lpSubItem
, sizeof(LISTVIEW_SUBITEM
));
2654 if (LISTVIEW_InitSubItemT(hwnd
, lpSubItem
, lpLVItem
, isW
))
2656 nPosition
= LISTVIEW_FindInsertPosition(hdpaSubItems
,
2657 lpSubItem
->iSubItem
);
2658 nItem
= DPA_InsertPtr(hdpaSubItems
, nPosition
, lpSubItem
);
2659 if (nItem
!= -1) bResult
= TRUE
;
2665 /* cleanup if unsuccessful */
2666 if (!bResult
&& lpSubItem
) COMCTL32_Free(lpSubItem
);
2673 * Finds the dpa insert position (array index).
2676 * [I] HWND : window handle
2677 * [I] INT : subitem index
2683 static INT
LISTVIEW_FindInsertPosition(HDPA hdpaSubItems
, INT nSubItem
)
2685 LISTVIEW_SUBITEM
*lpSubItem
;
2688 for (i
= 1; i
< hdpaSubItems
->nItemCount
; i
++)
2690 lpSubItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, i
);
2691 if (lpSubItem
&& lpSubItem
->iSubItem
> nSubItem
)
2695 return hdpaSubItems
->nItemCount
;
2700 * Retrieves a listview subitem at a given position (column index).
2703 * [I] HWND : window handle
2704 * [I] INT : subitem index
2710 static LISTVIEW_SUBITEM
* LISTVIEW_GetSubItem(HDPA hdpaSubItems
, INT nSubItem
)
2712 LISTVIEW_SUBITEM
*lpSubItem
;
2715 for (i
= 1; i
< hdpaSubItems
->nItemCount
; i
++)
2717 lpSubItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, i
);
2718 if (lpSubItem
!= NULL
)
2720 if (lpSubItem
->iSubItem
== nSubItem
)
2722 else if (lpSubItem
->iSubItem
> nSubItem
)
2732 * Sets item attributes.
2735 * [I] HWND : window handle
2736 * [I] LPLVITEM : new item atttributes
2737 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2743 static BOOL
LISTVIEW_SetMainItemT(HWND hwnd
, LPLVITEMW lpLVItem
, BOOL isW
)
2745 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
2746 BOOL bResult
= FALSE
;
2748 LISTVIEW_ITEM
*lpItem
;
2750 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
2752 UINT uView
= lStyle
& LVS_TYPEMASK
;
2756 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd
, debuglvitem_t(lpLVItem
, isW
), isW
);
2758 if (lStyle
& LVS_OWNERDATA
)
2760 if ((lpLVItem
->iSubItem
== 0)&&(lpLVItem
->mask
== LVIF_STATE
))
2764 ZeroMemory(&itm
, sizeof(itm
));
2765 itm
.mask
= LVIF_STATE
| LVIF_PARAM
;
2766 itm
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
2767 itm
.iItem
= lpLVItem
->iItem
;
2769 ListView_GetItemW(hwnd
, &itm
);
2772 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
2773 nmlv
.uNewState
= lpLVItem
->state
;
2774 nmlv
.uOldState
= itm
.state
;
2775 nmlv
.uChanged
= LVIF_STATE
;
2776 nmlv
.lParam
= itm
.lParam
;
2777 nmlv
.iItem
= lpLVItem
->iItem
;
2779 if ((itm
.state
& lpLVItem
->stateMask
) !=
2780 (lpLVItem
->state
& lpLVItem
->stateMask
))
2783 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent
2784 * by LVS_OWERNDATA list controls
2786 if (lpLVItem
->stateMask
& LVIS_FOCUSED
)
2788 if (lpLVItem
->state
& LVIS_FOCUSED
)
2789 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
2790 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
2791 infoPtr
->nFocusedItem
= -1;
2793 if (lpLVItem
->stateMask
& LVIS_SELECTED
)
2795 if (lpLVItem
->state
& LVIS_SELECTED
)
2797 if (lStyle
& LVS_SINGLESEL
) LISTVIEW_RemoveAllSelections(hwnd
);
2798 LISTVIEW_AddSelectionRange(hwnd
,lpLVItem
->iItem
,lpLVItem
->iItem
);
2801 LISTVIEW_RemoveSelectionRange(hwnd
,lpLVItem
->iItem
,
2805 listview_notify(hwnd
, LVN_ITEMCHANGED
, &nmlv
);
2807 rcItem
.left
= LVIR_BOUNDS
;
2808 LISTVIEW_GetItemRect(hwnd
, lpLVItem
->iItem
, &rcItem
);
2809 if (!infoPtr
->bIsDrawing
)
2810 InvalidateRect(hwnd
, &rcItem
, TRUE
);
2817 if (lpLVItem
!= NULL
)
2819 if (lpLVItem
->iSubItem
== 0)
2821 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
2822 if (hdpaSubItems
!= NULL
&& hdpaSubItems
!= (HDPA
)-1)
2824 lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
2827 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
2828 nmlv
.lParam
= lpItem
->lParam
;
2829 uChanged
= LISTVIEW_GetItemChangesT(lpItem
, lpLVItem
, isW
);
2832 if (uChanged
& LVIF_STATE
)
2834 nmlv
.uNewState
= lpLVItem
->state
& lpLVItem
->stateMask
;
2835 nmlv
.uOldState
= lpItem
->state
& lpLVItem
->stateMask
;
2837 if (nmlv
.uNewState
& LVIS_SELECTED
)
2840 * This is redundant if called through SetSelection
2842 * however is required if the used directly calls SetItem
2843 * to set the selection.
2845 if (lStyle
& LVS_SINGLESEL
)
2846 LISTVIEW_RemoveAllSelections(hwnd
);
2848 LISTVIEW_AddSelectionRange(hwnd
,lpLVItem
->iItem
,
2851 else if (lpLVItem
->stateMask
& LVIS_SELECTED
)
2853 LISTVIEW_RemoveSelectionRange(hwnd
,lpLVItem
->iItem
,
2856 if (nmlv
.uNewState
& LVIS_FOCUSED
)
2859 * This is a fun hoop to jump to try to catch if
2860 * the user is calling us directly to call focus or if
2861 * this function is being called as a result of a
2862 * SetItemFocus call.
2864 if (infoPtr
->nFocusedItem
>= 0)
2865 LISTVIEW_SetItemFocus(hwnd
, lpLVItem
->iItem
);
2869 nmlv
.uChanged
= uChanged
;
2870 nmlv
.iItem
= lpLVItem
->iItem
;
2871 nmlv
.lParam
= lpItem
->lParam
;
2872 /* send LVN_ITEMCHANGING notification */
2873 listview_notify(hwnd
, LVN_ITEMCHANGING
, &nmlv
);
2875 /* copy information */
2876 bResult
= LISTVIEW_InitItemT(hwnd
, lpItem
, lpLVItem
, isW
);
2878 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2879 based on the width of the items text */
2880 if((uView
== LVS_LIST
) || (uView
== LVS_SMALLICON
))
2882 item_width
= LISTVIEW_GetStringWidthT(hwnd
, lpItem
->pszText
, TRUE
);
2884 if(item_width
> infoPtr
->nItemWidth
)
2885 infoPtr
->nItemWidth
= item_width
;
2888 /* send LVN_ITEMCHANGED notification */
2889 listview_notify(hwnd
, LVN_ITEMCHANGED
, &nmlv
);
2898 rcItem
.left
= LVIR_BOUNDS
;
2899 LISTVIEW_GetItemRect(hwnd
, lpLVItem
->iItem
, &rcItem
);
2900 if (!infoPtr
->bIsDrawing
)
2901 InvalidateRect(hwnd
, &rcItem
, TRUE
);
2913 * Sets subitem attributes.
2916 * [I] HWND : window handle
2917 * [I] LPLVITEM : new subitem atttributes
2918 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2924 static BOOL
LISTVIEW_SetSubItemT(HWND hwnd
, LPLVITEMW lpLVItem
, BOOL isW
)
2926 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
2927 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
2928 BOOL bResult
= FALSE
;
2930 LISTVIEW_SUBITEM
*lpSubItem
;
2933 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd
, debuglvitem_t(lpLVItem
, isW
), isW
);
2935 if (lStyle
& LVS_OWNERDATA
)
2938 if (lpLVItem
!= NULL
)
2940 if (lpLVItem
->iSubItem
> 0)
2942 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
2943 if (hdpaSubItems
!= NULL
)
2945 /* set subitem only if column is present */
2946 if (Header_GetItemCount(infoPtr
->hwndHeader
) > lpLVItem
->iSubItem
)
2948 lpSubItem
= LISTVIEW_GetSubItem(hdpaSubItems
, lpLVItem
->iSubItem
);
2949 if (lpSubItem
!= NULL
)
2950 bResult
= LISTVIEW_InitSubItemT(hwnd
, lpSubItem
, lpLVItem
, isW
);
2952 bResult
= LISTVIEW_AddSubItemT(hwnd
, lpLVItem
, isW
);
2954 rcItem
.left
= LVIR_BOUNDS
;
2955 LISTVIEW_GetItemRect(hwnd
, lpLVItem
->iItem
, &rcItem
);
2956 InvalidateRect(hwnd
, &rcItem
, FALSE
);
2967 * Sets item attributes.
2970 * [I] HWND : window handle
2971 * [I] LPLVITEM : new item atttributes
2972 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2978 static BOOL
LISTVIEW_SetItemT(HWND hwnd
, LPLVITEMW lpLVItem
, BOOL isW
)
2980 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
2982 if (!lpLVItem
|| lpLVItem
->iItem
< 0 ||
2983 lpLVItem
->iItem
>=GETITEMCOUNT(infoPtr
))
2985 if (lpLVItem
->iSubItem
== 0)
2986 return LISTVIEW_SetMainItemT(hwnd
, lpLVItem
, isW
);
2988 return LISTVIEW_SetSubItemT(hwnd
, lpLVItem
, isW
);
2993 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2996 * [I] HWND : window handle
3001 static INT
LISTVIEW_GetTopIndex(HWND hwnd
)
3003 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
3004 UINT uView
= lStyle
& LVS_TYPEMASK
;
3006 SCROLLINFO scrollInfo
;
3008 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
3009 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3010 scrollInfo
.fMask
= SIF_POS
;
3012 if (uView
== LVS_LIST
)
3014 if ((lStyle
& WS_HSCROLL
) && GetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
))
3015 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(hwnd
);
3017 else if (uView
== LVS_REPORT
)
3019 if ((lStyle
& WS_VSCROLL
) && GetScrollInfo(hwnd
, SB_VERT
, &scrollInfo
))
3020 nItem
= scrollInfo
.nPos
;
3031 * [I] HWND : window handle
3032 * [I] HDC : device context handle
3033 * [I] INT : item index
3034 * [I] INT : subitem index
3035 * [I] RECT * : clipping rectangle
3040 static VOID
LISTVIEW_DrawSubItem(HWND hwnd
, HDC hdc
, INT nItem
, INT nSubItem
,
3041 RECT rcItem
, BOOL Selected
)
3043 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
3044 WCHAR szDispText
[DISP_TEXT_SIZE
];
3047 UINT textoutOptions
= ETO_CLIPPED
| ETO_OPAQUE
;
3050 INT nLabelWidth
= 0;
3052 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd
, hdc
,
3055 /* get information needed for drawing the item */
3056 ZeroMemory(&lvItem
, sizeof(lvItem
));
3057 lvItem
.mask
= LVIF_TEXT
;
3058 lvItem
.iItem
= nItem
;
3059 lvItem
.iSubItem
= nSubItem
;
3060 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3061 lvItem
.pszText
= szDispText
;
3062 *lvItem
.pszText
= '\0';
3063 LISTVIEW_GetItemW(hwnd
, &lvItem
, TRUE
);
3064 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3066 ZeroMemory(&lvColumn
, sizeof(lvColumn
));
3067 lvColumn
.mask
= LVCF_FMT
;
3068 LISTVIEW_GetColumnT(hwnd
, nSubItem
, &lvColumn
, TRUE
);
3069 textLeft
= rcItem
.left
;
3070 TRACE("lvColumn.fmt=%d\n", lvColumn
.fmt
);
3071 if (lvColumn
.fmt
!= LVCFMT_LEFT
)
3073 if ((nLabelWidth
= LISTVIEW_GetStringWidthT(hwnd
, lvItem
.pszText
, TRUE
)))
3075 if (lvColumn
.fmt
== LVCFMT_RIGHT
)
3076 textLeft
= rcItem
.right
- nLabelWidth
;
3078 textLeft
= rcItem
.left
+ (rcItem
.right
-rcItem
.left
-nLabelWidth
)/2;
3083 /* redraw the background of the item */
3085 if(infoPtr
->nColumnCount
== (nSubItem
+ 1))
3086 rcTemp
.right
= infoPtr
->rcList
.right
;
3088 rcTemp
.right
+= WIDTH_PADDING
;
3090 LISTVIEW_FillBackground(hwnd
, hdc
, &rcTemp
);
3092 /* set item colors */
3093 if (ListView_GetItemState(hwnd
,nItem
,LVIS_SELECTED
) && Selected
)
3095 if (infoPtr
->bFocus
)
3097 SetBkColor(hdc
, comctl32_color
.clrHighlight
);
3098 SetTextColor(hdc
, comctl32_color
.clrHighlightText
);
3102 SetBkColor(hdc
, comctl32_color
.clr3dFace
);
3103 SetTextColor(hdc
, comctl32_color
.clrBtnText
);
3108 if ( (infoPtr
->clrTextBk
== CLR_DEFAULT
) || (infoPtr
->clrTextBk
== CLR_NONE
) )
3110 SetBkMode(hdc
, TRANSPARENT
);
3111 textoutOptions
&= ~ETO_OPAQUE
;
3115 SetBkMode(hdc
, OPAQUE
);
3116 SetBkColor(hdc
, infoPtr
->clrTextBk
);
3119 SetTextColor(hdc
, infoPtr
->clrText
);
3122 TRACE("drawing text %s, l=%d, t=%d, rect=(%d,%d)-(%d,%d)\n",
3123 debugstr_w(lvItem
.pszText
), textLeft
, rcItem
.top
,
3124 rcItem
.left
, rcItem
.top
, rcItem
.right
, rcItem
.bottom
);
3125 ExtTextOutW(hdc
, textLeft
, rcItem
.top
, textoutOptions
,
3126 &rcItem
, lvItem
.pszText
, lstrlenW(lvItem
.pszText
), NULL
);
3130 /* fill in the gap */
3132 if (nSubItem
< Header_GetItemCount(infoPtr
->hwndHeader
)-1)
3134 CopyRect(&rec
,&rcItem
);
3135 rec
.left
= rec
.right
;
3136 rec
.right
= rec
.left
+REPORT_MARGINX
;
3137 ExtTextOutW(hdc
, rec
.left
, rec
.top
, textoutOptions
,
3138 &rec
, NULL
, 0, NULL
);
3140 CopyRect(&rec
,&rcItem
);
3141 rec
.right
= rec
.left
;
3142 rec
.left
= rec
.left
- REPORT_MARGINX
;
3143 ExtTextOutW(hdc
, rec
.left
, rec
.top
, textoutOptions
,
3144 &rec
, NULL
, 0, NULL
);
3154 * [I] HWND : window handle
3155 * [I] HDC : device context handle
3156 * [I] INT : item index
3157 * [I] RECT * : clipping rectangle
3162 static VOID
LISTVIEW_DrawItem(HWND hwnd
, HDC hdc
, INT nItem
, RECT rcItem
, BOOL FullSelect
, RECT
* SuggestedFocus
)
3164 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
3165 WCHAR szDispText
[DISP_TEXT_SIZE
];
3170 DWORD dwTextColor
,dwTextX
;
3171 BOOL bImage
= FALSE
;
3173 UINT textoutOptions
= ETO_OPAQUE
| ETO_CLIPPED
;
3176 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd
, hdc
, nItem
);
3179 /* get information needed for drawing the item */
3180 ZeroMemory(&lvItem
, sizeof(lvItem
));
3181 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
| LVIF_INDENT
;
3182 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_STATEIMAGEMASK
;
3183 lvItem
.iItem
= nItem
;
3184 lvItem
.iSubItem
= 0;
3185 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3186 lvItem
.pszText
= szDispText
;
3187 *lvItem
.pszText
= '\0';
3188 LISTVIEW_GetItemW(hwnd
, &lvItem
, TRUE
);
3189 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3191 /* redraw the background of the item */
3193 if(infoPtr
->nColumnCount
== (nItem
+ 1))
3194 rcTemp
.right
= infoPtr
->rcList
.right
;
3196 rcTemp
.right
+=WIDTH_PADDING
;
3198 LISTVIEW_FillBackground(hwnd
, hdc
, &rcTemp
);
3201 if (lvItem
.iIndent
>0 && infoPtr
->iconSize
.cx
> 0)
3203 rcItem
.left
+= infoPtr
->iconSize
.cx
* lvItem
.iIndent
;
3206 SuggestedFocus
->left
+= infoPtr
->iconSize
.cx
* lvItem
.iIndent
;
3210 if (infoPtr
->himlState
!= NULL
)
3212 UINT uStateImage
= (lvItem
.state
& LVIS_STATEIMAGEMASK
) >> 12;
3213 if (uStateImage
> 0)
3215 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
, rcItem
.left
,
3216 rcItem
.top
, ILD_NORMAL
);
3219 rcItem
.left
+= infoPtr
->iconSize
.cx
;
3221 SuggestedFocus
->left
+= infoPtr
->iconSize
.cx
;
3226 if (infoPtr
->himlSmall
!= NULL
)
3228 if ((lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
!= FALSE
) &&
3231 ImageList_SetBkColor(infoPtr
->himlSmall
, CLR_NONE
);
3232 ImageList_Draw(infoPtr
->himlSmall
, lvItem
.iImage
, hdc
, rcItem
.left
,
3233 rcItem
.top
, ILD_SELECTED
);
3235 else if (lvItem
.iImage
>=0)
3237 ImageList_SetBkColor(infoPtr
->himlSmall
, CLR_NONE
);
3238 ImageList_Draw(infoPtr
->himlSmall
, lvItem
.iImage
, hdc
, rcItem
.left
,
3239 rcItem
.top
, ILD_NORMAL
);
3242 rcItem
.left
+= infoPtr
->iconSize
.cx
;
3245 SuggestedFocus
->left
+= infoPtr
->iconSize
.cx
;
3249 /* Don't bother painting item being edited */
3250 if (infoPtr
->hwndEdit
&& lvItem
.state
& LVIS_FOCUSED
&& !FullSelect
)
3253 if ((lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
!= FALSE
))
3255 /* set item colors */
3256 dwBkColor
= SetBkColor(hdc
, comctl32_color
.clrHighlight
);
3257 dwTextColor
= SetTextColor(hdc
, comctl32_color
.clrHighlightText
);
3258 /* set raster mode */
3259 nMixMode
= SetROP2(hdc
, R2_XORPEN
);
3261 else if ((GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_SHOWSELALWAYS
) &&
3262 (lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
== FALSE
))
3264 dwBkColor
= SetBkColor(hdc
, comctl32_color
.clr3dFace
);
3265 dwTextColor
= SetTextColor(hdc
, comctl32_color
.clrBtnText
);
3266 /* set raster mode */
3267 nMixMode
= SetROP2(hdc
, R2_COPYPEN
);
3271 /* set item colors */
3272 if ( (infoPtr
->clrTextBk
== CLR_DEFAULT
) || (infoPtr
->clrTextBk
== CLR_NONE
) )
3274 dwBkColor
= GetBkColor(hdc
);
3275 iBkMode
= SetBkMode(hdc
, TRANSPARENT
);
3276 textoutOptions
&= ~ETO_OPAQUE
;
3280 dwBkColor
= SetBkColor(hdc
, infoPtr
->clrTextBk
);
3281 iBkMode
= SetBkMode(hdc
, OPAQUE
);
3284 dwTextColor
= SetTextColor(hdc
, infoPtr
->clrText
);
3285 /* set raster mode */
3286 nMixMode
= SetROP2(hdc
, R2_COPYPEN
);
3289 nLabelWidth
= ListView_GetStringWidthW(hwnd
, lvItem
.pszText
);
3290 if (rcItem
.left
+ nLabelWidth
< rcItem
.right
)
3293 rcItem
.right
= rcItem
.left
+ nLabelWidth
+ TRAILING_PADDING
;
3295 rcItem
.right
+= IMAGE_PADDING
;
3299 dwTextX
= rcItem
.left
+ 1;
3301 dwTextX
+= IMAGE_PADDING
;
3303 if (lvItem
.pszText
) {
3304 TRACE("drawing text hwnd=%x, rect=(%d,%d)-(%d,%d)\n",
3305 hwnd
, rcItem
.left
, rcItem
.top
, rcItem
.right
, rcItem
.bottom
);
3306 ExtTextOutW(hdc
, dwTextX
, rcItem
.top
, textoutOptions
,
3307 &rcItem
, lvItem
.pszText
, lstrlenW(lvItem
.pszText
), NULL
);
3310 if ((FullSelect
)&&(Header_GetItemCount(infoPtr
->hwndHeader
) > 1))
3312 /* fill in the gap */
3314 CopyRect(&rec
,&rcItem
);
3315 rec
.left
= rec
.right
;
3316 rec
.right
= rec
.left
+REPORT_MARGINX
;
3317 ExtTextOutW(hdc
, rec
.left
, rec
.top
, textoutOptions
,
3318 &rec
, NULL
, 0, NULL
);
3322 CopyRect(SuggestedFocus
,&rcItem
);
3326 SetROP2(hdc
, R2_COPYPEN
);
3327 SetBkColor(hdc
, dwBkColor
);
3328 SetTextColor(hdc
, dwTextColor
);
3330 SetBkMode(hdc
, iBkMode
);
3336 * Draws an item when in large icon display mode.
3339 * [I] HWND : window handle
3340 * [I] HDC : device context handle
3341 * [I] INT : item index
3342 * [I] RECT : clipping rectangle
3343 * [O] RECT * : The text rectangle about which to draw the focus
3348 static VOID
LISTVIEW_DrawLargeItem(HWND hwnd
, HDC hdc
, INT nItem
, RECT rcItem
,
3349 RECT
*SuggestedFocus
)
3351 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
3352 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
3354 UINT uFormat
= DT_TOP
| DT_CENTER
| DT_WORDBREAK
| DT_NOPREFIX
|
3356 /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
3359 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3360 hwnd
, hdc
, nItem
, rcItem
.left
, rcItem
.top
, rcItem
.right
, rcItem
.bottom
);
3362 /* get information needed for drawing the item */
3363 ZeroMemory(&lvItem
, sizeof(lvItem
));
3364 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
;
3365 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
3366 lvItem
.iItem
= nItem
;
3367 lvItem
.iSubItem
= 0;
3368 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3369 lvItem
.pszText
= szDispText
;
3370 *lvItem
.pszText
= '\0';
3371 LISTVIEW_GetItemW(hwnd
, &lvItem
, FALSE
);
3372 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3374 /* redraw the background of the item */
3376 if(infoPtr
->nColumnCount
== (nItem
+ 1))
3377 rcTemp
.right
= infoPtr
->rcList
.right
;
3379 rcTemp
.right
+=WIDTH_PADDING
;
3380 /* The comment doesn't say WIDTH_PADDING applies to large icons */
3382 TRACE("background rect (%d,%d)-(%d,%d)\n",
3383 rcTemp
.left
, rcTemp
.top
, rcTemp
.right
, rcTemp
.bottom
);
3385 LISTVIEW_FillBackground(hwnd
, hdc
, &rcTemp
);
3388 /* Figure out text colours etc. depending on state
3389 * At least the following states exist; there may be more.
3390 * Many items may be selected
3391 * At most one item may have the focus
3392 * The application may not actually be active currently
3393 * 1. The item is not selected in any way
3394 * 2. The cursor is flying over the icon or text and the text is being
3395 * expanded because it is not fully displayed currently.
3396 * 3. The item is selected and is focussed, i.e. the user has not clicked
3397 * in the blank area of the window, and the window (or application?)
3398 * still has the focus.
3399 * 4. As 3 except that a different window has the focus
3400 * 5. The item is the selected item of all the items, but the user has
3401 * clicked somewhere else on the window.
3402 * Only a few of these are handled currently. In particular 2 is not yet
3403 * handled since we do not support the functionality currently (or at least
3404 * we didn't when I wrote this)
3407 if (lvItem
.state
& LVIS_SELECTED
)
3409 /* set item colors */
3410 SetBkColor(hdc
, comctl32_color
.clrHighlight
);
3411 SetTextColor(hdc
, comctl32_color
.clrHighlightText
);
3412 SetBkMode (hdc
, OPAQUE
);
3413 /* set raster mode */
3414 SetROP2(hdc
, R2_XORPEN
);
3415 /* When exactly is it in XOR? while being dragged? */
3419 /* set item colors */
3420 if ( (infoPtr
->clrTextBk
== CLR_DEFAULT
) || (infoPtr
->clrTextBk
== CLR_NONE
) )
3422 SetBkMode(hdc
, TRANSPARENT
);
3426 SetBkMode(hdc
, OPAQUE
);
3427 SetBkColor(hdc
, infoPtr
->clrTextBk
);
3430 SetTextColor(hdc
, infoPtr
->clrText
);
3431 /* set raster mode */
3432 SetROP2(hdc
, R2_COPYPEN
);
3435 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3436 * wrapping and long words split.
3437 * In cases 1 and 4 only a portion of the text is displayed with word
3438 * wrapping and both word and end ellipsis. (I don't yet know about path
3441 uFormat
|= ( (lvItem
.state
& LVIS_FOCUSED
) && infoPtr
->bFocus
) ?
3444 DT_WORD_ELLIPSIS
| DT_END_ELLIPSIS
;
3447 if (infoPtr
->himlNormal
!= NULL
)
3449 if (lvItem
.iImage
>= 0)
3451 ImageList_Draw (infoPtr
->himlNormal
, lvItem
.iImage
, hdc
, rcItem
.left
,
3453 (lvItem
.state
& LVIS_SELECTED
) ? ILD_SELECTED
: ILD_NORMAL
);
3457 /* Draw the text below the icon */
3459 /* Don't bother painting item being edited */
3460 if ((infoPtr
->hwndEdit
&& (lvItem
.state
& LVIS_FOCUSED
)) ||
3461 !lstrlenW(lvItem
.pszText
))
3463 SetRectEmpty(SuggestedFocus
);
3467 /* Since rcItem.left is left point of icon, compute left point of item box */
3468 rcItem
.left
-= ((infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2);
3469 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
3470 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
3471 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3472 rcItem
.left
, rcItem
.top
, rcItem
.right
, rcItem
.bottom
,
3473 infoPtr
->iconSize
.cx
, infoPtr
->nItemWidth
);
3474 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3475 infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
3476 infoPtr
->rcList
.right
, infoPtr
->rcList
.bottom
,
3477 infoPtr
->rcView
.left
, infoPtr
->rcView
.top
,
3478 infoPtr
->rcView
.right
, infoPtr
->rcView
.bottom
);
3480 InflateRect(&rcItem
, -(2*CAPTION_BORDER
), 0);
3481 rcItem
.top
+= infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
3486 /* I am sure of most of the uFormat values. However I am not sure about
3487 * whether we need or do not need the following:
3488 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3489 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3490 * We certainly do not need
3491 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3492 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3495 /* If the text is being drawn without clipping (i.e. the full text) then we
3496 * need to jump through a few hoops to ensure that it all gets displayed and
3497 * that the background is complete
3499 if (uFormat
& DT_NOCLIP
)
3502 HBRUSH hBrush
= CreateSolidBrush(GetBkColor (hdc
));
3503 int dx
, dy
, old_wid
, new_wid
;
3504 DrawTextW (hdc
, lvItem
.pszText
, -1, &rcItem
, uFormat
| DT_CALCRECT
);
3505 /* Microsoft, in their great wisdom, have decided that the rectangle
3506 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3507 * not the location. So we have to do the centring ourselves (and take
3508 * responsibility for agreeing off-by-one consistency with them).
3510 old_wid
= rcItem
.right
-rcItem
.left
;
3511 new_wid
= rcBack
.right
- rcBack
.left
;
3512 dx
= rcBack
.left
- rcItem
.left
+ (new_wid
-old_wid
)/2;
3513 dy
= rcBack
.top
- rcItem
.top
;
3514 OffsetRect (&rcItem
, dx
, dy
);
3515 FillRect(hdc
, &rcItem
, hBrush
);
3516 DeleteObject(hBrush
);
3518 /* else ? What if we are losing the focus? will we not get a complete
3521 DrawTextW (hdc
, lvItem
.pszText
, -1, &rcItem
, uFormat
);
3523 CopyRect(SuggestedFocus
, &rcItem
);
3528 * Draws listview items when in report display mode.
3531 * [I] HWND : window handle
3532 * [I] HDC : device context handle
3537 static VOID
LISTVIEW_RefreshReport(HWND hwnd
, HDC hdc
, DWORD cdmode
)
3539 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
,0);
3540 SCROLLINFO scrollInfo
;
3541 INT nDrawPosY
= infoPtr
->rcList
.top
;
3543 RECT rcItem
, rcTemp
;
3548 DWORD cditemmode
= CDRF_DODEFAULT
;
3549 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
3553 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
3554 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3555 scrollInfo
.fMask
= SIF_POS
;
3557 nItem
= ListView_GetTopIndex(hwnd
);
3559 /* add 1 for displaying a partial item at the bottom */
3560 nLast
= nItem
+ LISTVIEW_GetCountPerColumn(hwnd
) + 1;
3561 nLast
= min(nLast
, GETITEMCOUNT(infoPtr
));
3563 /* send cache hint notification */
3564 if (GetWindowLongW(hwnd
,GWL_STYLE
) & LVS_OWNERDATA
)
3568 nmlv
.hdr
.hwndFrom
= hwnd
;
3569 nmlv
.hdr
.idFrom
= GetWindowLongW(hwnd
,GWL_ID
);
3570 nmlv
.hdr
.code
= LVN_ODCACHEHINT
;
3574 SendMessageW(GetParent(hwnd
), WM_NOTIFY
, (WPARAM
)nmlv
.hdr
.idFrom
,
3578 nColumnCount
= Header_GetItemCount(infoPtr
->hwndHeader
);
3579 infoPtr
->nColumnCount
= nColumnCount
; /* update nColumnCount */
3580 FullSelected
= infoPtr
->dwExStyle
& LVS_EX_FULLROWSELECT
;
3582 /* clear the background of any part of the control that doesn't contain items */
3583 SubtractRect(&rcTemp
, &infoPtr
->rcList
, &infoPtr
->rcView
);
3584 LISTVIEW_FillBackground(hwnd
, hdc
, &rcTemp
);
3586 /* nothing to draw */
3587 if(GETITEMCOUNT(infoPtr
) == 0)
3590 /* Get scroll bar info once before loop */
3591 GetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
);
3592 scrollOffset
= scrollInfo
.nPos
;
3594 for (; nItem
< nLast
; nItem
++)
3596 RECT SuggestedFocusRect
;
3599 if (lStyle
& LVS_OWNERDRAWFIXED
)
3601 UINT uID
= GetWindowLongW( hwnd
, GWL_ID
);
3606 TRACE("Owner Drawn\n");
3607 dis
.CtlType
= ODT_LISTVIEW
;
3610 dis
.itemAction
= ODA_DRAWENTIRE
;
3613 if (LISTVIEW_IsSelected(hwnd
,nItem
)) dis
.itemState
|=ODS_SELECTED
;
3614 if (nItem
==infoPtr
->nFocusedItem
) dis
.itemState
|=ODS_FOCUS
;
3616 dis
.hwndItem
= hwnd
;
3619 Header_GetItemRect(infoPtr
->hwndHeader
, nColumnCount
-1, &br
);
3621 dis
.rcItem
.left
= -scrollOffset
;
3622 dis
.rcItem
.right
= max(dis
.rcItem
.left
, br
.right
- scrollOffset
);
3623 dis
.rcItem
.top
= nDrawPosY
;
3624 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
3626 ZeroMemory(&item
,sizeof(item
));
3628 item
.mask
= LVIF_PARAM
;
3629 ListView_GetItemW(hwnd
, &item
);
3631 dis
.itemData
= item
.lParam
;
3633 if (SendMessageW(GetParent(hwnd
),WM_DRAWITEM
,(WPARAM
)uID
,(LPARAM
)&dis
))
3635 nDrawPosY
+= infoPtr
->nItemHeight
;
3644 Header_GetItemRect(infoPtr
->hwndHeader
, 0, &ir
);
3645 Header_GetItemRect(infoPtr
->hwndHeader
, nColumnCount
-1, &br
);
3647 ir
.left
+= REPORT_MARGINX
;
3648 ir
.right
= max(ir
.left
, br
.right
- REPORT_MARGINX
);
3650 ir
.bottom
= ir
.top
+ infoPtr
->nItemHeight
;
3652 CopyRect(&SuggestedFocusRect
,&ir
);
3655 for (j
= 0; j
< nColumnCount
; j
++)
3657 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3658 cditemmode
= LISTVIEW_SendCustomDrawItemNotify (hwnd
, hdc
, nItem
, j
,
3660 if (cditemmode
& CDRF_SKIPDEFAULT
)
3663 Header_GetItemRect(infoPtr
->hwndHeader
, j
, &rcItem
);
3665 rcItem
.left
+= REPORT_MARGINX
;
3666 rcItem
.right
= max(rcItem
.left
, rcItem
.right
- REPORT_MARGINX
);
3667 rcItem
.top
= nDrawPosY
;
3668 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
3670 /* Offset the Scroll Bar Pos */
3671 rcItem
.left
-= scrollOffset
;
3672 rcItem
.right
-= scrollOffset
;
3676 LISTVIEW_DrawItem(hwnd
, hdc
, nItem
, rcItem
, FullSelected
,
3677 &SuggestedFocusRect
);
3681 LISTVIEW_DrawSubItem(hwnd
, hdc
, nItem
, j
, rcItem
, FullSelected
);
3684 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3685 LISTVIEW_SendCustomDrawItemNotify(hwnd
, hdc
, nItem
, 0,
3686 CDDS_ITEMPOSTPAINT
);
3691 if (LISTVIEW_GetItemState(hwnd
,nItem
,LVIS_FOCUSED
) && infoPtr
->bFocus
)
3694 if (FullSelected
&& LISTVIEW_GetItemState(hwnd
,nItem
,LVIS_SELECTED
))
3695 rop
= SetROP2(hdc
, R2_XORPEN
);
3697 Rectangle(hdc
, SuggestedFocusRect
.left
, SuggestedFocusRect
.top
,
3698 SuggestedFocusRect
.right
,SuggestedFocusRect
.bottom
);
3701 SetROP2(hdc
, R2_COPYPEN
);
3703 nDrawPosY
+= infoPtr
->nItemHeight
;
3709 * Retrieves the number of items that can fit vertically in the client area.
3712 * [I] HWND : window handle
3715 * Number of items per row.
3717 static INT
LISTVIEW_GetCountPerRow(HWND hwnd
)
3719 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
,0);
3720 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
3721 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
3722 INT nCountPerRow
= 1;
3726 if (uView
!= LVS_REPORT
)
3728 nCountPerRow
= nListWidth
/ infoPtr
->nItemWidth
;
3729 if (nCountPerRow
== 0) nCountPerRow
= 1;
3733 return nCountPerRow
;
3738 * Retrieves the number of items that can fit horizontally in the client
3742 * [I] HWND : window handle
3745 * Number of items per column.
3747 static INT
LISTVIEW_GetCountPerColumn(HWND hwnd
)
3749 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
,0);
3750 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
3751 INT nCountPerColumn
= 1;
3753 if (nListHeight
> 0)
3755 TRACE("rcList=(%d,%d)-(%d,%d), nItemHeight=%d, CYHSCROLL=%d\n",
3756 infoPtr
->rcList
.left
,infoPtr
->rcList
.top
,
3757 infoPtr
->rcList
.right
,infoPtr
->rcList
.bottom
,
3758 infoPtr
->nItemHeight
,
3759 GetSystemMetrics(SM_CYHSCROLL
));
3760 nCountPerColumn
= nListHeight
/ infoPtr
->nItemHeight
;
3761 if (nCountPerColumn
== 0) nCountPerColumn
= 1;
3764 return nCountPerColumn
;
3769 * Retrieves the number of columns needed to display all the items when in
3770 * list display mode.
3773 * [I] HWND : window handle
3776 * Number of columns.
3778 static INT
LISTVIEW_GetColumnCount(HWND hwnd
)
3780 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
3781 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
3782 INT nColumnCount
= 0;
3784 if ((lStyle
& LVS_TYPEMASK
) == LVS_LIST
)
3786 nColumnCount
= infoPtr
->rcList
.right
/ infoPtr
->nItemWidth
;
3787 if (infoPtr
->rcList
.right
% infoPtr
->nItemWidth
) nColumnCount
++;
3790 return nColumnCount
;
3796 * Draws listview items when in list display mode.
3799 * [I] HWND : window handle
3800 * [I] HDC : device context handle
3805 static VOID
LISTVIEW_RefreshList(HWND hwnd
, HDC hdc
, DWORD cdmode
)
3807 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
3808 RECT rcItem
, FocusRect
, rcTemp
;
3812 INT nCountPerColumn
;
3813 INT nItemWidth
= infoPtr
->nItemWidth
;
3814 INT nItemHeight
= infoPtr
->nItemHeight
;
3815 DWORD cditemmode
= CDRF_DODEFAULT
;
3817 /* get number of fully visible columns */
3818 nColumnCount
= LISTVIEW_GetColumnCount(hwnd
);
3819 infoPtr
->nColumnCount
= nColumnCount
;
3820 nCountPerColumn
= LISTVIEW_GetCountPerColumn(hwnd
);
3821 nItem
= ListView_GetTopIndex(hwnd
);
3822 TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3823 nColumnCount
, nCountPerColumn
, nItem
);
3825 /* paint the background of the control that doesn't contain any items */
3826 SubtractRect(&rcTemp
, &infoPtr
->rcList
, &infoPtr
->rcView
);
3827 LISTVIEW_FillBackground(hwnd
, hdc
, &rcTemp
);
3829 /* nothing to draw, return here */
3830 if(GETITEMCOUNT(infoPtr
) == 0)
3833 for (i
= 0; i
< nColumnCount
; i
++)
3835 for (j
= 0; j
< nCountPerColumn
; j
++, nItem
++)
3837 if (nItem
>= GETITEMCOUNT(infoPtr
))
3840 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3841 cditemmode
= LISTVIEW_SendCustomDrawItemNotify (hwnd
, hdc
, nItem
, 0,
3843 if (cditemmode
& CDRF_SKIPDEFAULT
)
3846 rcItem
.top
= j
* nItemHeight
;
3847 rcItem
.left
= i
* nItemWidth
;
3848 rcItem
.bottom
= rcItem
.top
+ nItemHeight
;
3849 rcItem
.right
= rcItem
.left
+ nItemWidth
;
3850 LISTVIEW_DrawItem(hwnd
, hdc
, nItem
, rcItem
, FALSE
, &FocusRect
);
3854 if (LISTVIEW_GetItemState(hwnd
,nItem
,LVIS_FOCUSED
) && infoPtr
->bFocus
)
3855 Rectangle(hdc
, FocusRect
.left
, FocusRect
.top
,
3856 FocusRect
.right
,FocusRect
.bottom
);
3858 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3859 LISTVIEW_SendCustomDrawItemNotify(hwnd
, hdc
, nItem
, 0,
3860 CDDS_ITEMPOSTPAINT
);
3868 * Draws listview items when in icon or small icon display mode.
3871 * [I] HWND : window handle
3872 * [I] HDC : device context handle
3877 static VOID
LISTVIEW_RefreshIcon(HWND hwnd
, HDC hdc
, BOOL bSmall
, DWORD cdmode
)
3879 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
3882 RECT rcItem
, SuggestedFocus
, rcTemp
;
3884 DWORD cditemmode
= CDRF_DODEFAULT
;
3887 infoPtr
->nColumnCount
= 1; /* set this to an arbitrary value to prevent */
3888 /* DrawItem from erasing the incorrect background area */
3890 /* paint the background of the control that doesn't contain any items */
3891 SubtractRect(&rcTemp
, &infoPtr
->rcList
, &infoPtr
->rcView
);
3892 LISTVIEW_FillBackground(hwnd
, hdc
, &rcTemp
);
3894 /* nothing to draw, return here */
3895 if(GETITEMCOUNT(infoPtr
) == 0)
3898 LISTVIEW_GetOrigin(hwnd
, &ptOrigin
);
3899 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
3901 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3902 cditemmode
= LISTVIEW_SendCustomDrawItemNotify (hwnd
, hdc
, i
, 0,
3904 if (cditemmode
& CDRF_SKIPDEFAULT
)
3907 LISTVIEW_GetItemPosition(hwnd
, i
, &ptPosition
);
3908 ptPosition
.x
+= ptOrigin
.x
;
3909 ptPosition
.y
+= ptOrigin
.y
;
3911 if (ptPosition
.y
+ infoPtr
->nItemHeight
> infoPtr
->rcList
.top
)
3913 if (ptPosition
.x
+ infoPtr
->nItemWidth
> infoPtr
->rcList
.left
)
3915 if (ptPosition
.y
< infoPtr
->rcList
.bottom
)
3917 if (ptPosition
.x
< infoPtr
->rcList
.right
)
3919 rcItem
.top
= ptPosition
.y
;
3920 rcItem
.left
= ptPosition
.x
;
3921 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
3922 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
3924 LISTVIEW_DrawItem(hwnd
, hdc
, i
, rcItem
, FALSE
, &SuggestedFocus
);
3926 LISTVIEW_DrawLargeItem(hwnd
, hdc
, i
, rcItem
, &SuggestedFocus
);
3930 if (LISTVIEW_GetItemState(hwnd
,i
,LVIS_FOCUSED
) &&
3931 infoPtr
->bFocus
&& !IsRectEmpty(&SuggestedFocus
))
3932 Rectangle(hdc
, SuggestedFocus
.left
, SuggestedFocus
.top
,
3933 SuggestedFocus
.right
,SuggestedFocus
.bottom
);
3938 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3939 LISTVIEW_SendCustomDrawItemNotify(hwnd
, hdc
, i
, 0,
3940 CDDS_ITEMPOSTPAINT
);
3946 * Draws listview items.
3949 * [I] HWND : window handle
3950 * [I] HDC : device context handle
3955 static VOID
LISTVIEW_Refresh(HWND hwnd
, HDC hdc
)
3957 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
3958 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
3964 infoPtr
->bIsDrawing
= TRUE
;
3965 LISTVIEW_DumpListview (infoPtr
, __LINE__
);
3967 GetClientRect(hwnd
, &rect
);
3968 cdmode
= LISTVIEW_SendCustomDrawNotify(hwnd
,CDDS_PREPAINT
,hdc
,rect
);
3970 if (cdmode
== CDRF_SKIPDEFAULT
) return;
3973 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
3975 /* select the dotted pen (for drawing the focus box) */
3976 hPen
= CreatePen(PS_ALTERNATE
, 1, 0);
3977 hOldPen
= SelectObject(hdc
, hPen
);
3979 /* select transparent brush (for drawing the focus box) */
3980 SelectObject(hdc
, GetStockObject(NULL_BRUSH
));
3983 if (uView
== LVS_LIST
)
3984 LISTVIEW_RefreshList(hwnd
, hdc
, cdmode
);
3985 else if (uView
== LVS_REPORT
)
3986 LISTVIEW_RefreshReport(hwnd
, hdc
, cdmode
);
3987 else if (uView
== LVS_SMALLICON
)
3988 LISTVIEW_RefreshIcon(hwnd
, hdc
, TRUE
, cdmode
);
3989 else if (uView
== LVS_ICON
)
3990 LISTVIEW_RefreshIcon(hwnd
, hdc
, FALSE
, cdmode
);
3992 /* unselect objects */
3993 SelectObject(hdc
, hOldFont
);
3994 SelectObject(hdc
, hOldPen
);
3999 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
4000 LISTVIEW_SendCustomDrawNotify(hwnd
, CDDS_POSTPAINT
, hdc
, rect
);
4002 infoPtr
->bIsDrawing
= FALSE
;
4008 * Calculates the approximate width and height of a given number of items.
4011 * [I] HWND : window handle
4012 * [I] INT : number of items
4017 * Returns a DWORD. The width in the low word and the height in high word.
4019 static LRESULT
LISTVIEW_ApproximateViewRect(HWND hwnd
, INT nItemCount
,
4020 WORD wWidth
, WORD wHeight
)
4022 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
4023 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
4024 INT nItemCountPerColumn
= 1;
4025 INT nColumnCount
= 0;
4026 DWORD dwViewRect
= 0;
4028 if (nItemCount
== -1)
4029 nItemCount
= GETITEMCOUNT(infoPtr
);
4031 if (uView
== LVS_LIST
)
4033 if (wHeight
== 0xFFFF)
4035 /* use current height */
4036 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4039 if (wHeight
< infoPtr
->nItemHeight
)
4040 wHeight
= infoPtr
->nItemHeight
;
4044 if (infoPtr
->nItemHeight
> 0)
4046 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
4047 if (nItemCountPerColumn
== 0)
4048 nItemCountPerColumn
= 1;
4050 if (nItemCount
% nItemCountPerColumn
!= 0)
4051 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
4053 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
4057 /* Microsoft padding magic */
4058 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
4059 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
4061 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4063 else if (uView
== LVS_REPORT
)
4064 FIXME("uView == LVS_REPORT: not implemented\n");
4065 else if (uView
== LVS_SMALLICON
)
4066 FIXME("uView == LVS_SMALLICON: not implemented\n");
4067 else if (uView
== LVS_ICON
)
4068 FIXME("uView == LVS_ICON: not implemented\n");
4075 * Arranges listview items in icon display mode.
4078 * [I] HWND : window handle
4079 * [I] INT : alignment code
4085 static LRESULT
LISTVIEW_Arrange(HWND hwnd
, INT nAlignCode
)
4087 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
4088 BOOL bResult
= FALSE
;
4090 if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
4095 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
4098 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
4101 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4103 case LVA_SNAPTOGRID
:
4104 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4112 /* << LISTVIEW_CreateDragImage >> */
4117 * Removes all listview items and subitems.
4120 * [I] HWND : window handle
4126 static LRESULT
LISTVIEW_DeleteAllItems(HWND hwnd
)
4128 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
4129 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
4130 UINT uView
= lStyle
& LVS_TYPEMASK
;
4131 LISTVIEW_ITEM
*lpItem
;
4132 LISTVIEW_SUBITEM
*lpSubItem
;
4135 BOOL bResult
= FALSE
;
4138 TRACE("(hwnd=%x,)\n", hwnd
);
4140 LISTVIEW_RemoveAllSelections(hwnd
);
4141 infoPtr
->nSelectionMark
=-1;
4142 infoPtr
->nFocusedItem
=-1;
4143 /* But we are supposed to leave nHotItem as is! */
4145 if (lStyle
& LVS_OWNERDATA
)
4147 infoPtr
->hdpaItems
->nItemCount
= 0;
4148 InvalidateRect(hwnd
, NULL
, TRUE
);
4152 if (GETITEMCOUNT(infoPtr
) > 0)
4156 /* send LVN_DELETEALLITEMS notification */
4157 /* verify if subsequent LVN_DELETEITEM notifications should be
4159 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4161 bSuppress
= listview_notify(hwnd
, LVN_DELETEALLITEMS
, &nmlv
);
4163 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
4165 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
4166 if (hdpaSubItems
!= NULL
)
4168 for (j
= 1; j
< hdpaSubItems
->nItemCount
; j
++)
4170 lpSubItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, j
);
4171 if (lpSubItem
!= NULL
)
4173 /* free subitem string */
4174 if (is_textW(lpSubItem
->pszText
))
4175 COMCTL32_Free(lpSubItem
->pszText
);
4178 COMCTL32_Free(lpSubItem
);
4182 lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0);
4187 /* send LVN_DELETEITEM notification */
4189 nmlv
.lParam
= lpItem
->lParam
;
4190 listview_notify(hwnd
, LVN_DELETEITEM
, &nmlv
);
4193 /* free item string */
4194 if (is_textW(lpItem
->pszText
))
4195 COMCTL32_Free(lpItem
->pszText
);
4198 COMCTL32_Free(lpItem
);
4201 DPA_Destroy(hdpaSubItems
);
4205 /* reinitialize listview memory */
4206 bResult
= DPA_DeleteAllPtrs(infoPtr
->hdpaItems
);
4208 /* align items (set position of each item) */
4209 if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
4211 if (lStyle
& LVS_ALIGNLEFT
)
4213 LISTVIEW_AlignLeft(hwnd
);
4217 LISTVIEW_AlignTop(hwnd
);
4221 LISTVIEW_UpdateScroll(hwnd
);
4223 /* invalidate client area (optimization needed) */
4224 InvalidateRect(hwnd
, NULL
, TRUE
);
4232 * Removes a column from the listview control.
4235 * [I] HWND : window handle
4236 * [I] INT : column index
4242 static LRESULT
LISTVIEW_DeleteColumn(HWND hwnd
, INT nColumn
)
4244 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
4245 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
4246 UINT uOwnerData
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_OWNERDATA
;
4247 BOOL bResult
= FALSE
;
4249 if (Header_DeleteItem(infoPtr
->hwndHeader
, nColumn
) != FALSE
)
4251 bResult
= uOwnerData
? TRUE
: LISTVIEW_RemoveColumn(infoPtr
->hdpaItems
, nColumn
);
4253 /* Need to reset the item width when deleting a column */
4254 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(hwnd
);
4256 /* reset scroll parameters */
4257 if (uView
== LVS_REPORT
)
4259 /* update scrollbar(s) */
4260 LISTVIEW_UpdateScroll(hwnd
);
4262 /* refresh client area */
4263 InvalidateRect(hwnd
, NULL
, FALSE
);
4272 * Removes an item from the listview control.
4275 * [I] HWND : window handle
4276 * [I] INT : item index
4282 static LRESULT
LISTVIEW_DeleteItem(HWND hwnd
, INT nItem
)
4284 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
4285 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
4286 UINT uView
= lStyle
& LVS_TYPEMASK
;
4287 LONG lCtrlId
= GetWindowLongW(hwnd
, GWL_ID
);
4289 BOOL bResult
= FALSE
;
4291 LISTVIEW_ITEM
*lpItem
;
4292 LISTVIEW_SUBITEM
*lpSubItem
;
4296 TRACE("(hwnd=%x, nItem=%d)\n", hwnd
, nItem
);
4299 /* First, send LVN_DELETEITEM notification. */
4300 memset(&nmlv
, 0, sizeof (NMLISTVIEW
));
4301 nmlv
.hdr
.hwndFrom
= hwnd
;
4302 nmlv
.hdr
.idFrom
= lCtrlId
;
4303 nmlv
.hdr
.code
= LVN_DELETEITEM
;
4305 SendMessageW(GetParent(hwnd
), WM_NOTIFY
, (WPARAM
)lCtrlId
,
4309 /* remove it from the selection range */
4310 ZeroMemory(&item
,sizeof(item
));
4311 item
.stateMask
= LVIS_SELECTED
;
4312 LISTVIEW_SetItemState(hwnd
,nItem
,&item
);
4314 if (lStyle
& LVS_OWNERDATA
)
4316 infoPtr
->hdpaItems
->nItemCount
--;
4317 InvalidateRect(hwnd
, NULL
, TRUE
);
4321 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
4323 /* initialize memory */
4324 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4326 hdpaSubItems
= (HDPA
)DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
4327 if (hdpaSubItems
!= NULL
)
4329 for (i
= 1; i
< hdpaSubItems
->nItemCount
; i
++)
4331 lpSubItem
= (LISTVIEW_SUBITEM
*)DPA_GetPtr(hdpaSubItems
, i
);
4332 if (lpSubItem
!= NULL
)
4334 /* free item string */
4335 if (is_textW(lpSubItem
->pszText
))
4336 COMCTL32_Free(lpSubItem
->pszText
);
4339 COMCTL32_Free(lpSubItem
);
4343 lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0);
4346 /* free item string */
4347 if (is_textW(lpItem
->pszText
))
4348 COMCTL32_Free(lpItem
->pszText
);
4351 COMCTL32_Free(lpItem
);
4354 bResult
= DPA_Destroy(hdpaSubItems
);
4357 LISTVIEW_ShiftIndices(hwnd
,nItem
,-1);
4359 /* align items (set position of each item) */
4360 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
4362 if (lStyle
& LVS_ALIGNLEFT
)
4363 LISTVIEW_AlignLeft(hwnd
);
4365 LISTVIEW_AlignTop(hwnd
);
4368 LISTVIEW_UpdateScroll(hwnd
);
4370 /* refresh client area */
4371 InvalidateRect(hwnd
, NULL
, TRUE
);
4380 * Return edit control handle of current edit label
4383 * [I] HWND : window handle
4389 static LRESULT
LISTVIEW_GetEditControl(HWND hwnd
)
4391 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
4392 return infoPtr
->hwndEdit
;
4398 * Callback implementation for editlabel control
4401 * [I] HWND : window handle
4402 * [I] LPSTR : modified text
4403 * [I] DWORD : item index
4404 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4410 static BOOL
LISTVIEW_EndEditLabelT(HWND hwnd
, LPWSTR pszText
, DWORD nItem
, BOOL isW
)
4412 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
4413 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
4414 NMLVDISPINFOW dispInfo
;
4415 LISTVIEW_ITEM
*lpItem
;
4417 LISTVIEW_ITEM lvItemRef
;
4419 BOOL bResult
= TRUE
;
4421 TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd
, debugstr_t(pszText
, isW
), nItem
, isW
);
4423 if (!(lStyle
& LVS_OWNERDATA
))
4425 if (!(hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
)))
4428 if (!(lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0)))
4433 ZeroMemory(&lvItemRef
,sizeof(LISTVIEW_ITEM
));
4434 ZeroMemory(&item
,sizeof(item
));
4437 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4438 ListView_GetItemW(hwnd
, &item
);
4439 lvItemRef
.state
= item
.state
;
4440 lvItemRef
.iImage
= item
.iImage
;
4441 lvItemRef
.lParam
= item
.lParam
;
4442 lpItem
= &lvItemRef
;
4445 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4446 dispInfo
.item
.mask
= 0;
4447 dispInfo
.item
.iItem
= nItem
;
4448 dispInfo
.item
.state
= lpItem
->state
;
4449 dispInfo
.item
.stateMask
= 0;
4450 dispInfo
.item
.pszText
= pszText
;
4451 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4452 dispInfo
.item
.iImage
= lpItem
->iImage
;
4453 dispInfo
.item
.lParam
= lpItem
->lParam
;
4454 infoPtr
->hwndEdit
= 0;
4456 /* Do we need to update the Item Text */
4457 if(dispinfo_notifyT(hwnd
, LVN_ENDLABELEDITW
, &dispInfo
, isW
))
4458 if (lpItem
->pszText
!= LPSTR_TEXTCALLBACKW
&& !(lStyle
& LVS_OWNERDATA
))
4459 bResult
= textsetptrT(&lpItem
->pszText
, pszText
, isW
);
4466 * Callback implementation for editlabel control
4469 * [I] HWND : window handle
4470 * [I] LPSTR : modified text
4471 * [I] DWORD : item index
4477 static BOOL
LISTVIEW_EndEditLabelW(HWND hwnd
, LPWSTR pszText
, DWORD nItem
)
4479 return LISTVIEW_EndEditLabelT(hwnd
, pszText
, nItem
, TRUE
);
4484 * Callback implementation for editlabel control
4487 * [I] HWND : window handle
4488 * [I] LPSTR : modified text
4489 * [I] DWORD : item index
4495 static BOOL
LISTVIEW_EndEditLabelA(HWND hwnd
, LPSTR pszText
, DWORD nItem
)
4497 return LISTVIEW_EndEditLabelT(hwnd
, (LPWSTR
)pszText
, nItem
, FALSE
);
4502 * Begin in place editing of specified list view item
4505 * [I] HWND : window handle
4506 * [I] INT : item index
4507 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4513 static HWND
LISTVIEW_EditLabelT(HWND hwnd
, INT nItem
, BOOL isW
)
4515 NMLVDISPINFOW dispInfo
;
4517 LISTVIEW_ITEM
*lpItem
;
4519 HINSTANCE hinst
= GetWindowLongW(hwnd
, GWL_HINSTANCE
);
4520 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
4522 WCHAR szDispText
[DISP_TEXT_SIZE
];
4524 LISTVIEW_ITEM lvItemRef
;
4525 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
4527 if (~GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_EDITLABELS
)
4530 /* Is the EditBox still there, if so remove it */
4531 if(infoPtr
->hwndEdit
!= 0)
4534 LISTVIEW_SetSelection(hwnd
, nItem
);
4535 LISTVIEW_SetItemFocus(hwnd
, nItem
);
4537 if (!(lStyle
& LVS_OWNERDATA
))
4539 if (NULL
== (hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
)))
4542 if (NULL
== (lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0)))
4548 ZeroMemory(&lvItemRef
,sizeof(LISTVIEW_ITEM
));
4549 ZeroMemory(&item
, sizeof(item
));
4552 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4553 ListView_GetItemW(hwnd
, &item
);
4554 lvItemRef
.iImage
= item
.iImage
;
4555 lvItemRef
.state
= item
.state
;
4556 lvItemRef
.lParam
= item
.lParam
;
4557 lpItem
= &lvItemRef
;
4560 /* get information needed for drawing the item */
4561 ZeroMemory(&lvItem
, sizeof(lvItem
));
4562 lvItem
.mask
= LVIF_TEXT
;
4563 lvItem
.iItem
= nItem
;
4564 lvItem
.iSubItem
= 0;
4565 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4566 lvItem
.pszText
= szDispText
;
4567 *lvItem
.pszText
= '\0';
4568 LISTVIEW_GetItemT(hwnd
, &lvItem
, FALSE
, isW
);
4570 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4571 dispInfo
.item
.mask
= 0;
4572 dispInfo
.item
.iItem
= nItem
;
4573 dispInfo
.item
.state
= lpItem
->state
;
4574 dispInfo
.item
.stateMask
= 0;
4575 dispInfo
.item
.pszText
= lvItem
.pszText
;
4576 dispInfo
.item
.cchTextMax
= lstrlenW(lvItem
.pszText
);
4577 dispInfo
.item
.iImage
= lpItem
->iImage
;
4578 dispInfo
.item
.lParam
= lpItem
->lParam
;
4580 if (dispinfo_notifyT(hwnd
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
4583 rect
.left
= LVIR_LABEL
;
4584 if (!LISTVIEW_GetItemRect(hwnd
, nItem
, &rect
))
4587 if (!(hedit
= CreateEditLabelT(szDispText
, WS_VISIBLE
,
4588 rect
.left
-2, rect
.top
-1, 0, rect
.bottom
- rect
.top
+2, hwnd
, hinst
,
4589 isW
? LISTVIEW_EndEditLabelW
: (EditlblCallbackW
)LISTVIEW_EndEditLabelA
,
4593 infoPtr
->hwndEdit
= hedit
;
4595 SendMessageW(hedit
, EM_SETSEL
, 0, -1);
4603 * Ensures the specified item is visible, scrolling into view if necessary.
4606 * [I] HWND : window handle
4607 * [I] INT : item index
4608 * [I] BOOL : partially or entirely visible
4614 static BOOL
LISTVIEW_EnsureVisible(HWND hwnd
, INT nItem
, BOOL bPartial
)
4616 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
4617 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
4618 INT nScrollPosHeight
= 0;
4619 INT nScrollPosWidth
= 0;
4620 SCROLLINFO scrollInfo
;
4622 BOOL bRedraw
= FALSE
;
4624 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
4625 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
4626 scrollInfo
.fMask
= SIF_POS
;
4628 /* ALWAYS bPartial == FALSE, FOR NOW! */
4630 rcItem
.left
= LVIR_BOUNDS
;
4631 if (LISTVIEW_GetItemRect(hwnd
, nItem
, &rcItem
) != FALSE
)
4633 if (rcItem
.left
< infoPtr
->rcList
.left
)
4635 if (GetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
) != FALSE
)
4639 if (uView
== LVS_LIST
)
4641 nScrollPosWidth
= infoPtr
->nItemWidth
;
4642 rcItem
.left
+= infoPtr
->rcList
.left
;
4644 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
4646 nScrollPosWidth
= 1;
4647 rcItem
.left
+= infoPtr
->rcList
.left
;
4650 /* When in LVS_REPORT view, the scroll position should
4652 if (nScrollPosWidth
!= 0)
4654 if (rcItem
.left
% nScrollPosWidth
== 0)
4655 scrollInfo
.nPos
+= rcItem
.left
/ nScrollPosWidth
;
4657 scrollInfo
.nPos
+= rcItem
.left
/ nScrollPosWidth
- 1;
4659 SetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
, TRUE
);
4663 else if (rcItem
.right
> infoPtr
->rcList
.right
)
4665 if (GetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
) != FALSE
)
4669 if (uView
== LVS_LIST
)
4671 rcItem
.right
-= infoPtr
->rcList
.right
;
4672 nScrollPosWidth
= infoPtr
->nItemWidth
;
4674 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
4676 rcItem
.right
-= infoPtr
->rcList
.right
;
4677 nScrollPosWidth
= 1;
4680 /* When in LVS_REPORT view, the scroll position should
4682 if (nScrollPosWidth
!= 0)
4684 if (rcItem
.right
% nScrollPosWidth
== 0)
4685 scrollInfo
.nPos
+= rcItem
.right
/ nScrollPosWidth
;
4687 scrollInfo
.nPos
+= rcItem
.right
/ nScrollPosWidth
+ 1;
4689 SetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
, TRUE
);
4694 if (rcItem
.top
< infoPtr
->rcList
.top
)
4698 if (GetScrollInfo(hwnd
, SB_VERT
, &scrollInfo
) != FALSE
)
4700 if (uView
== LVS_REPORT
)
4702 rcItem
.top
-= infoPtr
->rcList
.top
;
4703 nScrollPosHeight
= infoPtr
->nItemHeight
;
4705 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
4707 nScrollPosHeight
= 1;
4708 rcItem
.top
+= infoPtr
->rcList
.top
;
4711 if (rcItem
.top
% nScrollPosHeight
== 0)
4712 scrollInfo
.nPos
+= rcItem
.top
/ nScrollPosHeight
;
4714 scrollInfo
.nPos
+= rcItem
.top
/ nScrollPosHeight
- 1;
4716 SetScrollInfo(hwnd
, SB_VERT
, &scrollInfo
, TRUE
);
4719 else if (rcItem
.bottom
> infoPtr
->rcList
.bottom
)
4723 if (GetScrollInfo(hwnd
, SB_VERT
, &scrollInfo
) != FALSE
)
4725 if (uView
== LVS_REPORT
)
4727 rcItem
.bottom
-= infoPtr
->rcList
.bottom
;
4728 nScrollPosHeight
= infoPtr
->nItemHeight
;
4730 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
4732 nScrollPosHeight
= 1;
4733 rcItem
.bottom
-= infoPtr
->rcList
.bottom
;
4736 if (rcItem
.bottom
% nScrollPosHeight
== 0)
4737 scrollInfo
.nPos
+= rcItem
.bottom
/ nScrollPosHeight
;
4739 scrollInfo
.nPos
+= rcItem
.bottom
/ nScrollPosHeight
+ 1;
4741 SetScrollInfo(hwnd
, SB_VERT
, &scrollInfo
, TRUE
);
4747 InvalidateRect(hwnd
,NULL
,TRUE
);
4753 * Retrieves the nearest item, given a position and a direction.
4756 * [I] HWND : window handle
4757 * [I] POINT : start position
4758 * [I] UINT : direction
4761 * Item index if successdful, -1 otherwise.
4763 static INT
LISTVIEW_GetNearestItem(HWND hwnd
, POINT pt
, UINT vkDirection
)
4765 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
4770 TRACE("point %ld,%ld, direction %s\n", pt
.x
, pt
.y
,
4771 (vkDirection
== VK_DOWN
) ? "VK_DOWN" :
4772 ((vkDirection
== VK_UP
) ? "VK_UP" :
4773 ((vkDirection
== VK_LEFT
) ? "VK_LEFT" : "VK_RIGHT")));
4775 if (LISTVIEW_GetViewRect(hwnd
, &rcView
) != FALSE
)
4777 ZeroMemory(&lvIntHit
, sizeof(lvIntHit
));
4778 LISTVIEW_GetOrigin(hwnd
, &lvIntHit
.ht
.pt
);
4779 lvIntHit
.ht
.pt
.x
+= pt
.x
;
4780 lvIntHit
.ht
.pt
.y
+= pt
.y
;
4782 if (vkDirection
== VK_DOWN
)
4783 lvIntHit
.ht
.pt
.y
+= infoPtr
->nItemHeight
;
4784 else if (vkDirection
== VK_UP
)
4785 lvIntHit
.ht
.pt
.y
-= infoPtr
->nItemHeight
;
4786 else if (vkDirection
== VK_LEFT
)
4787 lvIntHit
.ht
.pt
.x
-= infoPtr
->nItemWidth
;
4788 else if (vkDirection
== VK_RIGHT
)
4789 lvIntHit
.ht
.pt
.x
+= infoPtr
->nItemWidth
;
4791 if (PtInRect(&rcView
, lvIntHit
.ht
.pt
) == FALSE
)
4795 nItem
= LISTVIEW_SuperHitTestItem(hwnd
, &lvIntHit
, TRUE
);
4796 return nItem
== -1 ? lvIntHit
.iDistItem
: nItem
;
4805 * Searches for an item with specific characteristics.
4808 * [I] hwnd : window handle
4809 * [I] nStart : base item index
4810 * [I] lpFindInfo : item information to look for
4813 * SUCCESS : index of item
4816 static LRESULT
LISTVIEW_FindItemW(HWND hwnd
, INT nStart
,
4817 LPLVFINDINFOW lpFindInfo
)
4819 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
4821 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
4825 INT nLast
= GETITEMCOUNT(infoPtr
);
4827 if ((nItem
>= -1) && (lpFindInfo
!= NULL
))
4829 ZeroMemory(&lvItem
, sizeof(lvItem
));
4831 if (lpFindInfo
->flags
& LVFI_PARAM
)
4833 lvItem
.mask
|= LVIF_PARAM
;
4836 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
))
4838 lvItem
.mask
|= LVIF_TEXT
;
4839 lvItem
.pszText
= szDispText
;
4840 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4843 if (lpFindInfo
->flags
& LVFI_WRAP
)
4846 if (lpFindInfo
->flags
& LVFI_NEARESTXY
)
4848 ptItem
.x
= lpFindInfo
->pt
.x
;
4849 ptItem
.y
= lpFindInfo
->pt
.y
;
4854 while (nItem
< nLast
)
4856 if (lpFindInfo
->flags
& LVFI_NEARESTXY
)
4858 nItem
= LISTVIEW_GetNearestItem(hwnd
, ptItem
,
4859 lpFindInfo
->vkDirection
);
4862 /* get position of the new item index */
4863 if (ListView_GetItemPosition(hwnd
, nItem
, &ptItem
) == FALSE
)
4874 lvItem
.iItem
= nItem
;
4875 lvItem
.iSubItem
= 0;
4876 if (LISTVIEW_GetItemW(hwnd
, &lvItem
, TRUE
))
4878 if (lvItem
.mask
& LVIF_TEXT
)
4880 if (lpFindInfo
->flags
& LVFI_PARTIAL
)
4882 if (strstrW(lvItem
.pszText
, lpFindInfo
->psz
) == NULL
)
4887 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0)
4892 if (lvItem
.mask
& LVIF_PARAM
)
4894 if (lpFindInfo
->lParam
!= lvItem
.lParam
)
4920 * Searches for an item with specific characteristics.
4923 * [I] hwnd : window handle
4924 * [I] nStart : base item index
4925 * [I] lpFindInfo : item information to look for
4928 * SUCCESS : index of item
4931 static LRESULT
LISTVIEW_FindItemA(HWND hwnd
, INT nStart
,
4932 LPLVFINDINFOA lpFindInfo
)
4934 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
);
4938 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
4939 if (hasText
) fiw
.psz
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
4940 res
= LISTVIEW_FindItemW(hwnd
, nStart
, &fiw
);
4941 if (hasText
) textfreeT((LPWSTR
)fiw
.psz
, FALSE
);
4947 * Retrieves the background color of the listview control.
4950 * [I] HWND : window handle
4953 * COLORREF associated with the background.
4955 static LRESULT
LISTVIEW_GetBkColor(HWND hwnd
)
4957 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
4959 return infoPtr
->clrBk
;
4964 * Retrieves the background image of the listview control.
4967 * [I] HWND : window handle
4968 * [O] LPLVMKBIMAGE : background image attributes
4974 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4976 /* FIXME (listview, "empty stub!\n"); */
4982 * Retrieves the callback mask.
4985 * [I] HWND : window handle
4990 static UINT
LISTVIEW_GetCallbackMask(HWND hwnd
)
4992 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
4994 return infoPtr
->uCallbackMask
;
4999 * Retrieves column attributes.
5002 * [I] HWND : window handle
5003 * [I] INT : column index
5004 * [IO] LPLVCOLUMNW : column information
5005 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5006 * otherwise it is in fact a LPLVCOLUMNA
5012 static LRESULT
LISTVIEW_GetColumnT(HWND hwnd
, INT nItem
, LPLVCOLUMNW lpColumn
, BOOL isW
)
5014 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
5016 BOOL bResult
= FALSE
;
5018 if (lpColumn
!= NULL
)
5021 /* initialize memory */
5022 ZeroMemory(&hdi
, sizeof(hdi
));
5024 if (lpColumn
->mask
& LVCF_FMT
)
5025 hdi
.mask
|= HDI_FORMAT
;
5027 if (lpColumn
->mask
& LVCF_WIDTH
)
5028 hdi
.mask
|= HDI_WIDTH
;
5030 if (lpColumn
->mask
& LVCF_TEXT
)
5032 hdi
.mask
|= HDI_TEXT
;
5033 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
5034 hdi
.pszText
= lpColumn
->pszText
;
5037 if (lpColumn
->mask
& LVCF_IMAGE
)
5038 hdi
.mask
|= HDI_IMAGE
;
5040 if (lpColumn
->mask
& LVCF_ORDER
)
5041 hdi
.mask
|= HDI_ORDER
;
5044 bResult
= Header_GetItemW(infoPtr
->hwndHeader
, nItem
, &hdi
);
5046 bResult
= Header_GetItemA(infoPtr
->hwndHeader
, nItem
, &hdi
);
5048 if (bResult
!= FALSE
)
5050 if (lpColumn
->mask
& LVCF_FMT
)
5054 if (hdi
.fmt
& HDF_LEFT
)
5055 lpColumn
->fmt
|= LVCFMT_LEFT
;
5056 else if (hdi
.fmt
& HDF_RIGHT
)
5057 lpColumn
->fmt
|= LVCFMT_RIGHT
;
5058 else if (hdi
.fmt
& HDF_CENTER
)
5059 lpColumn
->fmt
|= LVCFMT_CENTER
;
5061 if (hdi
.fmt
& HDF_IMAGE
)
5062 lpColumn
->fmt
|= LVCFMT_COL_HAS_IMAGES
;
5064 if (hdi
.fmt
& HDF_BITMAP_ON_RIGHT
)
5065 lpColumn
->fmt
|= LVCFMT_BITMAP_ON_RIGHT
;
5068 if (lpColumn
->mask
& LVCF_WIDTH
)
5069 lpColumn
->cx
= hdi
.cxy
;
5071 if (lpColumn
->mask
& LVCF_IMAGE
)
5072 lpColumn
->iImage
= hdi
.iImage
;
5074 if (lpColumn
->mask
& LVCF_ORDER
)
5075 lpColumn
->iOrder
= hdi
.iOrder
;
5077 TRACE("(hwnd=%x, col=%d, lpColumn=%s, isW=%d)\n",
5078 hwnd
, nItem
, debuglvcolumn_t(lpColumn
, isW
), isW
);
5087 static LRESULT
LISTVIEW_GetColumnOrderArray(HWND hwnd
, INT iCount
, LPINT lpiArray
)
5089 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
5096 for (i
= 0; i
< iCount
; i
++)
5104 * Retrieves the column width.
5107 * [I] HWND : window handle
5108 * [I] int : column index
5111 * SUCCESS : column width
5114 static LRESULT
LISTVIEW_GetColumnWidth(HWND hwnd
, INT nColumn
)
5116 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
5117 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
5118 INT nColumnWidth
= 0;
5121 if (uView
== LVS_LIST
)
5123 nColumnWidth
= infoPtr
->nItemWidth
;
5125 else if (uView
== LVS_REPORT
)
5127 /* get column width from header */
5128 ZeroMemory(&hdi
, sizeof(hdi
));
5129 hdi
.mask
= HDI_WIDTH
;
5130 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
) != FALSE
)
5131 nColumnWidth
= hdi
.cxy
;
5134 return nColumnWidth
;
5139 * In list or report display mode, retrieves the number of items that can fit
5140 * vertically in the visible area. In icon or small icon display mode,
5141 * retrieves the total number of visible items.
5144 * [I] HWND : window handle
5147 * Number of fully visible items.
5149 static LRESULT
LISTVIEW_GetCountPerPage(HWND hwnd
)
5151 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
5152 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
5155 if (uView
== LVS_LIST
)
5157 if (infoPtr
->rcList
.right
> infoPtr
->nItemWidth
)
5159 nItemCount
= LISTVIEW_GetCountPerRow(hwnd
) *
5160 LISTVIEW_GetCountPerColumn(hwnd
);
5163 else if (uView
== LVS_REPORT
)
5165 nItemCount
= LISTVIEW_GetCountPerColumn(hwnd
);
5169 nItemCount
= GETITEMCOUNT(infoPtr
);
5175 /* LISTVIEW_GetEditControl */
5179 * Retrieves the extended listview style.
5182 * [I] HWND : window handle
5185 * SUCCESS : previous style
5188 static LRESULT
LISTVIEW_GetExtendedListViewStyle(HWND hwnd
)
5190 LISTVIEW_INFO
*infoPtr
;
5192 /* make sure we can get the listview info */
5193 if (!(infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0)))
5196 return (infoPtr
->dwExStyle
);
5201 * Retrieves the handle to the header control.
5204 * [I] HWND : window handle
5209 static LRESULT
LISTVIEW_GetHeader(HWND hwnd
)
5211 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
5213 return infoPtr
->hwndHeader
;
5216 /* LISTVIEW_GetHotCursor */
5220 * Returns the time that the mouse cursor must hover over an item
5221 * before it is selected.
5224 * [I] HWND : window handle
5227 * Returns the previously set hover time or (DWORD)-1 to indicate that the
5228 * hover time is set to the default hover time.
5230 static LRESULT
LISTVIEW_GetHoverTime(HWND hwnd
)
5232 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
5234 return infoPtr
->dwHoverTime
;
5239 * Retrieves an image list handle.
5242 * [I] HWND : window handle
5243 * [I] INT : image list identifier
5246 * SUCCESS : image list handle
5249 static LRESULT
LISTVIEW_GetImageList(HWND hwnd
, INT nImageList
)
5251 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
5252 HIMAGELIST himl
= NULL
;
5257 himl
= infoPtr
->himlNormal
;
5260 himl
= infoPtr
->himlSmall
;
5263 himl
= infoPtr
->himlState
;
5267 return (LRESULT
)himl
;
5270 /* LISTVIEW_GetISearchString */
5274 * Retrieves item attributes.
5277 * [I] hwnd : window handle
5278 * [IO] lpLVItem : item info
5279 * [I] internal : if true then we will use tricks that avoid copies
5280 * but are not compatible with the regular interface
5281 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5282 * if FALSE, the lpLVItem is a LPLVITEMA.
5288 static LRESULT
LISTVIEW_GetItemT(HWND hwnd
, LPLVITEMW lpLVItem
, BOOL internal
, BOOL isW
)
5290 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
5291 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
5292 NMLVDISPINFOW dispInfo
;
5293 LISTVIEW_SUBITEM
*lpSubItem
;
5294 LISTVIEW_ITEM
*lpItem
;
5297 INT
* piImage
= (INT
*)&null
;
5298 LPWSTR
* ppszText
= (LPWSTR
*)&null
;
5299 LPARAM
* plParam
= (LPARAM
*)&null
;
5301 if (internal
&& !isW
)
5303 ERR("We can't have internal non-Unicode GetItem!\n");
5307 /* In the following:
5308 * lpLVItem describes the information requested by the user
5309 * lpItem/lpSubItem is what we have
5310 * dispInfo is a structure we use to request the missing
5311 * information from the application
5314 TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n",
5315 hwnd
, debuglvitem_t(lpLVItem
, isW
), internal
, isW
);
5317 if ((lpLVItem
== NULL
) || (lpLVItem
->iItem
< 0) ||
5318 (lpLVItem
->iItem
>= GETITEMCOUNT(infoPtr
)))
5321 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5323 if (lStyle
& LVS_OWNERDATA
)
5325 if (lpLVItem
->mask
& ~LVIF_STATE
)
5327 memcpy(&dispInfo
.item
, lpLVItem
, sizeof(LVITEMW
));
5328 dispinfo_notifyT(hwnd
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5329 memcpy(lpLVItem
, &dispInfo
.item
, sizeof(LVITEMW
));
5330 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
5333 if ((lpLVItem
->mask
& LVIF_STATE
)&&(lpLVItem
->iSubItem
== 0))
5335 lpLVItem
->state
= 0;
5336 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5337 lpLVItem
->state
|= LVIS_FOCUSED
;
5338 if (LISTVIEW_IsSelected(hwnd
,lpLVItem
->iItem
))
5339 lpLVItem
->state
|= LVIS_SELECTED
;
5345 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
5346 if (hdpaSubItems
== NULL
) return FALSE
;
5348 if ( (lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0)) == NULL
)
5351 ZeroMemory(&dispInfo
.item
, sizeof(LVITEMW
));
5352 if (lpLVItem
->iSubItem
== 0)
5354 piImage
=&lpItem
->iImage
;
5355 ppszText
=&lpItem
->pszText
;
5356 plParam
=&lpItem
->lParam
;
5357 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
)
5359 dispInfo
.item
.mask
|= LVIF_STATE
;
5360 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
5365 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
5366 if (lpSubItem
!= NULL
)
5368 piImage
=&lpSubItem
->iImage
;
5369 ppszText
=&lpSubItem
->pszText
;
5373 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (*piImage
==I_IMAGECALLBACK
))
5375 dispInfo
.item
.mask
|= LVIF_IMAGE
;
5378 if ((lpLVItem
->mask
& LVIF_TEXT
) && !is_textW(*ppszText
))
5380 dispInfo
.item
.mask
|= LVIF_TEXT
;
5381 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5382 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5383 if (dispInfo
.item
.pszText
&& lpLVItem
->cchTextMax
> 0)
5384 *dispInfo
.item
.pszText
= '\0';
5385 if (dispInfo
.item
.pszText
&& (*ppszText
== NULL
))
5386 *dispInfo
.item
.pszText
= '\0';
5389 if (dispInfo
.item
.mask
!= 0)
5391 /* We don't have all the requested info, query the application */
5392 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5393 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
;
5394 dispInfo
.item
.lParam
= lpItem
->lParam
;
5395 dispinfo_notifyT(hwnd
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5396 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
5399 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
5401 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
5402 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && (*piImage
==I_IMAGECALLBACK
))
5403 *piImage
= dispInfo
.item
.iImage
;
5405 else if (lpLVItem
->mask
& LVIF_IMAGE
)
5407 lpLVItem
->iImage
= *piImage
;
5410 if (dispInfo
.item
.mask
& LVIF_PARAM
)
5412 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
5413 if (dispInfo
.item
.mask
& LVIF_DI_SETITEM
)
5414 *plParam
= dispInfo
.item
.lParam
;
5416 else if (lpLVItem
->mask
& LVIF_PARAM
)
5417 lpLVItem
->lParam
= lpItem
->lParam
;
5419 if (dispInfo
.item
.mask
& LVIF_TEXT
)
5421 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && *ppszText
)
5422 textsetptrT(ppszText
, dispInfo
.item
.pszText
, isW
);
5424 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5425 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5426 if (lpLVItem
->pszText
!= dispInfo
.item
.pszText
)
5427 textcpynT(lpLVItem
->pszText
, isW
, dispInfo
.item
.pszText
, isW
, lpLVItem
->cchTextMax
);
5430 else if (lpLVItem
->mask
& LVIF_TEXT
)
5432 if (internal
) lpLVItem
->pszText
= *ppszText
;
5433 else textcpynT(lpLVItem
->pszText
, isW
, *ppszText
, TRUE
, lpLVItem
->cchTextMax
);
5436 if (lpLVItem
->iSubItem
== 0)
5438 if (dispInfo
.item
.mask
& LVIF_STATE
)
5440 lpLVItem
->state
= lpItem
->state
;
5441 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
5442 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
5444 lpLVItem
->state
&= ~LVIS_SELECTED
;
5445 if ((dispInfo
.item
.stateMask
& LVIS_SELECTED
) &&
5446 LISTVIEW_IsSelected(hwnd
,dispInfo
.item
.iItem
))
5447 lpLVItem
->state
|= LVIS_SELECTED
;
5449 else if (lpLVItem
->mask
& LVIF_STATE
)
5451 lpLVItem
->state
= lpItem
->state
& lpLVItem
->stateMask
;
5453 lpLVItem
->state
&= ~LVIS_SELECTED
;
5454 if ((lpLVItem
->stateMask
& LVIS_SELECTED
) &&
5455 LISTVIEW_IsSelected(hwnd
,lpLVItem
->iItem
))
5456 lpLVItem
->state
|= LVIS_SELECTED
;
5459 if (lpLVItem
->mask
& LVIF_PARAM
)
5460 lpLVItem
->lParam
= lpItem
->lParam
;
5462 if (lpLVItem
->mask
& LVIF_INDENT
)
5463 lpLVItem
->iIndent
= lpItem
->iIndent
;
5469 /* LISTVIEW_GetHotCursor */
5473 * Retrieves the index of the hot item.
5476 * [I] HWND : window handle
5479 * SUCCESS : hot item index
5480 * FAILURE : -1 (no hot item)
5482 static LRESULT
LISTVIEW_GetHotItem(HWND hwnd
)
5484 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
5486 return infoPtr
->nHotItem
;
5489 /* LISTVIEW_GetHoverTime */
5493 * Retrieves the number of items in the listview control.
5496 * [I] HWND : window handle
5501 static LRESULT
LISTVIEW_GetItemCount(HWND hwnd
)
5503 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
5505 return GETITEMCOUNT(infoPtr
);
5510 * Retrieves the rectangle enclosing the item icon and text.
5513 * [I] HWND : window handle
5514 * [I] INT : item index
5515 * [O] LPRECT : coordinate information
5521 static BOOL
LISTVIEW_GetItemBoundBox(HWND hwnd
, INT nItem
, LPRECT lpRect
)
5523 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
5524 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
5525 UINT uView
= lStyle
& LVS_TYPEMASK
;
5526 BOOL bResult
= FALSE
;
5528 LISTVIEW_ITEM
*lpItem
;
5529 INT nCountPerColumn
;
5532 TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd
, nItem
, lpRect
);
5534 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)) &&
5537 if (uView
== LVS_LIST
)
5540 nItem
= nItem
- ListView_GetTopIndex(hwnd
);
5541 nCountPerColumn
= LISTVIEW_GetCountPerColumn(hwnd
);
5544 nRow
= nItem
% nCountPerColumn
;
5547 lpRect
->left
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
5552 lpRect
->left
= (nItem
/ nCountPerColumn
-1) * infoPtr
->nItemWidth
;
5553 lpRect
->top
= (nRow
+ nCountPerColumn
) * infoPtr
->nItemHeight
;
5558 lpRect
->left
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
5559 lpRect
->top
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
5562 else if (uView
== LVS_REPORT
)
5565 lpRect
->left
= REPORT_MARGINX
;
5566 lpRect
->top
= ((nItem
- ListView_GetTopIndex(hwnd
)) *
5567 infoPtr
->nItemHeight
) + infoPtr
->rcList
.top
;
5569 if (!(lStyle
& LVS_NOSCROLL
))
5571 SCROLLINFO scrollInfo
;
5572 /* Adjust position by scrollbar offset */
5573 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
5574 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
5575 scrollInfo
.fMask
= SIF_POS
;
5576 GetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
);
5577 lpRect
->left
-= scrollInfo
.nPos
;
5580 else /* either LVS_ICON or LVS_SMALLICON */
5582 if ((hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
)))
5584 if ((lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0)))
5587 lpRect
->left
= lpItem
->ptPosition
.x
;
5588 lpRect
->top
= lpItem
->ptPosition
.y
;
5593 lpRect
->right
= lpRect
->left
+ infoPtr
->nItemWidth
;
5594 lpRect
->bottom
= lpRect
->top
+ infoPtr
->nItemHeight
;
5595 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult
? "TRUE" : "FALSE",
5596 lpRect
->left
, lpRect
->top
, lpRect
->right
, lpRect
->bottom
);
5602 * Retrieves the position (upper-left) of the listview control item.
5603 * Note that for LVS_ICON style, the upper-left is that of the icon
5604 * and not the bounding box.
5607 * [I] HWND : window handle
5608 * [I] INT : item index
5609 * [O] LPPOINT : coordinate information
5615 static BOOL
LISTVIEW_GetItemPosition(HWND hwnd
, INT nItem
, LPPOINT lpptPosition
)
5617 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongA(hwnd
, 0);
5618 UINT uView
= GetWindowLongA(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
5619 BOOL bResult
= FALSE
;
5622 TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd
, nItem
, lpptPosition
);
5624 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)) &&
5625 (lpptPosition
!= NULL
))
5627 bResult
= LISTVIEW_GetItemBoundBox(hwnd
, nItem
, &rcBounding
);
5628 lpptPosition
->x
= rcBounding
.left
;
5629 lpptPosition
->y
= rcBounding
.top
;
5630 if (uView
== LVS_ICON
)
5632 lpptPosition
->y
+= ICON_TOP_PADDING
;
5633 lpptPosition
->x
+= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2;
5635 TRACE("result %s (%ld,%ld)\n", bResult
? "TRUE" : "FALSE",
5636 lpptPosition
->x
, lpptPosition
->y
);
5642 * Update the bounding rectangle around the text under a large icon.
5643 * This depends on whether it has the focus or not.
5644 * On entry the rectangle's top, left and right should be set.
5645 * On return the bottom will also be set and the width may have been
5648 * This appears to be weird, even in the Microsoft implementation.
5651 static void ListView_UpdateLargeItemLabelRect (
5652 HWND hwnd
, /* The window of the listview */
5653 const LISTVIEW_INFO
*infoPtr
, /* The listview itself */
5654 int nItem
, /* The item for which we are calculating this */
5655 RECT
*rect
) /* The rectangle to be updated */
5657 HDC hdc
= GetDC (hwnd
);
5658 HFONT hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
5660 if (infoPtr
->bFocus
&& infoPtr
->nFocusedItem
== nItem
)
5662 /* We (aim to) display the full text. In Windows 95 it appears to
5663 * calculate the size assuming the specified font and then it draws
5664 * the text in that region with the specified font except scaled to
5665 * 10 point (or the height of the system font or ...). Thus if the
5666 * window has 24 point Helvetica the highlit rectangle will be
5667 * taller than the text and if it is 7 point Helvetica then the text
5669 * For now we will simply say that it is the correct size to display
5670 * the text in the specified font.
5673 lvItem
.mask
= LVIF_TEXT
;
5674 lvItem
.iItem
= nItem
;
5675 lvItem
.iSubItem
= 0;
5676 /* We will specify INTERNAL and so will receive back a const
5677 * pointer to the text, rather than specifying a buffer to which
5680 LISTVIEW_GetItemW (hwnd
, &lvItem
, TRUE
);
5681 DrawTextW (hdc
, lvItem
.pszText
, -1, rect
, DT_CALCRECT
|
5682 DT_NOCLIP
| DT_EDITCONTROL
| DT_TOP
| DT_CENTER
|
5683 DT_WORDBREAK
| DT_NOPREFIX
);
5684 /* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
5688 /* As far as I can see the text region seems to be trying to be
5689 * "tall enough for two lines of text". Once again (comctl32.dll ver
5690 * 5.81?) it measures this on the basis of the selected font and then
5691 * draws it with the same font except in 10 point size. This can lead
5692 * to more or less than the two rows appearing.
5693 * Question; are we supposed to be including DT_EXTERNALLEADING?
5694 * Question; should the width be shrunk to the space required to
5695 * display the two lines?
5697 rect
->bottom
= rect
->top
+ 2 * infoPtr
->ntmHeight
;
5700 SelectObject (hdc
, hOldFont
);
5701 ReleaseDC (hwnd
, hdc
);
5706 * Retrieves the bounding rectangle for a listview control item.
5709 * [I] HWND : window handle
5710 * [I] INT : item index
5711 * [IO] LPRECT : bounding rectangle coordinates
5712 * lprc->left specifies the portion of the item for which the bounding
5713 * rectangle will be retrieved.
5715 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5716 * including the icon and label.
5717 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5718 * LVIR_LABEL Returns the bounding rectangle of the item text.
5719 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5720 * rectangles, but excludes columns in report view.
5727 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5728 * upon whether the window has the focus currently and on whether the item
5729 * is the one with the focus. Ensure that the control's record of which
5730 * item has the focus agrees with the items' records.
5732 static LRESULT
LISTVIEW_GetItemRect(HWND hwnd
, INT nItem
, LPRECT lprc
)
5734 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
5735 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
5736 BOOL bResult
= FALSE
;
5745 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd
, nItem
, lprc
);
5747 if (uView
& LVS_REPORT
)
5749 ZeroMemory(&lvItem
, sizeof(lvItem
));
5750 lvItem
.mask
= LVIF_INDENT
;
5751 lvItem
.iItem
= nItem
;
5752 lvItem
.iSubItem
= 0;
5753 LISTVIEW_GetItemW(hwnd
, &lvItem
, TRUE
);
5756 if (lvItem
.iIndent
>0 && infoPtr
->iconSize
.cx
> 0)
5757 nIndent
= infoPtr
->iconSize
.cx
* lvItem
.iIndent
;
5764 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)) && (lprc
!= NULL
))
5769 if (!ListView_GetItemPosition(hwnd
, nItem
, &ptItem
)) break;
5770 if (uView
== LVS_ICON
)
5772 if (infoPtr
->himlNormal
!= NULL
)
5774 if (LISTVIEW_GetOrigin(hwnd
, &ptOrigin
) != FALSE
)
5777 lprc
->left
= ptItem
.x
+ ptOrigin
.x
;
5778 lprc
->top
= ptItem
.y
+ ptOrigin
.y
;
5779 lprc
->right
= lprc
->left
+ infoPtr
->iconSize
.cx
;
5780 lprc
->bottom
= (lprc
->top
+ infoPtr
->iconSize
.cy
+
5781 ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
);
5785 else if (uView
== LVS_SMALLICON
)
5787 if (LISTVIEW_GetOrigin(hwnd
, &ptOrigin
) != FALSE
)
5790 lprc
->left
= ptItem
.x
+ ptOrigin
.x
;
5791 lprc
->top
= ptItem
.y
+ ptOrigin
.y
;
5792 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5794 if (infoPtr
->himlState
!= NULL
)
5795 lprc
->left
+= infoPtr
->iconSize
.cx
;
5797 if (infoPtr
->himlSmall
!= NULL
)
5798 lprc
->right
= lprc
->left
+ infoPtr
->iconSize
.cx
;
5800 lprc
->right
= lprc
->left
;
5806 lprc
->left
= ptItem
.x
;
5807 if (uView
& LVS_REPORT
)
5808 lprc
->left
+= nIndent
;
5809 lprc
->top
= ptItem
.y
;
5810 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5812 if (infoPtr
->himlState
!= NULL
)
5813 lprc
->left
+= infoPtr
->iconSize
.cx
;
5815 if (infoPtr
->himlSmall
!= NULL
)
5816 lprc
->right
= lprc
->left
+ infoPtr
->iconSize
.cx
;
5818 lprc
->right
= lprc
->left
;
5823 if (!ListView_GetItemPosition(hwnd
, nItem
, &ptItem
)) break;
5824 if (uView
== LVS_ICON
)
5826 if (infoPtr
->himlNormal
!= NULL
)
5828 if (LISTVIEW_GetOrigin(hwnd
, &ptOrigin
) != FALSE
)
5831 lprc
->left
= ptItem
.x
+ ptOrigin
.x
;
5832 lprc
->top
= (ptItem
.y
+ ptOrigin
.y
+ infoPtr
->iconSize
.cy
+
5833 ICON_BOTTOM_PADDING
);
5834 nLabelWidth
= LISTVIEW_GetLabelWidth(hwnd
, nItem
);
5835 if (infoPtr
->iconSpacing
.cx
- nLabelWidth
> 1)
5837 lprc
->left
+= (infoPtr
->iconSpacing
.cx
- nLabelWidth
) / 2;
5838 lprc
->right
= lprc
->left
+ nLabelWidth
;
5843 lprc
->right
= lprc
->left
+ infoPtr
->iconSpacing
.cx
- 1;
5844 ListView_UpdateLargeItemLabelRect (hwnd
, infoPtr
, nItem
, lprc
);
5849 else if (uView
== LVS_SMALLICON
)
5851 if (LISTVIEW_GetOrigin(hwnd
, &ptOrigin
) != FALSE
)
5854 nLeftPos
= lprc
->left
= ptItem
.x
+ ptOrigin
.x
;
5855 lprc
->top
= ptItem
.y
+ ptOrigin
.y
;
5856 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5858 if (infoPtr
->himlState
!= NULL
)
5859 lprc
->left
+= infoPtr
->iconSize
.cx
;
5861 if (infoPtr
->himlSmall
!= NULL
)
5862 lprc
->left
+= infoPtr
->iconSize
.cx
;
5864 nLabelWidth
= LISTVIEW_GetLabelWidth(hwnd
, nItem
);
5865 nLabelWidth
+= TRAILING_PADDING
;
5866 if (lprc
->left
+ nLabelWidth
< nLeftPos
+ infoPtr
->nItemWidth
)
5867 lprc
->right
= lprc
->left
+ nLabelWidth
;
5869 lprc
->right
= nLeftPos
+ infoPtr
->nItemWidth
;
5875 if (uView
== LVS_REPORT
)
5876 nLeftPos
= lprc
->left
= ptItem
.x
+ nIndent
;
5878 nLeftPos
= lprc
->left
= ptItem
.x
;
5879 lprc
->top
= ptItem
.y
;
5880 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5882 if (infoPtr
->himlState
!= NULL
)
5883 lprc
->left
+= infoPtr
->iconSize
.cx
;
5885 if (infoPtr
->himlSmall
!= NULL
)
5886 lprc
->left
+= infoPtr
->iconSize
.cx
;
5888 if (uView
!= LVS_REPORT
)
5890 nLabelWidth
= LISTVIEW_GetLabelWidth(hwnd
, nItem
);
5891 nLabelWidth
+= TRAILING_PADDING
;
5892 if (infoPtr
->himlSmall
)
5893 nLabelWidth
+= IMAGE_PADDING
;
5896 nLabelWidth
= LISTVIEW_GetColumnWidth(hwnd
, 0)-lprc
->left
;
5897 if (lprc
->left
+ nLabelWidth
< nLeftPos
+ infoPtr
->nItemWidth
)
5898 lprc
->right
= lprc
->left
+ nLabelWidth
;
5900 lprc
->right
= nLeftPos
+ infoPtr
->nItemWidth
;
5905 if (!LISTVIEW_GetItemBoundBox(hwnd
, nItem
, &rcInternal
)) break;
5906 ptItem
.x
= rcInternal
.left
;
5907 ptItem
.y
= rcInternal
.top
;
5908 if (uView
== LVS_ICON
)
5910 if (infoPtr
->himlNormal
!= NULL
)
5912 if (LISTVIEW_GetOrigin(hwnd
, &ptOrigin
) != FALSE
)
5915 INT text_left
, text_right
, icon_left
, text_pos_x
;
5916 /* for style LVS_ICON bounds
5917 * left = min(icon.left, text.left)
5918 * right = max(icon.right, text.right)
5919 * top = boundbox.top + NOTHITABLE
5920 * bottom = text.bottom + 1
5923 icon_left
= text_left
= ptItem
.x
;
5925 /* Correct ptItem to icon upper-left */
5926 icon_left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
)/2;
5927 ptItem
.y
+= ICON_TOP_PADDING
;
5929 /* Compute the label left and right */
5930 nLabelWidth
= LISTVIEW_GetLabelWidth(hwnd
, nItem
);
5931 text_pos_x
= infoPtr
->iconSpacing
.cx
- 2*CAPTION_BORDER
- nLabelWidth
;
5934 text_left
+= text_pos_x
/ 2;
5935 text_right
= text_left
+ nLabelWidth
+ 2*CAPTION_BORDER
;
5940 text_right
= text_left
+ infoPtr
->iconSpacing
.cx
- 1;
5943 /* Compute rectangle w/o the text height */
5944 lprc
->left
= min(icon_left
, text_left
) + ptOrigin
.x
;
5945 lprc
->right
= max(icon_left
+ infoPtr
->iconSize
.cx
,
5946 text_right
) + ptOrigin
.x
;
5947 lprc
->top
= ptItem
.y
+ ptOrigin
.y
- ICON_TOP_PADDING_HITABLE
;
5948 lprc
->bottom
= lprc
->top
+ ICON_TOP_PADDING_HITABLE
5949 + infoPtr
->iconSize
.cy
+ 1
5950 + ICON_BOTTOM_PADDING
;
5952 CopyRect (&label_rect
, lprc
);
5953 label_rect
.top
= lprc
->bottom
;
5954 ListView_UpdateLargeItemLabelRect (hwnd
, infoPtr
, nItem
, &label_rect
);
5955 UnionRect (lprc
, lprc
, &label_rect
);
5959 else if (uView
== LVS_SMALLICON
)
5961 if (LISTVIEW_GetOrigin(hwnd
, &ptOrigin
) != FALSE
)
5964 lprc
->left
= ptItem
.x
+ ptOrigin
.x
;
5965 lprc
->right
= lprc
->left
;
5966 lprc
->top
= ptItem
.y
+ ptOrigin
.y
;
5967 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5968 if (infoPtr
->himlState
!= NULL
)
5969 lprc
->right
+= infoPtr
->iconSize
.cx
;
5970 if (infoPtr
->himlSmall
!= NULL
)
5971 lprc
->right
+= infoPtr
->iconSize
.cx
;
5973 nLabelWidth
= LISTVIEW_GetLabelWidth(hwnd
, nItem
);
5974 nLabelWidth
+= TRAILING_PADDING
;
5975 if (infoPtr
->himlSmall
)
5976 nLabelWidth
+= IMAGE_PADDING
;
5977 if (lprc
->right
+ nLabelWidth
< lprc
->left
+ infoPtr
->nItemWidth
)
5978 lprc
->right
+= nLabelWidth
;
5980 lprc
->right
= lprc
->left
+ infoPtr
->nItemWidth
;
5986 lprc
->left
= ptItem
.x
;
5987 if (!(infoPtr
->dwExStyle
&LVS_EX_FULLROWSELECT
) && uView
&LVS_REPORT
)
5988 lprc
->left
+= nIndent
;
5989 lprc
->right
= lprc
->left
;
5990 lprc
->top
= ptItem
.y
;
5991 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
5993 if ((infoPtr
->dwExStyle
& LVS_EX_FULLROWSELECT
) || (uView
== LVS_REPORT
))
5996 int nColumnCount
= Header_GetItemCount(infoPtr
->hwndHeader
);
5997 Header_GetItemRect(infoPtr
->hwndHeader
, nColumnCount
-1, &br
);
5999 lprc
->right
= max(lprc
->left
, br
.right
- REPORT_MARGINX
);
6003 if (infoPtr
->himlState
!= NULL
)
6004 lprc
->right
+= infoPtr
->iconSize
.cx
;
6006 if (infoPtr
->himlSmall
!= NULL
)
6007 lprc
->right
+= infoPtr
->iconSize
.cx
;
6009 nLabelWidth
= LISTVIEW_GetLabelWidth(hwnd
, nItem
);
6010 nLabelWidth
+= TRAILING_PADDING
;
6011 if (lprc
->right
+ nLabelWidth
< lprc
->left
+ infoPtr
->nItemWidth
)
6012 lprc
->right
+= nLabelWidth
;
6014 lprc
->right
= lprc
->left
+ infoPtr
->nItemWidth
;
6019 case LVIR_SELECTBOUNDS
:
6020 if (!ListView_GetItemPosition(hwnd
, nItem
, &ptItem
)) break;
6021 if (uView
== LVS_ICON
)
6023 if (infoPtr
->himlNormal
!= NULL
)
6025 if (LISTVIEW_GetOrigin(hwnd
, &ptOrigin
) != FALSE
)
6028 lprc
->left
= ptItem
.x
+ ptOrigin
.x
;
6029 lprc
->top
= ptItem
.y
+ ptOrigin
.y
;
6030 lprc
->right
= lprc
->left
+ infoPtr
->iconSpacing
.cx
;
6031 lprc
->bottom
= lprc
->top
+ infoPtr
->iconSpacing
.cy
;
6035 else if (uView
== LVS_SMALLICON
)
6037 if (LISTVIEW_GetOrigin(hwnd
, &ptOrigin
) != FALSE
)
6040 nLeftPos
= lprc
->left
= ptItem
.x
+ ptOrigin
.x
;
6041 lprc
->top
= ptItem
.y
+ ptOrigin
.y
;
6042 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
6044 if (infoPtr
->himlState
!= NULL
)
6045 lprc
->left
+= infoPtr
->iconSize
.cx
;
6047 lprc
->right
= lprc
->left
;
6049 if (infoPtr
->himlSmall
!= NULL
)
6050 lprc
->right
+= infoPtr
->iconSize
.cx
;
6052 nLabelWidth
= LISTVIEW_GetLabelWidth(hwnd
, nItem
);
6053 nLabelWidth
+= TRAILING_PADDING
;
6054 if (lprc
->right
+ nLabelWidth
< nLeftPos
+ infoPtr
->nItemWidth
)
6055 lprc
->right
+= nLabelWidth
;
6057 lprc
->right
= nLeftPos
+ infoPtr
->nItemWidth
;
6063 if (!(infoPtr
->dwExStyle
&LVS_EX_FULLROWSELECT
) && (uView
&LVS_REPORT
))
6064 nLeftPos
= lprc
->left
= ptItem
.x
+ nIndent
;
6066 nLeftPos
= lprc
->left
= ptItem
.x
;
6067 lprc
->top
= ptItem
.y
;
6068 lprc
->bottom
= lprc
->top
+ infoPtr
->nItemHeight
;
6070 if (infoPtr
->himlState
!= NULL
)
6071 lprc
->left
+= infoPtr
->iconSize
.cx
;
6073 lprc
->right
= lprc
->left
;
6075 if (infoPtr
->dwExStyle
& LVS_EX_FULLROWSELECT
)
6078 int nColumnCount
= Header_GetItemCount(infoPtr
->hwndHeader
);
6079 Header_GetItemRect(infoPtr
->hwndHeader
, nColumnCount
-1, &br
);
6081 lprc
->right
= max(lprc
->left
, br
.right
- REPORT_MARGINX
);
6085 if (infoPtr
->himlSmall
!= NULL
)
6086 lprc
->right
+= infoPtr
->iconSize
.cx
;
6088 nLabelWidth
= LISTVIEW_GetLabelWidth(hwnd
, nItem
);
6089 nLabelWidth
+= TRAILING_PADDING
;
6090 if (infoPtr
->himlSmall
)
6091 nLabelWidth
+= IMAGE_PADDING
;
6092 if (lprc
->right
+ nLabelWidth
< nLeftPos
+ infoPtr
->nItemWidth
)
6093 lprc
->right
+= nLabelWidth
;
6095 lprc
->right
= nLeftPos
+ infoPtr
->nItemWidth
;
6102 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult
? "TRUE" : "FALSE",
6103 lprc
->left
, lprc
->top
, lprc
->right
, lprc
->bottom
);
6109 static LRESULT
LISTVIEW_GetSubItemRect(HWND hwnd
, INT nItem
, INT nSubItem
, INT
6112 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
6115 TRACE("(hwnd=%x, nItem=%d, nSubItem=%d lprc=%p)\n", hwnd
, nItem
, nSubItem
,
6118 if (!(uView
& LVS_REPORT
))
6121 if (flags
& LVIR_ICON
)
6123 FIXME("Unimplemented LVIR_ICON\n");
6128 int top
= min(((LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0))->nColumnCount
,
6131 LISTVIEW_GetItemRect(hwnd
,nItem
,lprc
);
6132 for (count
= 0; count
< top
; count
++)
6133 lprc
->left
+= LISTVIEW_GetColumnWidth(hwnd
,count
);
6135 lprc
->right
= LISTVIEW_GetColumnWidth(hwnd
,(nSubItem
-1)) +
6144 * Retrieves the width of a label.
6147 * [I] HWND : window handle
6150 * SUCCESS : string width (in pixels)
6153 static INT
LISTVIEW_GetLabelWidth(HWND hwnd
, INT nItem
)
6155 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6156 INT nLabelWidth
= 0;
6159 TRACE("(hwnd=%x, nItem=%d)\n", hwnd
, nItem
);
6161 ZeroMemory(&lvItem
, sizeof(lvItem
));
6162 lvItem
.mask
= LVIF_TEXT
;
6163 lvItem
.iItem
= nItem
;
6164 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6165 lvItem
.pszText
= szDispText
;
6166 if (LISTVIEW_GetItemW(hwnd
, &lvItem
, TRUE
))
6167 nLabelWidth
= ListView_GetStringWidthW(hwnd
, lvItem
.pszText
);
6174 * Retrieves the spacing between listview control items.
6177 * [I] HWND : window handle
6178 * [I] BOOL : flag for small or large icon
6181 * Horizontal + vertical spacing
6183 static LRESULT
LISTVIEW_GetItemSpacing(HWND hwnd
, BOOL bSmall
)
6185 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
6188 if (bSmall
== FALSE
)
6190 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
6194 LONG style
= GetWindowLongW(hwnd
, GWL_STYLE
);
6195 if ((style
& LVS_TYPEMASK
) == LVS_ICON
)
6196 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
6198 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
6205 * Retrieves the state of a listview control item.
6208 * [I] HWND : window handle
6209 * [I] INT : item index
6210 * [I] UINT : state mask
6213 * State specified by the mask.
6215 static LRESULT
LISTVIEW_GetItemState(HWND hwnd
, INT nItem
, UINT uMask
)
6217 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
6221 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
6223 ZeroMemory(&lvItem
, sizeof(lvItem
));
6224 lvItem
.iItem
= nItem
;
6225 lvItem
.stateMask
= uMask
;
6226 lvItem
.mask
= LVIF_STATE
;
6227 if (LISTVIEW_GetItemW(hwnd
, &lvItem
, TRUE
))
6228 uState
= lvItem
.state
;
6236 * Retrieves the text of a listview control item or subitem.
6239 * [I] hwnd : window handle
6240 * [I] nItem : item index
6241 * [IO] lpLVItem : item information
6242 * [I] isW : TRUE if lpLVItem is Unicode
6245 * SUCCESS : string length
6248 static LRESULT
LISTVIEW_GetItemTextT(HWND hwnd
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
6250 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
6253 if (lpLVItem
!= NULL
)
6255 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
6257 lpLVItem
->mask
= LVIF_TEXT
;
6258 lpLVItem
->iItem
= nItem
;
6259 if (LISTVIEW_GetItemT(hwnd
, lpLVItem
, FALSE
, isW
))
6260 nLength
= textlenT(lpLVItem
->pszText
, isW
);
6269 * Searches for an item based on properties + relationships.
6272 * [I] HWND : window handle
6273 * [I] INT : item index
6274 * [I] INT : relationship flag
6277 * SUCCESS : item index
6280 static LRESULT
LISTVIEW_GetNextItem(HWND hwnd
, INT nItem
, UINT uFlags
)
6282 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
6283 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
6285 LVFINDINFOW lvFindInfo
;
6286 INT nCountPerColumn
;
6289 if ((nItem
>= -1) && (nItem
< GETITEMCOUNT(infoPtr
)))
6291 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
6293 if (uFlags
& LVNI_CUT
)
6296 if (uFlags
& LVNI_DROPHILITED
)
6297 uMask
|= LVIS_DROPHILITED
;
6299 if (uFlags
& LVNI_FOCUSED
)
6300 uMask
|= LVIS_FOCUSED
;
6302 if (uFlags
& LVNI_SELECTED
)
6303 uMask
|= LVIS_SELECTED
;
6305 if (uFlags
& LVNI_ABOVE
)
6307 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
6312 if ((ListView_GetItemState(hwnd
, nItem
, uMask
) & uMask
) == uMask
)
6318 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6319 lvFindInfo
.vkDirection
= VK_UP
;
6320 ListView_GetItemPosition(hwnd
, nItem
, &lvFindInfo
.pt
);
6321 while ((nItem
= ListView_FindItemW(hwnd
, nItem
, &lvFindInfo
)) != -1)
6323 if ((ListView_GetItemState(hwnd
, nItem
, uMask
) & uMask
) == uMask
)
6328 else if (uFlags
& LVNI_BELOW
)
6330 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
6332 while (nItem
< GETITEMCOUNT(infoPtr
))
6335 if ((ListView_GetItemState(hwnd
, nItem
, uMask
) & uMask
) == uMask
)
6341 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6342 lvFindInfo
.vkDirection
= VK_DOWN
;
6343 ListView_GetItemPosition(hwnd
, nItem
, &lvFindInfo
.pt
);
6344 while ((nItem
= ListView_FindItemW(hwnd
, nItem
, &lvFindInfo
)) != -1)
6346 if ((ListView_GetItemState(hwnd
, nItem
, uMask
) & uMask
) == uMask
)
6351 else if (uFlags
& LVNI_TOLEFT
)
6353 if (uView
== LVS_LIST
)
6355 nCountPerColumn
= LISTVIEW_GetCountPerColumn(hwnd
);
6356 while (nItem
- nCountPerColumn
>= 0)
6358 nItem
-= nCountPerColumn
;
6359 if ((ListView_GetItemState(hwnd
, nItem
, uMask
) & uMask
) == uMask
)
6363 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6365 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6366 lvFindInfo
.vkDirection
= VK_LEFT
;
6367 ListView_GetItemPosition(hwnd
, nItem
, &lvFindInfo
.pt
);
6368 while ((nItem
= ListView_FindItemW(hwnd
, nItem
, &lvFindInfo
)) != -1)
6370 if ((ListView_GetItemState(hwnd
, nItem
, uMask
) & uMask
) == uMask
)
6375 else if (uFlags
& LVNI_TORIGHT
)
6377 if (uView
== LVS_LIST
)
6379 nCountPerColumn
= LISTVIEW_GetCountPerColumn(hwnd
);
6380 while (nItem
+ nCountPerColumn
< GETITEMCOUNT(infoPtr
))
6382 nItem
+= nCountPerColumn
;
6383 if ((ListView_GetItemState(hwnd
, nItem
, uMask
) & uMask
) == uMask
)
6387 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6389 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6390 lvFindInfo
.vkDirection
= VK_RIGHT
;
6391 ListView_GetItemPosition(hwnd
, nItem
, &lvFindInfo
.pt
);
6392 while ((nItem
= ListView_FindItemW(hwnd
, nItem
, &lvFindInfo
)) != -1)
6394 if ((ListView_GetItemState(hwnd
, nItem
, uMask
) & uMask
) == uMask
)
6403 /* search by index */
6404 for (i
= nItem
; i
< GETITEMCOUNT(infoPtr
); i
++)
6406 if ((ListView_GetItemState(hwnd
, i
, uMask
) & uMask
) == uMask
)
6415 /* LISTVIEW_GetNumberOfWorkAreas */
6419 * Retrieves the origin coordinates when in icon or small icon display mode.
6422 * [I] HWND : window handle
6423 * [O] LPPOINT : coordinate information
6429 static LRESULT
LISTVIEW_GetOrigin(HWND hwnd
, LPPOINT lpptOrigin
)
6431 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
6432 UINT uView
= lStyle
& LVS_TYPEMASK
;
6433 BOOL bResult
= FALSE
;
6435 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd
, lpptOrigin
);
6437 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6439 SCROLLINFO scrollInfo
;
6440 ZeroMemory(lpptOrigin
, sizeof(POINT
));
6441 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
6442 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
6444 if (lStyle
& WS_HSCROLL
)
6446 scrollInfo
.fMask
= SIF_POS
;
6447 if (GetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
) != FALSE
)
6448 lpptOrigin
->x
= -scrollInfo
.nPos
;
6451 if (lStyle
& WS_VSCROLL
)
6453 scrollInfo
.fMask
= SIF_POS
;
6454 if (GetScrollInfo(hwnd
, SB_VERT
, &scrollInfo
) != FALSE
)
6455 lpptOrigin
->y
= -scrollInfo
.nPos
;
6466 * Retrieves the number of items that are marked as selected.
6469 * [I] HWND : window handle
6472 * Number of items selected.
6474 static LRESULT
LISTVIEW_GetSelectedCount(HWND hwnd
)
6477 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
6478 INT nSelectedCount
= 0;
6481 for (i
= 0; i
< GETITEMCOUNT(infoPtr
); i
++)
6483 if (ListView_GetItemState(hwnd
, i
, LVIS_SELECTED
) & LVIS_SELECTED
)
6487 return nSelectedCount
;
6492 * Retrieves item index that marks the start of a multiple selection.
6495 * [I] HWND : window handle
6498 * Index number or -1 if there is no selection mark.
6500 static LRESULT
LISTVIEW_GetSelectionMark(HWND hwnd
)
6502 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
6504 return infoPtr
->nSelectionMark
;
6510 * Retrieves the width of a string.
6513 * [I] hwnd : window handle
6514 * [I] lpszText : text string to process
6515 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6518 * SUCCESS : string width (in pixels)
6521 static LRESULT
LISTVIEW_GetStringWidthT(HWND hwnd
, LPCWSTR lpszText
, BOOL isW
)
6523 if (is_textT(lpszText
, isW
))
6525 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
6526 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
6527 HDC hdc
= GetDC(hwnd
);
6528 HFONT hOldFont
= SelectObject(hdc
, hFont
);
6530 ZeroMemory(&stringSize
, sizeof(SIZE
));
6532 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
6534 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
6535 SelectObject(hdc
, hOldFont
);
6536 ReleaseDC(hwnd
, hdc
);
6537 return stringSize
.cx
;
6544 * Retrieves the text backgound color.
6547 * [I] HWND : window handle
6550 * COLORREF associated with the the background.
6552 static LRESULT
LISTVIEW_GetTextBkColor(HWND hwnd
)
6554 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
6556 return infoPtr
->clrTextBk
;
6561 * Retrieves the text color.
6564 * [I] HWND : window handle
6567 * COLORREF associated with the text.
6569 static LRESULT
LISTVIEW_GetTextColor(HWND hwnd
)
6571 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
6573 return infoPtr
->clrText
;
6578 * Determines item if a hit or closest if not
6581 * [I] HWND : window handle
6582 * [IO] LPLV_INTHIT : hit test information
6583 * [I] subitem : fill out iSubItem.
6586 * SUCCESS : item index of hit
6589 static INT
LISTVIEW_SuperHitTestItem(HWND hwnd
, LPLV_INTHIT lpInt
, BOOL subitem
)
6591 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
6592 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
6593 UINT uView
= lStyle
& LVS_TYPEMASK
;
6594 INT i
,j
,topindex
,bottomindex
;
6595 RECT rcItem
,rcSubItem
;
6596 DWORD xterm
, yterm
, dist
;
6598 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd
, lpInt
->ht
.pt
.x
, lpInt
->ht
.pt
.y
);
6600 topindex
= ListView_GetTopIndex(hwnd
);
6601 if (uView
== LVS_REPORT
)
6603 bottomindex
= topindex
+ LISTVIEW_GetCountPerColumn(hwnd
) + 1;
6604 bottomindex
= min(bottomindex
,GETITEMCOUNT(infoPtr
));
6608 bottomindex
= GETITEMCOUNT(infoPtr
);
6611 lpInt
->distance
= 0x7fffffff;
6612 lpInt
->iDistItem
= -1;
6614 for (i
= topindex
; i
< bottomindex
; i
++)
6616 rcItem
.left
= LVIR_BOUNDS
;
6617 if (LISTVIEW_GetItemRect(hwnd
, i
, &rcItem
))
6619 if (PtInRect(&rcItem
, lpInt
->ht
.pt
))
6622 rcItem
.left
= LVIR_ICON
;
6623 if (LISTVIEW_GetItemRect(hwnd
, i
, &rcItem
))
6625 if (PtInRect(&rcItem
, lpInt
->ht
.pt
))
6627 lpInt
->ht
.flags
= LVHT_ONITEMICON
;
6628 lpInt
->ht
.iItem
= i
;
6633 rcItem
.left
= LVIR_LABEL
;
6634 if (LISTVIEW_GetItemRect(hwnd
, i
, &rcItem
))
6636 if (PtInRect(&rcItem
, lpInt
->ht
.pt
))
6638 lpInt
->ht
.flags
= LVHT_ONITEMLABEL
;
6639 lpInt
->ht
.iItem
= i
;
6644 lpInt
->ht
.flags
= LVHT_ONITEMSTATEICON
;
6645 lpInt
->ht
.iItem
= i
;
6649 lpInt
->ht
.iSubItem
= 0;
6650 rcSubItem
.right
= rcSubItem
.left
;
6651 for (j
= 0; j
< infoPtr
->nColumnCount
; j
++)
6653 rcSubItem
.left
= rcSubItem
.right
;
6654 rcSubItem
.right
+= LISTVIEW_GetColumnWidth(hwnd
, j
);
6655 if (PtInRect(&rcSubItem
, lpInt
->ht
.pt
))
6657 lpInt
->ht
.iSubItem
= j
;
6667 * Now compute distance from point to center of boundary
6668 * box. Since we are only interested in the relative
6669 * distance, we can skip the nasty square root operation
6671 xterm
= rcItem
.left
+ (rcItem
.right
- rcItem
.left
)/2 - lpInt
->ht
.pt
.x
;
6672 yterm
= rcItem
.top
+ (rcItem
.bottom
- rcItem
.top
)/2 - lpInt
->ht
.pt
.y
;
6673 dist
= xterm
* xterm
+ yterm
* yterm
;
6674 if (dist
< lpInt
->distance
)
6676 lpInt
->distance
= dist
;
6677 lpInt
->iDistItem
= i
;
6683 lpInt
->ht
.flags
= LVHT_NOWHERE
;
6684 TRACE("no hit, closest item %d, distance %ld\n", lpInt
->iDistItem
, lpInt
->distance
);
6691 * Determines which section of the item was selected (if any).
6694 * [I] HWND : window handle
6695 * [IO] LPLVHITTESTINFO : hit test information
6696 * [I] subitem : fill out iSubItem.
6699 * SUCCESS : item index
6702 static INT
LISTVIEW_HitTestItem(HWND hwnd
, LPLVHITTESTINFO lpHitTestInfo
, BOOL subitem
)
6705 LV_INTHIT lv_inthit
;
6707 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd
, lpHitTestInfo
->pt
.x
,
6708 lpHitTestInfo
->pt
.y
);
6710 memcpy(&lv_inthit
, lpHitTestInfo
, sizeof(LVHITTESTINFO
));
6711 ret
= LISTVIEW_SuperHitTestItem(hwnd
, &lv_inthit
, subitem
);
6712 memcpy(lpHitTestInfo
, &lv_inthit
, sizeof(LVHITTESTINFO
));
6718 * Determines which listview item is located at the specified position.
6721 * [I] HWND : window handle
6722 * [IO} LPLVHITTESTINFO : hit test information
6725 * SUCCESS : item index
6728 static LRESULT
LISTVIEW_HitTest(HWND hwnd
, LPLVHITTESTINFO lpHitTestInfo
)
6730 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
6733 lpHitTestInfo
->flags
= 0;
6735 if (infoPtr
->rcList
.left
> lpHitTestInfo
->pt
.x
)
6736 lpHitTestInfo
->flags
= LVHT_TOLEFT
;
6737 else if (infoPtr
->rcList
.right
< lpHitTestInfo
->pt
.x
)
6738 lpHitTestInfo
->flags
= LVHT_TORIGHT
;
6739 if (infoPtr
->rcList
.top
> lpHitTestInfo
->pt
.y
)
6740 lpHitTestInfo
->flags
|= LVHT_ABOVE
;
6741 else if (infoPtr
->rcList
.bottom
< lpHitTestInfo
->pt
.y
)
6742 lpHitTestInfo
->flags
|= LVHT_BELOW
;
6744 if (lpHitTestInfo
->flags
== 0)
6746 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6747 * an app might pass only a structure with space up to iItem!
6748 * (MS Office 97 does that for instance in the file open dialog)
6750 nItem
= LISTVIEW_HitTestItem(hwnd
, lpHitTestInfo
, FALSE
);
6758 * Determines which listview subitem is located at the specified position.
6761 * [I] HWND : window handle
6762 * [IO} LPLVHITTESTINFO : hit test information
6765 * SUCCESS : item index
6768 static LRESULT
LISTVIEW_SubItemHitTest(HWND hwnd
, LPLVHITTESTINFO lpHitTestInfo
)
6770 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
6773 lpHitTestInfo
->flags
= 0;
6775 if (infoPtr
->rcList
.left
> lpHitTestInfo
->pt
.x
)
6776 lpHitTestInfo
->flags
= LVHT_TOLEFT
;
6777 else if (infoPtr
->rcList
.right
< lpHitTestInfo
->pt
.x
)
6778 lpHitTestInfo
->flags
= LVHT_TORIGHT
;
6779 if (infoPtr
->rcList
.top
> lpHitTestInfo
->pt
.y
)
6780 lpHitTestInfo
->flags
|= LVHT_ABOVE
;
6781 else if (infoPtr
->rcList
.bottom
< lpHitTestInfo
->pt
.y
)
6782 lpHitTestInfo
->flags
|= LVHT_BELOW
;
6784 if (lpHitTestInfo
->flags
== 0)
6785 nItem
= LISTVIEW_HitTestItem(hwnd
, lpHitTestInfo
, TRUE
);
6792 * Inserts a new column.
6795 * [I] HWND : window handle
6796 * [I] INT : column index
6797 * [I] LPLVCOLUMNW : column information
6800 * SUCCESS : new column index
6803 static LRESULT
LISTVIEW_InsertColumnT(HWND hwnd
, INT nColumn
,
6804 LPLVCOLUMNW lpColumn
, BOOL isW
)
6806 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
6807 INT nNewColumn
= -1;
6810 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd
, nColumn
, lpColumn
);
6812 if (lpColumn
!= NULL
)
6814 /* initialize memory */
6815 ZeroMemory(&hdi
, sizeof(hdi
));
6817 if (lpColumn
->mask
& LVCF_FMT
)
6819 /* format member is valid */
6820 hdi
.mask
|= HDI_FORMAT
;
6822 /* set text alignment (leftmost column must be left-aligned) */
6825 hdi
.fmt
|= HDF_LEFT
;
6829 if (lpColumn
->fmt
& LVCFMT_LEFT
)
6831 hdi
.fmt
|= HDF_LEFT
;
6833 else if (lpColumn
->fmt
& LVCFMT_RIGHT
)
6835 hdi
.fmt
|= HDF_RIGHT
;
6837 else if (lpColumn
->fmt
& LVCFMT_CENTER
)
6839 hdi
.fmt
|= HDF_CENTER
;
6843 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6845 hdi
.fmt
|= HDF_BITMAP_ON_RIGHT
;
6849 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6854 if (lpColumn
->fmt
& LVCFMT_IMAGE
)
6856 hdi
.fmt
|= HDF_IMAGE
;
6857 hdi
.iImage
= I_IMAGECALLBACK
;
6861 if (lpColumn
->mask
& LVCF_WIDTH
)
6863 hdi
.mask
|= HDI_WIDTH
;
6864 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
6866 /* make it fill the remainder of the controls width */
6871 ZeroMemory(&hdit
, sizeof(hdit
));
6873 /* get the width of every item except the current one */
6874 hdit
.mask
= HDI_WIDTH
;
6877 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++) {
6878 Header_GetItemW(infoPtr
->hwndHeader
, item_index
, (LPARAM
)(&hdit
));
6882 /* retrieve the layout of the header */
6883 GetClientRect(hwnd
, &rcHeader
);
6884 /* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6885 TRACE("start cxy=%d left=%d right=%d\n", hdi
.cxy
, rcHeader
.left
, rcHeader
.right
);
6887 hdi
.cxy
= (rcHeader
.right
- rcHeader
.left
) - hdi
.cxy
;
6890 hdi
.cxy
= lpColumn
->cx
;
6893 if (lpColumn
->mask
& LVCF_TEXT
)
6895 hdi
.mask
|= HDI_TEXT
| HDI_FORMAT
;
6896 hdi
.pszText
= lpColumn
->pszText
;
6897 hdi
.cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
6898 hdi
.fmt
|= HDF_STRING
;
6901 if (lpColumn
->mask
& LVCF_IMAGE
)
6903 hdi
.mask
|= HDI_IMAGE
;
6904 hdi
.iImage
= lpColumn
->iImage
;
6907 if (lpColumn
->mask
& LVCF_ORDER
)
6909 hdi
.mask
|= HDI_ORDER
;
6910 hdi
.iOrder
= lpColumn
->iOrder
;
6913 /* insert item in header control */
6914 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
, HDM_INSERTITEMT(isW
),
6915 (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6917 /* Need to reset the item width when inserting a new column */
6918 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(hwnd
);
6920 LISTVIEW_UpdateScroll(hwnd
);
6921 InvalidateRect(hwnd
, NULL
, FALSE
);
6927 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6928 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6929 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6930 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6931 their own sort proc. when sending LVM_SORTITEMS.
6934 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6936 LVS_SORTXXX must be specified,
6937 LVS_OWNERDRAW is not set,
6938 <item>.pszText is not LPSTR_TEXTCALLBACK.
6940 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6941 are sorted based on item text..."
6943 static INT WINAPI
LISTVIEW_InsertCompare( LPVOID first
, LPVOID second
, LPARAM lParam
)
6945 LONG lStyle
= GetWindowLongW((HWND
) lParam
, GWL_STYLE
);
6946 LISTVIEW_ITEM
* lv_first
= (LISTVIEW_ITEM
*) DPA_GetPtr( (HDPA
)first
, 0 );
6947 LISTVIEW_ITEM
* lv_second
= (LISTVIEW_ITEM
*) DPA_GetPtr( (HDPA
)second
, 0 );
6948 INT cmpv
= lstrcmpW( lv_first
->pszText
, lv_second
->pszText
);
6949 /* if we're sorting descending, negate the return value */
6950 return (lStyle
& LVS_SORTDESCENDING
) ? -cmpv
: cmpv
;
6955 * Inserts a new item in the listview control.
6958 * [I] HWND : window handle
6959 * [I] LPLVITEMW : item information
6960 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6963 * SUCCESS : new item index
6966 static LRESULT
LISTVIEW_InsertItemT(HWND hwnd
, LPLVITEMW lpLVItem
, BOOL isW
)
6968 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
6969 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
6970 UINT uView
= lStyle
& LVS_TYPEMASK
;
6974 LISTVIEW_ITEM
*lpItem
= NULL
;
6976 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
6977 hwnd
, debuglvitem_t(lpLVItem
, isW
), isW
);
6979 if (lStyle
& LVS_OWNERDATA
)
6981 nItem
= infoPtr
->hdpaItems
->nItemCount
;
6982 infoPtr
->hdpaItems
->nItemCount
++;
6986 if (lpLVItem
!= NULL
)
6988 /* make sure it's not a subitem; cannot insert a subitem */
6989 if (lpLVItem
->iSubItem
== 0)
6991 if ( (lpItem
= (LISTVIEW_ITEM
*)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM
))) )
6993 ZeroMemory(lpItem
, sizeof(LISTVIEW_ITEM
));
6994 if (LISTVIEW_InitItemT(hwnd
, lpItem
, lpLVItem
, isW
))
6996 /* insert item in listview control data structure */
6997 if ( (hdpaSubItems
= DPA_Create(8)) )
6999 if ( (nItem
= DPA_InsertPtr(hdpaSubItems
, 0, lpItem
)) != -1)
7001 if ( ((lStyle
& LVS_SORTASCENDING
) || (lStyle
& LVS_SORTDESCENDING
))
7002 && !(lStyle
& LVS_OWNERDRAWFIXED
)
7003 && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
) )
7005 /* Insert the item in the proper sort order based on the pszText
7006 member. See comments for LISTVIEW_InsertCompare() for greater detail */
7007 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
,
7008 GETITEMCOUNT( infoPtr
) + 1, hdpaSubItems
);
7009 DPA_Sort( infoPtr
->hdpaItems
, LISTVIEW_InsertCompare
, hwnd
);
7010 nItem
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, hdpaSubItems
);
7014 nItem
= DPA_InsertPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
,
7021 LISTVIEW_ShiftIndices(hwnd
,nItem
,1);
7023 /* manage item focus */
7024 if (lpLVItem
->mask
& LVIF_STATE
)
7026 lpItem
->state
&= ~(LVIS_FOCUSED
|LVIS_SELECTED
);
7027 if (lpLVItem
->stateMask
& LVIS_SELECTED
)
7028 LISTVIEW_SetSelection(hwnd
, nItem
);
7029 else if (lpLVItem
->stateMask
& LVIS_FOCUSED
)
7030 LISTVIEW_SetItemFocus(hwnd
, nItem
);
7033 /* send LVN_INSERTITEM notification */
7034 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
7036 nmlv
.lParam
= lpItem
->lParam
;
7037 listview_notify(hwnd
, LVN_INSERTITEM
, &nmlv
);
7039 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_LIST
))
7041 nItemWidth
= LISTVIEW_CalculateWidth(hwnd
, lpLVItem
->iItem
);
7042 if (nItemWidth
> infoPtr
->nItemWidth
)
7043 infoPtr
->nItemWidth
= nItemWidth
;
7046 /* align items (set position of each item) */
7047 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
7049 if (lStyle
& LVS_ALIGNLEFT
)
7050 LISTVIEW_AlignLeft(hwnd
);
7052 LISTVIEW_AlignTop(hwnd
);
7055 LISTVIEW_UpdateScroll(hwnd
);
7056 /* refresh client area */
7057 InvalidateRect(hwnd
, NULL
, FALSE
);
7066 /* free memory if unsuccessful */
7067 if ((nItem
== -1) && (lpItem
!= NULL
))
7068 COMCTL32_Free(lpItem
);
7075 * Redraws a range of items.
7078 * [I] HWND : window handle
7079 * [I] INT : first item
7080 * [I] INT : last item
7086 static LRESULT
LISTVIEW_RedrawItems(HWND hwnd
, INT nFirst
, INT nLast
)
7088 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
7089 BOOL bResult
= FALSE
;
7093 if (nFirst
<= nLast
)
7095 if ((nFirst
>= 0) && (nFirst
< GETITEMCOUNT(infoPtr
)))
7097 if ((nLast
>= 0) && (nLast
< GETITEMCOUNT(infoPtr
)))
7099 for (i
= nFirst
; i
<= nLast
; i
++)
7101 rcItem
.left
= LVIR_BOUNDS
;
7102 LISTVIEW_GetItemRect(hwnd
, i
, &rcItem
);
7103 InvalidateRect(hwnd
, &rcItem
, TRUE
);
7114 * Scroll the content of a listview.
7117 * [I] HWND : window handle
7118 * [I] INT : horizontal scroll amount in pixels
7119 * [I] INT : vertical scroll amount in pixels
7126 * If the control is in report mode (LVS_REPORT) the control can
7127 * be scrolled only in line increments. "dy" will be rounded to the
7128 * nearest number of pixels that are a whole line. Ex: if line height
7129 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
7130 * is passed the the scroll will be 0. (per MSDN 7/2002)
7132 * For: (per experimentaion with native control and CSpy ListView)
7133 * LVS_ICON dy=1 = 1 pixel (vertical only)
7135 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
7137 * LVS_LIST dx=1 = 1 column (horizontal only)
7138 * but will only scroll 1 column per message
7139 * no matter what the value.
7140 * dy must be 0 or FALSE returned.
7141 * LVS_REPORT dx=1 = 1 pixel
7145 static LRESULT
LISTVIEW_Scroll(HWND hwnd
, INT dx
, INT dy
)
7147 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
7148 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
7149 UINT uView
= lStyle
& LVS_TYPEMASK
;
7152 if (uView
== LVS_REPORT
)
7154 rows
= (abs(dy
) + infoPtr
->nItemHeight
/2) / infoPtr
->nItemHeight
;
7157 mode
= (dy
>0) ? SB_INTERNAL_DOWN
: SB_INTERNAL_UP
;
7158 for ( i
=0; i
<rows
; i
++)
7159 LISTVIEW_VScroll(hwnd
, mode
, 0, hwnd
);
7164 mode
= (dx
>0) ? SB_INTERNAL_RIGHT
: SB_INTERNAL_LEFT
;
7165 for ( i
=0; i
<abs(dx
); i
++)
7166 LISTVIEW_HScroll(hwnd
, mode
, 0, hwnd
);
7170 else if (uView
== LVS_ICON
)
7174 mode
= (dy
>0) ? SB_INTERNAL_DOWN
: SB_INTERNAL_UP
;
7175 for(i
=0; i
<abs(dy
); i
++)
7176 LISTVIEW_VScroll(hwnd
, mode
, 0, hwnd
);
7179 else if (uView
== LVS_SMALLICON
)
7183 mode
= (dy
>0) ? SB_INTERNAL_DOWN
: SB_INTERNAL_UP
;
7184 for(i
=0; i
<abs(dy
); i
++)
7185 LISTVIEW_VScroll(hwnd
, mode
, 0, hwnd
);
7188 else if (uView
== LVS_LIST
)
7190 if (dy
!= 0) return FALSE
;
7191 if (dx
== 0) return TRUE
;
7192 mode
= (dx
>0) ? SB_INTERNAL_RIGHT
: SB_INTERNAL_LEFT
;
7193 LISTVIEW_HScroll(hwnd
, mode
, 0, hwnd
);
7201 * Sets the background color.
7204 * [I] HWND : window handle
7205 * [I] COLORREF : background color
7211 static LRESULT
LISTVIEW_SetBkColor(HWND hwnd
, COLORREF clrBk
)
7213 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
7215 if(infoPtr
->clrBk
!=clrBk
){
7216 infoPtr
->clrBk
= clrBk
;
7217 InvalidateRect(hwnd
, NULL
, TRUE
);
7223 /* LISTVIEW_SetBkImage */
7227 * Sets the callback mask. This mask will be used when the parent
7228 * window stores state information (some or all).
7231 * [I] HWND : window handle
7232 * [I] UINT : state mask
7238 static BOOL
LISTVIEW_SetCallbackMask(HWND hwnd
, UINT uMask
)
7240 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
7242 infoPtr
->uCallbackMask
= uMask
;
7249 * Sets the attributes of a header item.
7252 * [I] HWND : window handle
7253 * [I] INT : column index
7254 * [I] LPLVCOLUMNW : column attributes
7255 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
7256 * otherwise it is in fact a LPLVCOLUMNA
7262 static LRESULT
LISTVIEW_SetColumnT(HWND hwnd
, INT nColumn
,
7263 LPLVCOLUMNW lpColumn
, BOOL isW
)
7265 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
7266 BOOL bResult
= FALSE
;
7267 HDITEMW hdi
, hdiget
;
7269 if ((lpColumn
!= NULL
) && (nColumn
>= 0) &&
7270 (nColumn
< Header_GetItemCount(infoPtr
->hwndHeader
)))
7272 /* initialize memory */
7273 ZeroMemory(&hdi
, sizeof(hdi
));
7275 if (lpColumn
->mask
& LVCF_FMT
)
7277 /* format member is valid */
7278 hdi
.mask
|= HDI_FORMAT
;
7280 /* get current format first */
7281 hdiget
.mask
= HDI_FORMAT
;
7282 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdiget
))
7283 /* preserve HDF_STRING if present */
7284 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
7286 /* set text alignment (leftmost column must be left-aligned) */
7289 hdi
.fmt
|= HDF_LEFT
;
7293 if (lpColumn
->fmt
& LVCFMT_LEFT
)
7294 hdi
.fmt
|= HDF_LEFT
;
7295 else if (lpColumn
->fmt
& LVCFMT_RIGHT
)
7296 hdi
.fmt
|= HDF_RIGHT
;
7297 else if (lpColumn
->fmt
& LVCFMT_CENTER
)
7298 hdi
.fmt
|= HDF_CENTER
;
7301 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
7302 hdi
.fmt
|= HDF_BITMAP_ON_RIGHT
;
7304 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
7305 hdi
.fmt
|= HDF_IMAGE
;
7307 if (lpColumn
->fmt
& LVCFMT_IMAGE
)
7309 hdi
.fmt
|= HDF_IMAGE
;
7310 hdi
.iImage
= I_IMAGECALLBACK
;
7314 if (lpColumn
->mask
& LVCF_WIDTH
)
7316 hdi
.mask
|= HDI_WIDTH
;
7317 hdi
.cxy
= lpColumn
->cx
;
7320 if (lpColumn
->mask
& LVCF_TEXT
)
7322 hdi
.mask
|= HDI_TEXT
| HDI_FORMAT
;
7323 hdi
.pszText
= lpColumn
->pszText
;
7324 hdi
.cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
7325 hdi
.fmt
|= HDF_STRING
;
7328 if (lpColumn
->mask
& LVCF_IMAGE
)
7330 hdi
.mask
|= HDI_IMAGE
;
7331 hdi
.iImage
= lpColumn
->iImage
;
7334 if (lpColumn
->mask
& LVCF_ORDER
)
7336 hdi
.mask
|= HDI_ORDER
;
7337 hdi
.iOrder
= lpColumn
->iOrder
;
7340 /* set header item attributes */
7342 bResult
= Header_SetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
);
7344 bResult
= Header_SetItemA(infoPtr
->hwndHeader
, nColumn
, &hdi
);
7352 * Sets the column order array
7355 * [I] HWND : window handle
7356 * [I] INT : number of elements in column order array
7357 * [I] INT : pointer to column order array
7363 static LRESULT
LISTVIEW_SetColumnOrderArray(HWND hwnd
, INT iCount
, LPINT lpiArray
)
7365 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
7367 FIXME("iCount %d lpiArray %p\n", iCount
, lpiArray
);
7378 * Sets the width of a column
7381 * [I] HWND : window handle
7382 * [I] INT : column index
7383 * [I] INT : column width
7389 static LRESULT
LISTVIEW_SetColumnWidth(HWND hwnd
, INT iCol
, INT cx
)
7391 LISTVIEW_INFO
*infoPtr
;
7394 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
7395 UINT uView
= lStyle
& LVS_TYPEMASK
;
7400 WCHAR text_buffer
[DISP_TEXT_SIZE
];
7401 INT header_item_count
;
7406 WCHAR szDispText
[DISP_TEXT_SIZE
];
7408 /* make sure we can get the listview info */
7409 if (!(infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0)))
7412 if (!infoPtr
->hwndHeader
) /* make sure we have a header */
7415 /* set column width only if in report or list mode */
7416 if ((uView
!= LVS_REPORT
) && (uView
!= LVS_LIST
))
7419 TRACE("(hwnd=%x, iCol=%d, cx=%d\n", hwnd
, iCol
, cx
);
7421 /* take care of invalid cx values */
7422 if((uView
== LVS_REPORT
) && (cx
< -2))
7423 cx
= LVSCW_AUTOSIZE
;
7424 else if (uView
== LVS_LIST
&& (cx
< 1))
7427 /* resize all columns if in LVS_LIST mode */
7428 if(uView
== LVS_LIST
) {
7429 infoPtr
->nItemWidth
= cx
;
7430 InvalidateRect(hwnd
, NULL
, TRUE
); /* force redraw of the listview */
7434 /* autosize based on listview items width */
7435 if(cx
== LVSCW_AUTOSIZE
)
7437 /* set the width of the column to the width of the widest item */
7438 if (iCol
== 0 || uView
== LVS_LIST
)
7441 for(item_index
= 0; item_index
< GETITEMCOUNT(infoPtr
); item_index
++)
7443 nLabelWidth
= LISTVIEW_GetLabelWidth(hwnd
, item_index
);
7444 cx
= (nLabelWidth
>cx
)?nLabelWidth
:cx
;
7446 if (infoPtr
->himlSmall
)
7447 cx
+= infoPtr
->iconSize
.cx
+ IMAGE_PADDING
;
7451 ZeroMemory(&lvItem
, sizeof(lvItem
));
7452 lvItem
.iSubItem
= iCol
;
7453 lvItem
.mask
= LVIF_TEXT
;
7454 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7455 lvItem
.pszText
= szDispText
;
7456 *lvItem
.pszText
= '\0';
7458 for(item_index
= 0; item_index
< GETITEMCOUNT(infoPtr
); item_index
++)
7460 lvItem
.iItem
= item_index
;
7461 LISTVIEW_GetItemT(hwnd
, &lvItem
, FALSE
, TRUE
);
7462 nLabelWidth
= LISTVIEW_GetStringWidthT(hwnd
, lvItem
.pszText
, TRUE
);
7463 cx
= (nLabelWidth
>cx
)?nLabelWidth
:cx
;
7466 cx
+= TRAILING_PADDING
;
7467 } /* autosize based on listview header width */
7468 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
7470 header_item_count
= Header_GetItemCount(infoPtr
->hwndHeader
);
7472 /* if iCol is the last column make it fill the remainder of the controls width */
7473 if(iCol
== (header_item_count
- 1)) {
7474 /* get the width of every item except the current one */
7475 hdi
.mask
= HDI_WIDTH
;
7478 for(item_index
= 0; item_index
< (header_item_count
- 1); item_index
++) {
7479 Header_GetItemW(infoPtr
->hwndHeader
, item_index
, (LPARAM
)(&hdi
));
7483 /* retrieve the layout of the header */
7484 GetWindowRect(infoPtr
->hwndHeader
, &rcHeader
);
7486 cx
= (rcHeader
.right
- rcHeader
.left
) - cx
;
7490 /* Despite what the MS docs say, if this is not the last
7491 column, then MS resizes the column to the width of the
7492 largest text string in the column, including headers
7493 and items. This is different from LVSCW_AUTOSIZE in that
7494 LVSCW_AUTOSIZE ignores the header string length.
7497 /* retrieve header font */
7498 header_font
= SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0L, 0L);
7500 /* retrieve header text */
7501 hdi
.mask
= HDI_TEXT
;
7502 hdi
.cchTextMax
= sizeof(text_buffer
)/sizeof(text_buffer
[0]);
7503 hdi
.pszText
= text_buffer
;
7505 Header_GetItemW(infoPtr
->hwndHeader
, iCol
, (LPARAM
)(&hdi
));
7507 /* determine the width of the text in the header */
7509 old_font
= SelectObject(hdc
, header_font
); /* select the font into hdc */
7511 GetTextExtentPoint32W(hdc
, text_buffer
, lstrlenW(text_buffer
), &size
);
7513 SelectObject(hdc
, old_font
); /* restore the old font */
7514 ReleaseDC(hwnd
, hdc
);
7516 ZeroMemory(&lvItem
, sizeof(lvItem
));
7517 lvItem
.iSubItem
= iCol
;
7518 lvItem
.mask
= LVIF_TEXT
;
7519 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7520 lvItem
.pszText
= szDispText
;
7521 *lvItem
.pszText
= '\0';
7523 for(item_index
= 0; item_index
< GETITEMCOUNT(infoPtr
); item_index
++)
7525 lvItem
.iItem
= item_index
;
7526 LISTVIEW_GetItemT(hwnd
, &lvItem
, FALSE
, TRUE
);
7527 nLabelWidth
= LISTVIEW_GetStringWidthT(hwnd
, lvItem
.pszText
, TRUE
);
7528 nLabelWidth
+= TRAILING_PADDING
;
7529 /* While it is possible for subitems to have icons, even MS messes
7530 up the positioning, so I suspect no applications actually use
7532 if (item_index
== 0 && infoPtr
->himlSmall
)
7533 nLabelWidth
+= infoPtr
->iconSize
.cx
+ IMAGE_PADDING
;
7534 cx
= (nLabelWidth
>cx
)?nLabelWidth
:cx
;
7539 /* call header to update the column change */
7540 hdi
.mask
= HDI_WIDTH
;
7543 lret
= Header_SetItemW(infoPtr
->hwndHeader
, (WPARAM
)iCol
, (LPARAM
)&hdi
);
7545 InvalidateRect(hwnd
, NULL
, TRUE
); /* force redraw of the listview */
7552 * Sets the extended listview style.
7555 * [I] HWND : window handle
7560 * SUCCESS : previous style
7563 static LRESULT
LISTVIEW_SetExtendedListViewStyle(HWND hwnd
, DWORD dwMask
, DWORD dwStyle
)
7565 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
7566 DWORD dwOldStyle
= infoPtr
->dwExStyle
;
7570 infoPtr
->dwExStyle
= (dwOldStyle
& ~dwMask
) | (dwStyle
& dwMask
);
7572 infoPtr
->dwExStyle
= dwStyle
;
7577 /* LISTVIEW_SetHotCursor */
7581 * Sets the hot item index.
7584 * [I] HWND : window handle
7588 * SUCCESS : previous hot item index
7589 * FAILURE : -1 (no hot item)
7591 static LRESULT
LISTVIEW_SetHotItem(HWND hwnd
, INT iIndex
)
7593 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
7594 INT iOldIndex
= infoPtr
->nHotItem
;
7597 infoPtr
->nHotItem
= iIndex
;
7604 * Sets the amount of time the cursor must hover over an item before it is selected.
7607 * [I] HWND : window handle
7608 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7611 * Returns the previous hover time
7613 static LRESULT
LISTVIEW_SetHoverTime(HWND hwnd
, DWORD dwHoverTime
)
7615 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
7616 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
7618 infoPtr
->dwHoverTime
= dwHoverTime
;
7620 return oldHoverTime
;
7625 * Sets spacing for icons of LVS_ICON style.
7628 * [I] HWND : window handle
7629 * [I] DWORD : MAKELONG(cx, cy)
7632 * MAKELONG(oldcx, oldcy)
7634 static LRESULT
LISTVIEW_SetIconSpacing(HWND hwnd
, DWORD spacing
)
7636 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongA(hwnd
, 0);
7637 INT cy
= HIWORD(spacing
);
7638 INT cx
= LOWORD(spacing
);
7640 LONG lStyle
= GetWindowLongA(hwnd
, GWL_STYLE
);
7641 UINT uView
= lStyle
& LVS_TYPEMASK
;
7643 oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
7644 if (cx
== -1) /* set to default */
7645 cx
= GetSystemMetrics(SM_CXICONSPACING
);
7646 if (cy
== -1) /* set to default */
7647 cy
= GetSystemMetrics(SM_CYICONSPACING
);
7650 infoPtr
->iconSpacing
.cx
= cx
;
7652 { /* if 0 then compute width */
7653 if (uView
== LVS_ICON
)
7654 FIXME("width computation not yet done\n");
7656 * Should scan each item and determine max width of
7657 * icon or label, then make that the width
7659 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7660 infoPtr
->iconSpacing
.cx
= LISTVIEW_GetItemWidth(hwnd
);
7663 infoPtr
->iconSpacing
.cy
= cy
;
7665 { /* if 0 then compute height */
7666 if (uView
== LVS_ICON
)
7667 infoPtr
->iconSpacing
.cy
= infoPtr
->iconSize
.cy
+ infoPtr
->ntmHeight
7668 + ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_OFFSET
;
7669 /* FIXME. I don't think so; I think it is based on twice the ntmHeight */
7670 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7671 infoPtr
->iconSpacing
.cy
= LISTVIEW_GetItemHeight(hwnd
);
7674 TRACE("old=(%d,%d), new=(%ld,%ld)\n", LOWORD(oldspacing
), HIWORD(oldspacing
),
7675 infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
7677 /* these depend on the iconSpacing */
7678 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(hwnd
);
7679 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(hwnd
);
7689 * [I] HWND : window handle
7690 * [I] INT : image list type
7691 * [I] HIMAGELIST : image list handle
7694 * SUCCESS : old image list
7697 static HIMAGELIST
LISTVIEW_SetImageList(HWND hwnd
, INT nType
, HIMAGELIST himl
)
7699 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
7700 HIMAGELIST himlOld
= 0;
7706 himlOld
= infoPtr
->himlNormal
;
7707 infoPtr
->himlNormal
= himl
;
7711 himlOld
= infoPtr
->himlSmall
;
7712 infoPtr
->himlSmall
= himl
;
7716 himlOld
= infoPtr
->himlState
;
7717 infoPtr
->himlState
= himl
;
7718 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
7722 oldHeight
= infoPtr
->nItemHeight
;
7723 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(hwnd
);
7724 if (infoPtr
->nItemHeight
!= oldHeight
)
7725 LISTVIEW_UpdateScroll(hwnd
);
7732 * Preallocates memory (does *not* set the actual count of items !)
7735 * [I] HWND : window handle
7736 * [I] INT : item count (projected number of items to allocate)
7737 * [I] DWORD : update flags
7743 static BOOL
LISTVIEW_SetItemCount(HWND hwnd
, INT nItems
, DWORD dwFlags
)
7745 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
7747 TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd
, nItems
, dwFlags
);
7749 if (GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_OWNERDATA
)
7751 int precount
,topvisible
;
7753 TRACE("LVS_OWNERDATA is set!\n");
7754 if (dwFlags
& (LVSICF_NOINVALIDATEALL
| LVSICF_NOSCROLL
))
7755 FIXME("flags %s %s not implemented\n",
7756 (dwFlags
& LVSICF_NOINVALIDATEALL
) ? "LVSICF_NOINVALIDATEALL"
7758 (dwFlags
& LVSICF_NOSCROLL
) ? "LVSICF_NOSCROLL" : "");
7761 * Internally remove all the selections.
7765 LISTVIEW_SELECTION
*selection
;
7766 selection
= DPA_GetPtr(infoPtr
->hdpaSelectionRanges
,0);
7768 LISTVIEW_RemoveSelectionRange(hwnd
,selection
->lower
,
7771 while (infoPtr
->hdpaSelectionRanges
->nItemCount
>0);
7773 precount
= infoPtr
->hdpaItems
->nItemCount
;
7774 topvisible
= ListView_GetTopIndex(hwnd
) +
7775 LISTVIEW_GetCountPerColumn(hwnd
) + 1;
7777 infoPtr
->hdpaItems
->nItemCount
= nItems
;
7779 infoPtr
->nItemWidth
= max(LISTVIEW_GetItemWidth(hwnd
),
7780 DEFAULT_COLUMN_WIDTH
);
7782 LISTVIEW_UpdateSize(hwnd
);
7783 LISTVIEW_UpdateScroll(hwnd
);
7785 if (min(precount
,infoPtr
->hdpaItems
->nItemCount
)<topvisible
)
7786 InvalidateRect(hwnd
, NULL
, TRUE
);
7790 /* According to MSDN for non-LVS_OWNERDATA this is just
7791 * a performance issue. The control allocates its internal
7792 * data structures for the number of items specified. It
7793 * cuts down on the number of memory allocations. Therefore
7794 * we will just issue a WARN here
7796 WARN("for non-ownerdata performance option not implemented.\n");
7804 * Sets the position of an item.
7807 * [I] HWND : window handle
7808 * [I] INT : item index
7809 * [I] LONG : x coordinate
7810 * [I] LONG : y coordinate
7816 static BOOL
LISTVIEW_SetItemPosition(HWND hwnd
, INT nItem
,
7817 LONG nPosX
, LONG nPosY
)
7819 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
7820 UINT lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
7821 UINT uView
= lStyle
& LVS_TYPEMASK
;
7822 LISTVIEW_ITEM
*lpItem
;
7824 BOOL bResult
= FALSE
;
7826 TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd
, nItem
, nPosX
, nPosY
);
7828 if (lStyle
& LVS_OWNERDATA
)
7831 if ((nItem
>= 0) || (nItem
< GETITEMCOUNT(infoPtr
)))
7833 if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
7835 if ( (hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
)) )
7837 if ( (lpItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0)) )
7841 orig
= lpItem
->ptPosition
;
7842 if ((nPosX
== -1) && (nPosY
== -1))
7844 /* This point value seems to be an undocumented feature. The
7845 * best guess is that it means either at the origin, or at
7846 * the true beginning of the list. I will assume the origin.
7849 if (!LISTVIEW_GetOrigin(hwnd
, &pt1
))
7856 if (uView
== LVS_ICON
)
7858 nPosX
+= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2;
7859 nPosY
+= ICON_TOP_PADDING
;
7861 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7865 lpItem
->ptPosition
.x
= nPosX
;
7866 lpItem
->ptPosition
.y
= nPosY
;
7867 if (uView
== LVS_ICON
)
7869 lpItem
->ptPosition
.y
-= ICON_TOP_PADDING
;
7870 lpItem
->ptPosition
.x
-= (infoPtr
->iconSpacing
.cx
- infoPtr
->iconSize
.cx
) / 2;
7871 if ((lpItem
->ptPosition
.y
< 0) || (lpItem
->ptPosition
.x
< 0))
7873 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7874 orig
.x
, orig
.y
, nPosX
, nPosY
, lpItem
->ptPosition
.x
, lpItem
->ptPosition
.y
);
7877 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7878 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7883 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7884 orig
.x
, orig
.y
, nPosX
, nPosY
, lpItem
->ptPosition
.x
, lpItem
->ptPosition
.y
);
7897 * Sets the state of one or many items.
7900 * [I] HWND : window handle
7901 * [I]INT : item index
7902 * [I] LPLVITEM : item or subitem info
7908 static LRESULT
LISTVIEW_SetItemState(HWND hwnd
, INT nItem
, LPLVITEMW lpLVItem
)
7910 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
7911 BOOL bResult
= TRUE
;
7914 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
7915 hwnd
, nItem
, debuglvitem_t(lpLVItem
, TRUE
));
7917 ZeroMemory(&lvItem
, sizeof(lvItem
));
7918 lvItem
.mask
= LVIF_STATE
;
7919 lvItem
.state
= lpLVItem
->state
;
7920 lvItem
.stateMask
= lpLVItem
->stateMask
;
7921 lvItem
.iItem
= nItem
;
7925 /* apply to all items */
7926 for (lvItem
.iItem
= 0; lvItem
.iItem
< GETITEMCOUNT(infoPtr
); lvItem
.iItem
++)
7927 if (!ListView_SetItemW(hwnd
, &lvItem
)) bResult
= FALSE
;
7930 bResult
= ListView_SetItemW(hwnd
, &lvItem
);
7937 * Sets the text of an item or subitem.
7940 * [I] hwnd : window handle
7941 * [I] nItem : item index
7942 * [I] lpLVItem : item or subitem info
7943 * [I] isW : TRUE if input is Unicode
7949 static BOOL
LISTVIEW_SetItemTextT(HWND hwnd
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
7951 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
7952 BOOL bResult
= FALSE
;
7955 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
7956 hwnd
, nItem
, debuglvitem_t(lpLVItem
, isW
), isW
);
7958 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
7960 ZeroMemory(&lvItem
, sizeof(LVITEMW
));
7961 lvItem
.mask
= LVIF_TEXT
;
7962 lvItem
.pszText
= lpLVItem
->pszText
;
7963 lvItem
.iItem
= nItem
;
7964 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
7965 if(isW
) bResult
= ListView_SetItemW(hwnd
, &lvItem
);
7966 else bResult
= ListView_SetItemA(hwnd
, &lvItem
);
7974 * Set item index that marks the start of a multiple selection.
7977 * [I] HWND : window handle
7981 * Index number or -1 if there is no selection mark.
7983 static LRESULT
LISTVIEW_SetSelectionMark(HWND hwnd
, INT nIndex
)
7985 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
7986 INT nOldIndex
= infoPtr
->nSelectionMark
;
7988 TRACE("(hwnd=%x, nIndex=%d)\n", hwnd
, nIndex
);
7990 infoPtr
->nSelectionMark
= nIndex
;
7997 * Sets the text background color.
8000 * [I] HWND : window handle
8001 * [I] COLORREF : text background color
8007 static LRESULT
LISTVIEW_SetTextBkColor(HWND hwnd
, COLORREF clrTextBk
)
8009 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8011 TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd
, clrTextBk
);
8013 infoPtr
->clrTextBk
= clrTextBk
;
8014 InvalidateRect(hwnd
, NULL
, TRUE
);
8021 * Sets the text foreground color.
8024 * [I] HWND : window handle
8025 * [I] COLORREF : text color
8031 static LRESULT
LISTVIEW_SetTextColor (HWND hwnd
, COLORREF clrText
)
8033 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8035 TRACE("(hwnd=%x, clrText=%lx)\n", hwnd
, clrText
);
8037 infoPtr
->clrText
= clrText
;
8038 InvalidateRect(hwnd
, NULL
, TRUE
);
8043 /* LISTVIEW_SetToolTips */
8044 /* LISTVIEW_SetUnicodeFormat */
8045 /* LISTVIEW_SetWorkAreas */
8049 * Callback internally used by LISTVIEW_SortItems()
8052 * [I] LPVOID : first LISTVIEW_ITEM to compare
8053 * [I] LPVOID : second LISTVIEW_ITEM to compare
8054 * [I] LPARAM : HWND of control
8057 * if first comes before second : negative
8058 * if first comes after second : positive
8059 * if first and second are equivalent : zero
8061 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
8063 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW((HWND
)lParam
, 0);
8064 LISTVIEW_ITEM
* lv_first
= (LISTVIEW_ITEM
*) DPA_GetPtr( (HDPA
)first
, 0 );
8065 LISTVIEW_ITEM
* lv_second
= (LISTVIEW_ITEM
*) DPA_GetPtr( (HDPA
)second
, 0 );
8067 /* Forward the call to the client defined callback */
8068 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
8073 * Sorts the listview items.
8076 * [I] HWND : window handle
8077 * [I] WPARAM : application-defined value
8078 * [I] LPARAM : pointer to comparision callback
8084 static LRESULT
LISTVIEW_SortItems(HWND hwnd
, PFNLVCOMPARE pfnCompare
, LPARAM lParamSort
)
8086 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8087 UINT lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
8088 HDPA hdpaSubItems
=NULL
;
8089 LISTVIEW_ITEM
*pLVItem
=NULL
;
8090 LPVOID selectionMarkItem
;
8093 TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd
, pfnCompare
, lParamSort
);
8095 if (lStyle
& LVS_OWNERDATA
) return FALSE
;
8097 if (!infoPtr
|| !infoPtr
->hdpaItems
) return FALSE
;
8099 nCount
= GETITEMCOUNT(infoPtr
);
8100 /* if there are 0 or 1 items, there is no need to sort */
8104 infoPtr
->pfnCompare
= pfnCompare
;
8105 infoPtr
->lParamSort
= lParamSort
;
8106 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, hwnd
);
8108 /* Adjust selections and indices so that they are the way they should
8109 * be after the sort (otherwise, the list items move around, but
8110 * whatever is at the item's previous original position will be
8113 selectionMarkItem
=(infoPtr
->nSelectionMark
>=0)?DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
):NULL
;
8114 for (i
=0; i
< nCount
; i
++)
8116 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
8117 pLVItem
= (LISTVIEW_ITEM
*)DPA_GetPtr(hdpaSubItems
, 0);
8119 if (pLVItem
->state
& LVIS_SELECTED
)
8120 LISTVIEW_AddSelectionRange(hwnd
, i
, i
);
8122 LISTVIEW_RemoveSelectionRange(hwnd
, i
, i
);
8123 if (pLVItem
->state
& LVIS_FOCUSED
)
8124 infoPtr
->nFocusedItem
=i
;
8126 if (selectionMarkItem
!= NULL
)
8127 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
8128 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8130 /* align the items */
8131 LISTVIEW_AlignTop(hwnd
);
8133 /* refresh the display */
8134 InvalidateRect(hwnd
, NULL
, TRUE
);
8139 /* LISTVIEW_SubItemHitTest */
8143 * Updates an items or rearranges the listview control.
8146 * [I] HWND : window handle
8147 * [I] INT : item index
8153 static LRESULT
LISTVIEW_Update(HWND hwnd
, INT nItem
)
8155 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8156 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
8157 BOOL bResult
= FALSE
;
8160 TRACE("(hwnd=%x, nItem=%d)\n", hwnd
, nItem
);
8162 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
8166 /* rearrange with default alignment style */
8167 if ((lStyle
& LVS_AUTOARRANGE
) && (((lStyle
& LVS_TYPEMASK
) == LVS_ICON
) ||
8168 ((lStyle
& LVS_TYPEMASK
) == LVS_SMALLICON
)))
8170 ListView_Arrange(hwnd
, 0);
8174 /* get item bounding rectangle */
8175 ListView_GetItemRect(hwnd
, nItem
, &rc
, LVIR_BOUNDS
);
8176 InvalidateRect(hwnd
, &rc
, TRUE
);
8185 * Creates the listview control.
8188 * [I] HWND : window handle
8193 static LRESULT
LISTVIEW_Create(HWND hwnd
, LPCREATESTRUCTW lpcs
)
8195 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8196 UINT uView
= lpcs
->style
& LVS_TYPEMASK
;
8199 TRACE("(hwnd=%x, lpcs=%p)\n", hwnd
, lpcs
);
8201 /* initialize info pointer */
8202 ZeroMemory(infoPtr
, sizeof(LISTVIEW_INFO
));
8204 /* determine the type of structures to use */
8205 infoPtr
->notifyFormat
= SendMessageW(GetParent(hwnd
), WM_NOTIFYFORMAT
,
8206 (WPARAM
)hwnd
, (LPARAM
)NF_QUERY
);
8208 /* initialize color information */
8209 infoPtr
->clrBk
= comctl32_color
.clrWindow
;
8210 infoPtr
->clrText
= comctl32_color
.clrWindowText
;
8211 infoPtr
->clrTextBk
= CLR_DEFAULT
;
8213 /* set default values */
8214 infoPtr
->hwndSelf
= hwnd
;
8215 infoPtr
->uCallbackMask
= 0;
8216 infoPtr
->nFocusedItem
= -1;
8217 infoPtr
->nSelectionMark
= -1;
8218 infoPtr
->nHotItem
= -1;
8219 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
8220 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
8221 ZeroMemory(&infoPtr
->rcList
, sizeof(RECT
));
8222 infoPtr
->hwndEdit
= 0;
8223 infoPtr
->pedititem
= NULL
;
8224 infoPtr
->nEditLabelItem
= -1;
8225 infoPtr
->bIsDrawing
= FALSE
;
8227 /* get default font (icon title) */
8228 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
8229 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
8230 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
8231 LISTVIEW_SaveTextMetrics(hwnd
);
8234 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, (LPCWSTR
)NULL
,
8235 WS_CHILD
| HDS_HORZ
| (DWORD
)((LVS_NOSORTHEADER
& lpcs
->style
)?0:HDS_BUTTONS
),
8236 0, 0, 0, 0, hwnd
, (HMENU
)0,
8237 lpcs
->hInstance
, NULL
);
8239 /* set header unicode format */
8240 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
,(WPARAM
)TRUE
,(LPARAM
)NULL
);
8242 /* set header font */
8243 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
,
8246 if (uView
== LVS_ICON
)
8248 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXICON
);
8249 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYICON
);
8251 else if (uView
== LVS_REPORT
)
8253 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
))
8255 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
8259 /* set HDS_HIDDEN flag to hide the header bar */
8260 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
,
8261 GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
) | HDS_HIDDEN
);
8265 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
8266 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
8270 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
8271 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
8274 /* display unsupported listview window styles */
8275 LISTVIEW_UnsupportedStyles(lpcs
->style
);
8277 /* allocate memory for the data structure */
8278 infoPtr
->hdpaItems
= DPA_Create(10);
8280 /* allocate memory for the selection ranges */
8281 infoPtr
->hdpaSelectionRanges
= DPA_Create(10);
8283 /* initialize size of items */
8284 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(hwnd
);
8285 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(hwnd
);
8287 /* initialize the hover time to -1(indicating the default system hover time) */
8288 infoPtr
->dwHoverTime
= -1;
8295 * Erases the background of the listview control.
8298 * [I] HWND : window handle
8299 * [I] WPARAM : device context handle
8300 * [I] LPARAM : not used
8306 static LRESULT
LISTVIEW_EraseBackground(HWND hwnd
, WPARAM wParam
,
8309 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8312 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd
, wParam
, lParam
);
8314 if (infoPtr
->clrBk
== CLR_NONE
)
8316 bResult
= SendMessageW(GetParent(hwnd
), WM_ERASEBKGND
, wParam
, lParam
);
8321 HBRUSH hBrush
= CreateSolidBrush(infoPtr
->clrBk
);
8322 GetClientRect(hwnd
, &rc
);
8323 FillRect((HDC
)wParam
, &rc
, hBrush
);
8324 DeleteObject(hBrush
);
8332 static void LISTVIEW_FillBackground(HWND hwnd
, HDC hdc
, LPRECT rc
)
8334 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8336 TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd
, hdc
, rc
);
8338 if (infoPtr
->clrBk
!= CLR_NONE
)
8340 HBRUSH hBrush
= CreateSolidBrush(infoPtr
->clrBk
);
8341 FillRect(hdc
, rc
, hBrush
);
8342 DeleteObject(hBrush
);
8348 * Retrieves the listview control font.
8351 * [I] HWND : window handle
8356 static LRESULT
LISTVIEW_GetFont(HWND hwnd
)
8358 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8360 TRACE("(hwnd=%x)\n", hwnd
);
8362 return infoPtr
->hFont
;
8367 * Performs vertical scrolling.
8370 * [I] HWND : window handle
8371 * [I] INT : scroll code
8372 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8374 * [I] HWND : scrollbar control window handle
8380 * SB_LINEUP/SB_LINEDOWN:
8381 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8382 * for LVS_REPORT is 1 line --> infoPtr->nItemHeight
8383 * for LVS_LIST cannot occur ??? (implemented as LVS_REPORT)
8386 static LRESULT
LISTVIEW_VScroll(HWND hwnd
, INT nScrollCode
, SHORT nCurrentPos
,
8389 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8390 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
8391 SCROLLINFO scrollInfo
;
8394 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8395 hwnd
, nScrollCode
, nCurrentPos
, hScrollWnd
);
8397 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8399 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
8400 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8401 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
;
8403 is_an_icon
= ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
));
8405 if (GetScrollInfo(hwnd
, SB_VERT
, &scrollInfo
) != FALSE
)
8407 INT nOldScrollPos
= scrollInfo
.nPos
;
8408 switch (nScrollCode
)
8410 case SB_INTERNAL_UP
:
8411 if (scrollInfo
.nPos
> scrollInfo
.nMin
)
8415 case SB_INTERNAL_DOWN
:
8416 if (scrollInfo
.nPos
< scrollInfo
.nMax
)
8421 if (scrollInfo
.nPos
> scrollInfo
.nMin
)
8422 scrollInfo
.nPos
-= (is_an_icon
) ?
8423 LISTVIEW_SCROLL_ICON_LINE_SIZE
: infoPtr
->nItemHeight
;
8427 if (scrollInfo
.nPos
< scrollInfo
.nMax
)
8428 scrollInfo
.nPos
+= (is_an_icon
) ?
8429 LISTVIEW_SCROLL_ICON_LINE_SIZE
: infoPtr
->nItemHeight
;
8433 if (scrollInfo
.nPos
> scrollInfo
.nMin
)
8435 if (scrollInfo
.nPos
>= scrollInfo
.nPage
)
8436 scrollInfo
.nPos
-= scrollInfo
.nPage
;
8438 scrollInfo
.nPos
= scrollInfo
.nMin
;
8443 if (scrollInfo
.nPos
< scrollInfo
.nMax
)
8445 if (scrollInfo
.nPos
<= scrollInfo
.nMax
- scrollInfo
.nPage
)
8446 scrollInfo
.nPos
+= scrollInfo
.nPage
;
8448 scrollInfo
.nPos
= scrollInfo
.nMax
;
8452 case SB_THUMBPOSITION
:
8454 scrollInfo
.nPos
= nCurrentPos
;
8455 if (scrollInfo
.nPos
> scrollInfo
.nMax
)
8456 scrollInfo
.nPos
=scrollInfo
.nMax
;
8458 if (scrollInfo
.nPos
< scrollInfo
.nMin
)
8459 scrollInfo
.nPos
=scrollInfo
.nMin
;
8464 if (nOldScrollPos
!= scrollInfo
.nPos
)
8466 scrollInfo
.fMask
= SIF_POS
;
8467 SetScrollInfo(hwnd
, SB_VERT
, &scrollInfo
, TRUE
);
8469 /* Get real position value, the value we set might have been changed
8470 * by SetScrollInfo (especially if we went too far.
8472 scrollInfo
.fMask
= SIF_POS
;
8473 GetScrollInfo(hwnd
, SB_VERT
, &scrollInfo
);
8475 /* only if the scroll position really changed, do we update screen */
8476 if (nOldScrollPos
!= scrollInfo
.nPos
)
8478 if (IsWindowVisible(infoPtr
->hwndHeader
))
8480 RECT rListview
, rcHeader
, rDest
;
8481 GetClientRect(hwnd
, &rListview
);
8482 GetWindowRect(infoPtr
->hwndHeader
, &rcHeader
);
8483 MapWindowPoints((HWND
) NULL
, hwnd
, (LPPOINT
) &rcHeader
, 2);
8484 SubtractRect(&rDest
, &rListview
, &rcHeader
);
8485 InvalidateRect(hwnd
, &rDest
, TRUE
);
8488 InvalidateRect(hwnd
, NULL
, TRUE
);
8498 * Performs horizontal scrolling.
8501 * [I] HWND : window handle
8502 * [I] INT : scroll code
8503 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8505 * [I] HWND : scrollbar control window handle
8511 * SB_LINELEFT/SB_LINERIGHT:
8512 * for LVS_ICON, LVS_SMALLICON ??? (implemented as 1 pixel)
8513 * for LVS_REPORT is 1 pixel
8514 * for LVS_LIST is 1 column --> which is a 1 because the
8515 * scroll is based on columns not pixels
8518 static LRESULT
LISTVIEW_HScroll(HWND hwnd
, INT nScrollCode
, SHORT nCurrentPos
,
8521 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8522 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
8523 SCROLLINFO scrollInfo
;
8526 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8527 hwnd
, nScrollCode
, nCurrentPos
, hScrollWnd
);
8529 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8531 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
8532 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8533 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
;
8535 is_a_list
= (uView
== LVS_LIST
);
8537 if (GetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
) != FALSE
)
8539 INT nOldScrollPos
= scrollInfo
.nPos
;
8541 switch (nScrollCode
)
8543 case SB_INTERNAL_LEFT
:
8544 if (scrollInfo
.nPos
> scrollInfo
.nMin
)
8548 case SB_INTERNAL_RIGHT
:
8549 if (scrollInfo
.nPos
< scrollInfo
.nMax
)
8554 if (scrollInfo
.nPos
> scrollInfo
.nMin
)
8559 if (scrollInfo
.nPos
< scrollInfo
.nMax
)
8564 if (scrollInfo
.nPos
> scrollInfo
.nMin
)
8566 if (scrollInfo
.nPos
>= scrollInfo
.nPage
)
8567 scrollInfo
.nPos
-= scrollInfo
.nPage
;
8569 scrollInfo
.nPos
= scrollInfo
.nMin
;
8574 if (scrollInfo
.nPos
< scrollInfo
.nMax
)
8576 if (scrollInfo
.nPos
<= scrollInfo
.nMax
- scrollInfo
.nPage
)
8577 scrollInfo
.nPos
+= scrollInfo
.nPage
;
8579 scrollInfo
.nPos
= scrollInfo
.nMax
;
8583 case SB_THUMBPOSITION
:
8585 scrollInfo
.nPos
= nCurrentPos
;
8587 if (scrollInfo
.nPos
> scrollInfo
.nMax
)
8588 scrollInfo
.nPos
=scrollInfo
.nMax
;
8590 if (scrollInfo
.nPos
< scrollInfo
.nMin
)
8591 scrollInfo
.nPos
=scrollInfo
.nMin
;
8595 if (nOldScrollPos
!= scrollInfo
.nPos
)
8597 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
8598 scrollInfo
.fMask
= SIF_POS
;
8599 SetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
, TRUE
);
8601 /* Get real position value, the value we set might have been changed
8602 * by SetScrollInfo (especially if we went too far.
8604 scrollInfo
.fMask
= SIF_POS
;
8605 GetScrollInfo(hwnd
, SB_HORZ
, &scrollInfo
);
8606 if(uView
== LVS_REPORT
)
8608 LISTVIEW_UpdateHeaderSize(hwnd
, scrollInfo
.nPos
);
8611 /* only if the scroll position really changed, do we update screen */
8612 if (nOldScrollPos
!= scrollInfo
.nPos
)
8613 InvalidateRect(hwnd
, NULL
, TRUE
);
8620 static LRESULT
LISTVIEW_MouseWheel(HWND hwnd
, INT wheelDelta
)
8622 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
8623 INT gcWheelDelta
= 0;
8624 UINT pulScrollLines
= 3;
8625 SCROLLINFO scrollInfo
;
8627 TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd
, wheelDelta
);
8629 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
8630 gcWheelDelta
-= wheelDelta
;
8632 ZeroMemory(&scrollInfo
, sizeof(SCROLLINFO
));
8633 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8634 scrollInfo
.fMask
= SIF_POS
| SIF_RANGE
;
8641 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8642 * should be fixed in the future.
8644 if (GetScrollInfo(hwnd
, SB_VERT
, &scrollInfo
) != FALSE
)
8645 LISTVIEW_VScroll(hwnd
, SB_THUMBPOSITION
,
8646 scrollInfo
.nPos
+ (gcWheelDelta
< 0) ?
8647 LISTVIEW_SCROLL_ICON_LINE_SIZE
:
8648 -LISTVIEW_SCROLL_ICON_LINE_SIZE
, 0);
8652 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
8654 if (GetScrollInfo(hwnd
, SB_VERT
, &scrollInfo
) != FALSE
)
8656 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(hwnd
), pulScrollLines
);
8657 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
8658 LISTVIEW_VScroll(hwnd
, SB_THUMBPOSITION
, scrollInfo
.nPos
+ cLineScroll
, 0);
8664 LISTVIEW_HScroll(hwnd
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0, 0);
8675 * [I] HWND : window handle
8676 * [I] INT : virtual key
8677 * [I] LONG : key data
8682 static LRESULT
LISTVIEW_KeyDown(HWND hwnd
, INT nVirtualKey
, LONG lKeyData
)
8684 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8685 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
8687 NMLVKEYDOWN nmKeyDown
;
8689 TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd
, nVirtualKey
, lKeyData
);
8691 /* send LVN_KEYDOWN notification */
8692 nmKeyDown
.wVKey
= nVirtualKey
;
8693 nmKeyDown
.flags
= 0;
8694 notify(hwnd
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
8696 switch (nVirtualKey
)
8699 if ((GETITEMCOUNT(infoPtr
) > 0) && (infoPtr
->nFocusedItem
!= -1))
8701 hdr_notify(hwnd
, NM_RETURN
); /* NM_RETURN notification */
8702 hdr_notify(hwnd
, LVN_ITEMACTIVATE
); /* LVN_ITEMACTIVATE notification */
8707 if (GETITEMCOUNT(infoPtr
) > 0)
8712 if (GETITEMCOUNT(infoPtr
) > 0)
8713 nItem
= GETITEMCOUNT(infoPtr
) - 1;
8717 nItem
= ListView_GetNextItem(hwnd
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
8721 nItem
= ListView_GetNextItem(hwnd
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
8725 nItem
= ListView_GetNextItem(hwnd
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
8729 nItem
= ListView_GetNextItem(hwnd
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
8733 if (uView
== LVS_REPORT
)
8734 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(hwnd
);
8736 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(hwnd
)
8737 * LISTVIEW_GetCountPerRow(hwnd
);
8738 if(nItem
< 0) nItem
= 0;
8742 if (uView
== LVS_REPORT
)
8743 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(hwnd
);
8745 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(hwnd
)
8746 * LISTVIEW_GetCountPerRow(hwnd
);
8747 if(nItem
>= GETITEMCOUNT(infoPtr
)) nItem
= GETITEMCOUNT(infoPtr
) - 1;
8751 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
))
8753 if (LISTVIEW_KeySelection(hwnd
, nItem
))
8754 UpdateWindow(hwnd
); /* update client area */
8765 * [I] HWND : window handle
8770 static LRESULT
LISTVIEW_KillFocus(HWND hwnd
)
8772 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8773 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
8776 TRACE("(hwnd=%x)\n", hwnd
);
8778 /* send NM_KILLFOCUS notification */
8779 hdr_notify(hwnd
, NM_KILLFOCUS
);
8781 /* set window focus flag */
8782 infoPtr
->bFocus
= FALSE
;
8784 /* NEED drawing optimization ; redraw the selected items */
8785 if (uView
& LVS_REPORT
)
8787 nTop
= LISTVIEW_GetTopIndex(hwnd
);
8789 LISTVIEW_GetCountPerColumn(hwnd
) + 1;
8794 nBottom
= GETITEMCOUNT(infoPtr
);
8796 for (i
= nTop
; i
<nBottom
; i
++)
8798 if (LISTVIEW_IsSelected(hwnd
,i
))
8801 rcItem
.left
= LVIR_BOUNDS
;
8802 LISTVIEW_GetItemRect(hwnd
, i
, &rcItem
);
8803 InvalidateRect(hwnd
, &rcItem
, FALSE
);
8812 * Processes double click messages (left mouse button).
8815 * [I] HWND : window handle
8816 * [I] WORD : key flag
8817 * [I] WORD : x coordinate
8818 * [I] WORD : y coordinate
8823 static LRESULT
LISTVIEW_LButtonDblClk(HWND hwnd
, WORD wKey
, WORD wPosX
,
8826 LVHITTESTINFO htInfo
;
8829 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd
, wKey
, wPosX
, wPosY
);
8831 htInfo
.pt
.x
= wPosX
;
8832 htInfo
.pt
.y
= wPosY
;
8834 /* send NM_DBLCLK notification */
8835 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
8836 if (LISTVIEW_HitTestItem(hwnd
, &htInfo
, TRUE
) != -1)
8838 nmlv
.iItem
= htInfo
.iItem
;
8839 nmlv
.iSubItem
= htInfo
.iSubItem
;
8846 nmlv
.ptAction
.x
= wPosX
;
8847 nmlv
.ptAction
.y
= wPosY
;
8848 listview_notify(hwnd
, NM_DBLCLK
, &nmlv
);
8851 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8852 if(nmlv
.iItem
!= -1)
8853 hdr_notify(hwnd
, LVN_ITEMACTIVATE
);
8860 * Processes mouse down messages (left mouse button).
8863 * [I] HWND : window handle
8864 * [I] WORD : key flag
8865 * [I] WORD : x coordinate
8866 * [I] WORD : y coordinate
8871 static LRESULT
LISTVIEW_LButtonDown(HWND hwnd
, WORD wKey
, WORD wPosX
,
8874 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8875 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
8876 static BOOL bGroupSelect
= TRUE
;
8880 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd
, wKey
, wPosX
, wPosY
);
8882 /* send NM_RELEASEDCAPTURE notification */
8883 hdr_notify(hwnd
, NM_RELEASEDCAPTURE
);
8885 if (infoPtr
->bFocus
== FALSE
)
8888 /* set left button down flag */
8889 infoPtr
->bLButtonDown
= TRUE
;
8891 ptPosition
.x
= wPosX
;
8892 ptPosition
.y
= wPosY
;
8893 nItem
= LISTVIEW_MouseSelection(hwnd
, ptPosition
);
8894 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
8896 if (lStyle
& LVS_SINGLESEL
)
8898 if ((ListView_GetItemState(hwnd
, nItem
, LVIS_SELECTED
) & LVIS_SELECTED
)
8899 && infoPtr
->nEditLabelItem
== -1)
8900 infoPtr
->nEditLabelItem
= nItem
;
8902 LISTVIEW_SetSelection(hwnd
, nItem
);
8906 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
8909 LISTVIEW_AddGroupSelection(hwnd
, nItem
);
8911 LISTVIEW_AddSelection(hwnd
, nItem
);
8913 else if (wKey
& MK_CONTROL
)
8915 bGroupSelect
= LISTVIEW_ToggleSelection(hwnd
, nItem
);
8917 else if (wKey
& MK_SHIFT
)
8919 LISTVIEW_SetGroupSelection(hwnd
, nItem
);
8924 (ListView_GetItemState(hwnd
, nItem
, LVIS_SELECTED
) & LVIS_SELECTED
);
8926 /* set selection (clears other pre-existing selections) */
8927 LISTVIEW_SetSelection(hwnd
, nItem
);
8929 if (was_selected
&& infoPtr
->nEditLabelItem
== -1)
8930 infoPtr
->nEditLabelItem
= nItem
;
8936 /* remove all selections */
8937 LISTVIEW_RemoveAllSelections(hwnd
);
8940 /* redraw if we could have possibly selected something */
8941 if(!GETITEMCOUNT(infoPtr
)) InvalidateRect(hwnd
, NULL
, TRUE
);
8948 * Processes mouse up messages (left mouse button).
8951 * [I] HWND : window handle
8952 * [I] WORD : key flag
8953 * [I] WORD : x coordinate
8954 * [I] WORD : y coordinate
8959 static LRESULT
LISTVIEW_LButtonUp(HWND hwnd
, WORD wKey
, WORD wPosX
,
8962 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
8964 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd
, wKey
, wPosX
, wPosY
);
8966 if (infoPtr
->bLButtonDown
!= FALSE
)
8968 LVHITTESTINFO lvHitTestInfo
;
8971 lvHitTestInfo
.pt
.x
= wPosX
;
8972 lvHitTestInfo
.pt
.y
= wPosY
;
8974 /* send NM_CLICK notification */
8975 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
8976 if (LISTVIEW_HitTestItem(hwnd
, &lvHitTestInfo
, TRUE
) != -1)
8978 nmlv
.iItem
= lvHitTestInfo
.iItem
;
8979 nmlv
.iSubItem
= lvHitTestInfo
.iSubItem
;
8986 nmlv
.ptAction
.x
= wPosX
;
8987 nmlv
.ptAction
.y
= wPosY
;
8988 listview_notify(hwnd
, NM_CLICK
, &nmlv
);
8990 /* set left button flag */
8991 infoPtr
->bLButtonDown
= FALSE
;
8993 if(infoPtr
->nEditLabelItem
!= -1)
8995 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
)
8996 LISTVIEW_EditLabelT(hwnd
, lvHitTestInfo
.iItem
, TRUE
);
8997 infoPtr
->nEditLabelItem
= -1;
9006 * Creates the listview control (called before WM_CREATE).
9009 * [I] HWND : window handle
9010 * [I] WPARAM : unhandled
9011 * [I] LPARAM : widow creation info
9016 static LRESULT
LISTVIEW_NCCreate(HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
9018 LISTVIEW_INFO
*infoPtr
;
9020 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd
, wParam
, lParam
);
9022 /* allocate memory for info structure */
9023 infoPtr
= (LISTVIEW_INFO
*)COMCTL32_Alloc(sizeof(LISTVIEW_INFO
));
9024 if (infoPtr
== NULL
)
9026 ERR("could not allocate info memory!\n");
9030 SetWindowLongW(hwnd
, 0, (LONG
)infoPtr
);
9031 if ((LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0) != infoPtr
)
9033 ERR("pointer assignment error!\n");
9037 return DefWindowProcW(hwnd
, WM_NCCREATE
, wParam
, lParam
);
9042 * Destroys the listview control (called after WM_DESTROY).
9045 * [I] HWND : window handle
9050 static LRESULT
LISTVIEW_NCDestroy(HWND hwnd
)
9052 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
9053 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
9055 TRACE("(hwnd=%x)\n", hwnd
);
9057 /* delete all items */
9058 LISTVIEW_DeleteAllItems(hwnd
);
9060 /* destroy data structure */
9061 DPA_Destroy(infoPtr
->hdpaItems
);
9062 DPA_Destroy(infoPtr
->hdpaSelectionRanges
);
9064 /* destroy image lists */
9065 if (!(lStyle
& LVS_SHAREIMAGELISTS
))
9068 /* FIXME: If the caller does a ImageList_Destroy and then we
9069 * do this code the area will be freed twice. Currently
9070 * this generates an "err:heap:HEAP_ValidateInUseArena
9071 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
9072 * has PREV_FREE flag" sometimes.
9074 * We will leak the memory till we figure out how to fix
9076 if (infoPtr
->himlNormal
)
9077 ImageList_Destroy(infoPtr
->himlNormal
);
9078 if (infoPtr
->himlSmall
)
9079 ImageList_Destroy(infoPtr
->himlSmall
);
9080 if (infoPtr
->himlState
)
9081 ImageList_Destroy(infoPtr
->himlState
);
9086 infoPtr
->hFont
= (HFONT
)0;
9087 if (infoPtr
->hDefaultFont
)
9089 DeleteObject(infoPtr
->hDefaultFont
);
9092 /* free listview info pointer*/
9093 COMCTL32_Free(infoPtr
);
9095 SetWindowLongW(hwnd
, 0, 0);
9101 * Handles notifications from children.
9104 * [I] HWND : window handle
9105 * [I] INT : control identifier
9106 * [I] LPNMHDR : notification information
9111 static LRESULT
LISTVIEW_Notify(HWND hwnd
, INT nCtrlId
, LPNMHDR lpnmh
)
9113 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
9115 TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd
, nCtrlId
, lpnmh
);
9117 if (lpnmh
->hwndFrom
== infoPtr
->hwndHeader
)
9119 /* handle notification from header control */
9120 if (lpnmh
->code
== HDN_ENDTRACKW
)
9122 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(hwnd
);
9123 InvalidateRect(hwnd
, NULL
, TRUE
);
9125 else if(lpnmh
->code
== HDN_ITEMCLICKW
|| lpnmh
->code
== HDN_ITEMCLICKA
)
9127 /* Handle sorting by Header Column */
9130 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
9132 nmlv
.iSubItem
= ((LPNMHEADERW
)lpnmh
)->iItem
;
9133 listview_notify(hwnd
, LVN_COLUMNCLICK
, &nmlv
);
9135 else if(lpnmh
->code
== NM_RELEASEDCAPTURE
)
9137 /* Idealy this should be done in HDN_ENDTRACKA
9138 * but since SetItemBounds in Header.c is called after
9139 * the notification is sent, it is neccessary to handle the
9140 * update of the scroll bar here (Header.c works fine as it is,
9141 * no need to disturb it)
9143 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(hwnd
);
9144 LISTVIEW_UpdateScroll(hwnd
);
9145 InvalidateRect(hwnd
, NULL
, TRUE
);
9155 * Determines the type of structure to use.
9158 * [I] HWND : window handle of the sender
9159 * [I] HWND : listview window handle
9160 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
9165 static LRESULT
LISTVIEW_NotifyFormat(HWND hwndFrom
, HWND hwnd
, INT nCommand
)
9167 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
9169 TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom
, hwnd
, nCommand
);
9171 if (nCommand
== NF_REQUERY
)
9172 infoPtr
->notifyFormat
= SendMessageW(hwndFrom
, WM_NOTIFYFORMAT
,
9173 (WPARAM
)hwnd
, (LPARAM
)NF_QUERY
);
9179 * Paints/Repaints the listview control.
9182 * [I] HWND : window handle
9183 * [I] HDC : device context handle
9188 static LRESULT
LISTVIEW_Paint(HWND hwnd
, HDC hdc
)
9192 TRACE("(hwnd=%x, hdc=%x)\n", hwnd
, hdc
);
9196 hdc
= BeginPaint(hwnd
, &ps
);
9197 LISTVIEW_Refresh(hwnd
, hdc
);
9198 EndPaint(hwnd
, &ps
);
9202 LISTVIEW_Refresh(hwnd
, hdc
);
9210 * Processes double click messages (right mouse button).
9213 * [I] HWND : window handle
9214 * [I] WORD : key flag
9215 * [I] WORD : x coordinate
9216 * [I] WORD : y coordinate
9221 static LRESULT
LISTVIEW_RButtonDblClk(HWND hwnd
, WORD wKey
, WORD wPosX
,
9224 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd
, wKey
, wPosX
, wPosY
);
9226 /* send NM_RELEASEDCAPTURE notification */
9227 hdr_notify(hwnd
, NM_RELEASEDCAPTURE
);
9229 /* send NM_RDBLCLK notification */
9230 hdr_notify(hwnd
, NM_RDBLCLK
);
9237 * Processes mouse down messages (right mouse button).
9240 * [I] HWND : window handle
9241 * [I] WORD : key flag
9242 * [I] WORD : x coordinate
9243 * [I] WORD : y coordinate
9248 static LRESULT
LISTVIEW_RButtonDown(HWND hwnd
, WORD wKey
, WORD wPosX
,
9251 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
9255 LVHITTESTINFO lvHitTestInfo
;
9257 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd
, wKey
, wPosX
, wPosY
);
9259 /* send NM_RELEASEDCAPTURE notification */
9260 hdr_notify(hwnd
, NM_RELEASEDCAPTURE
);
9262 /* make sure the listview control window has the focus */
9263 if (infoPtr
->bFocus
== FALSE
)
9266 /* set right button down flag */
9267 infoPtr
->bRButtonDown
= TRUE
;
9269 /* determine the index of the selected item */
9270 ptPosition
.x
= wPosX
;
9271 ptPosition
.y
= wPosY
;
9272 nItem
= LISTVIEW_MouseSelection(hwnd
, ptPosition
);
9273 if ((nItem
>= 0) && (nItem
< GETITEMCOUNT(infoPtr
)))
9275 LISTVIEW_SetItemFocus(hwnd
,nItem
);
9276 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
9277 !LISTVIEW_IsSelected(hwnd
,nItem
))
9278 LISTVIEW_SetSelection(hwnd
, nItem
);
9282 LISTVIEW_RemoveAllSelections(hwnd
);
9285 lvHitTestInfo
.pt
.x
= wPosX
;
9286 lvHitTestInfo
.pt
.y
= wPosY
;
9288 /* Send NM_RClICK notification */
9289 ZeroMemory(&nmlv
, sizeof(nmlv
));
9290 if (LISTVIEW_HitTestItem(hwnd
, &lvHitTestInfo
, TRUE
) != -1)
9292 nmlv
.iItem
= lvHitTestInfo
.iItem
;
9293 nmlv
.iSubItem
= lvHitTestInfo
.iSubItem
;
9300 nmlv
.ptAction
.x
= wPosX
;
9301 nmlv
.ptAction
.y
= wPosY
;
9302 listview_notify(hwnd
, NM_RCLICK
, &nmlv
);
9309 * Processes mouse up messages (right mouse button).
9312 * [I] HWND : window handle
9313 * [I] WORD : key flag
9314 * [I] WORD : x coordinate
9315 * [I] WORD : y coordinate
9320 static LRESULT
LISTVIEW_RButtonUp(HWND hwnd
, WORD wKey
, WORD wPosX
,
9323 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
9325 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd
, wKey
, wPosX
, wPosY
);
9327 if (infoPtr
->bRButtonDown
)
9334 /* set button flag */
9335 infoPtr
->bRButtonDown
= FALSE
;
9337 /* Change to screen coordinate for WM_CONTEXTMENU */
9338 ClientToScreen(hwnd
, &pt
);
9340 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9341 SendMessageW( hwnd
, WM_CONTEXTMENU
, (WPARAM
) hwnd
, MAKELPARAM(pt
.x
, pt
.y
));
9352 * [I] HWND : window handle
9353 * [I] HWND : window handle of previously focused window
9358 static LRESULT
LISTVIEW_SetFocus(HWND hwnd
, HWND hwndLoseFocus
)
9360 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
9362 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd
, hwndLoseFocus
);
9364 /* send NM_SETFOCUS notification */
9365 hdr_notify(hwnd
, NM_SETFOCUS
);
9367 /* set window focus flag */
9368 infoPtr
->bFocus
= TRUE
;
9380 * [I] HWND : window handle
9381 * [I] HFONT : font handle
9382 * [I] WORD : redraw flag
9387 static LRESULT
LISTVIEW_SetFont(HWND hwnd
, HFONT hFont
, WORD fRedraw
)
9389 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
9390 UINT uView
= GetWindowLongW(hwnd
, GWL_STYLE
) & LVS_TYPEMASK
;
9392 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd
, hFont
, fRedraw
);
9394 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
9395 LISTVIEW_SaveTextMetrics(hwnd
);
9397 if (uView
== LVS_REPORT
)
9399 /* set header font */
9400 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
,
9401 MAKELPARAM(fRedraw
, 0));
9404 /* invalidate listview control client area */
9405 InvalidateRect(hwnd
, NULL
, TRUE
);
9407 if (fRedraw
!= FALSE
)
9415 * Message handling for WM_SETREDRAW.
9416 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9419 * [I] HWND : window handle
9420 * [I] bRedraw: state of redraw flag
9423 * DefWinProc return value
9425 static LRESULT
LISTVIEW_SetRedraw(HWND hwnd
, BOOL bRedraw
)
9427 LRESULT lResult
= DefWindowProcW(hwnd
, WM_SETREDRAW
, bRedraw
, 0);
9429 RedrawWindow(hwnd
, NULL
, 0,
9430 RDW_INVALIDATE
| RDW_FRAME
| RDW_ERASE
| RDW_ALLCHILDREN
| RDW_ERASENOW
);
9436 * Resizes the listview control. This function processes WM_SIZE
9437 * messages. At this time, the width and height are not used.
9440 * [I] HWND : window handle
9441 * [I] WORD : new width
9442 * [I] WORD : new height
9447 static LRESULT
LISTVIEW_Size(HWND hwnd
, int Width
, int Height
)
9449 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
9450 UINT uView
= lStyle
& LVS_TYPEMASK
;
9452 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd
, Width
, Height
);
9454 if (LISTVIEW_UpdateSize(hwnd
))
9456 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
9458 if (lStyle
& LVS_ALIGNLEFT
)
9459 LISTVIEW_AlignLeft(hwnd
);
9461 LISTVIEW_AlignTop(hwnd
);
9464 LISTVIEW_UpdateScroll(hwnd
);
9466 /* invalidate client area + erase background */
9467 InvalidateRect(hwnd
, NULL
, TRUE
);
9475 * Sets the size information.
9478 * [I] HWND : window handle
9481 * Zero if no size change
9484 static BOOL
LISTVIEW_UpdateSize(HWND hwnd
)
9486 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
9487 LONG lStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
9488 UINT uView
= lStyle
& LVS_TYPEMASK
;
9492 TRACE("(hwnd=%x)\n", hwnd
);
9494 GetClientRect(hwnd
, &rcList
);
9495 CopyRect(&rcOld
,&(infoPtr
->rcList
));
9496 infoPtr
->rcList
.left
= 0;
9497 infoPtr
->rcList
.right
= max(rcList
.right
- rcList
.left
, 1);
9498 infoPtr
->rcList
.top
= 0;
9499 infoPtr
->rcList
.bottom
= max(rcList
.bottom
- rcList
.top
, 1);
9501 if (uView
== LVS_LIST
)
9503 /* Apparently the "LIST" style is supposed to have the same
9504 * number of items in a column even if there is no scroll bar.
9505 * Since if a scroll bar already exists then the bottom is already
9506 * reduced, only reduce if the scroll bar does not currently exist.
9507 * The "2" is there to mimic the native control. I think it may be
9508 * related to either padding or edges. (GLA 7/2002)
9510 if (!(lStyle
& WS_HSCROLL
))
9512 INT nHScrollHeight
= GetSystemMetrics(SM_CYHSCROLL
);
9513 if (infoPtr
->rcList
.bottom
> nHScrollHeight
)
9514 infoPtr
->rcList
.bottom
-= (nHScrollHeight
+ 2);
9518 if (infoPtr
->rcList
.bottom
> 2)
9519 infoPtr
->rcList
.bottom
-= 2;
9522 else if (uView
== LVS_REPORT
)
9529 Header_Layout(infoPtr
->hwndHeader
, &hl
);
9531 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
9533 if (!(LVS_NOCOLUMNHEADER
& lStyle
))
9534 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
9536 return (EqualRect(&rcOld
,&(infoPtr
->rcList
)));
9541 * Processes WM_STYLECHANGED messages.
9544 * [I] HWND : window handle
9545 * [I] WPARAM : window style type (normal or extended)
9546 * [I] LPSTYLESTRUCT : window style information
9551 static INT
LISTVIEW_StyleChanged(HWND hwnd
, WPARAM wStyleType
,
9554 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
9555 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
9556 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
9557 RECT rcList
= infoPtr
->rcList
;
9559 TRACE("(hwnd=%x, styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
9560 hwnd
, wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
9562 if (wStyleType
== GWL_STYLE
)
9564 if (uOldView
== LVS_REPORT
)
9565 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
9567 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
9568 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
9569 ShowScrollBar(hwnd
, SB_HORZ
, FALSE
);
9571 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
9572 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
9573 ShowScrollBar(hwnd
, SB_VERT
, FALSE
);
9575 /* If switching modes, then start with no scroll bars and then
9578 if (uNewView
!= uOldView
)
9579 ShowScrollBar(hwnd
, SB_BOTH
, FALSE
);
9581 if (uNewView
== LVS_ICON
)
9583 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXICON
);
9584 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYICON
);
9585 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(hwnd
);
9586 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(hwnd
);
9587 if (lpss
->styleNew
& LVS_ALIGNLEFT
)
9588 LISTVIEW_AlignLeft(hwnd
);
9590 LISTVIEW_AlignTop(hwnd
);
9592 else if (uNewView
== LVS_REPORT
)
9599 Header_Layout(infoPtr
->hwndHeader
, &hl
);
9600 SetWindowPos(infoPtr
->hwndHeader
, hwnd
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9602 if (!(LVS_NOCOLUMNHEADER
& lpss
->styleNew
))
9603 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
9605 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
9606 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
9607 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(hwnd
);
9608 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(hwnd
);
9610 else if (uNewView
== LVS_LIST
)
9612 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
9613 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
9614 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(hwnd
);
9615 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(hwnd
);
9619 infoPtr
->iconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
9620 infoPtr
->iconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
9621 infoPtr
->nItemWidth
= LISTVIEW_GetItemWidth(hwnd
);
9622 infoPtr
->nItemHeight
= LISTVIEW_GetItemHeight(hwnd
);
9623 if (lpss
->styleNew
& LVS_ALIGNLEFT
)
9624 LISTVIEW_AlignLeft(hwnd
);
9626 LISTVIEW_AlignTop(hwnd
);
9629 /* update the size of the client area */
9630 LISTVIEW_UpdateSize(hwnd
);
9632 /* add scrollbars if needed */
9633 LISTVIEW_UpdateScroll(hwnd
);
9635 /* invalidate client area + erase background */
9636 InvalidateRect(hwnd
, NULL
, TRUE
);
9638 /* print the list of unsupported window styles */
9639 LISTVIEW_UnsupportedStyles(lpss
->styleNew
);
9642 /* If they change the view and we have an active edit control
9643 we will need to kill the control since the redraw will
9644 misplace the edit control.
9646 if (infoPtr
->hwndEdit
&&
9647 ((uNewView
& (LVS_ICON
|LVS_LIST
|LVS_SMALLICON
)) !=
9648 ((LVS_ICON
|LVS_LIST
|LVS_SMALLICON
) & uOldView
)))
9650 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9658 * Window procedure of the listview control.
9661 static LRESULT WINAPI
LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
,
9664 TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd
, uMsg
, wParam
, lParam
);
9665 if (!GetWindowLongW(hwnd
, 0) && (uMsg
!= WM_NCCREATE
))
9666 return DefWindowProcW( hwnd
, uMsg
, wParam
, lParam
);
9669 case LVM_APPROXIMATEVIEWRECT
:
9670 return LISTVIEW_ApproximateViewRect(hwnd
, (INT
)wParam
,
9671 LOWORD(lParam
), HIWORD(lParam
));
9673 return LISTVIEW_Arrange(hwnd
, (INT
)wParam
);
9675 /* case LVM_CREATEDRAGIMAGE: */
9677 case LVM_DELETEALLITEMS
:
9678 return LISTVIEW_DeleteAllItems(hwnd
);
9680 case LVM_DELETECOLUMN
:
9681 return LISTVIEW_DeleteColumn(hwnd
, (INT
)wParam
);
9683 case LVM_DELETEITEM
:
9684 return LISTVIEW_DeleteItem(hwnd
, (INT
)wParam
);
9686 case LVM_EDITLABELW
:
9687 return LISTVIEW_EditLabelT(hwnd
, (INT
)wParam
, TRUE
);
9689 case LVM_EDITLABELA
:
9690 return LISTVIEW_EditLabelT(hwnd
, (INT
)wParam
, FALSE
);
9692 case LVM_ENSUREVISIBLE
:
9693 return LISTVIEW_EnsureVisible(hwnd
, (INT
)wParam
, (BOOL
)lParam
);
9696 return LISTVIEW_FindItemW(hwnd
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
9699 return LISTVIEW_FindItemA(hwnd
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
9701 case LVM_GETBKCOLOR
:
9702 return LISTVIEW_GetBkColor(hwnd
);
9704 /* case LVM_GETBKIMAGE: */
9706 case LVM_GETCALLBACKMASK
:
9707 return LISTVIEW_GetCallbackMask(hwnd
);
9709 case LVM_GETCOLUMNA
:
9710 return LISTVIEW_GetColumnT(hwnd
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9712 case LVM_GETCOLUMNW
:
9713 return LISTVIEW_GetColumnT(hwnd
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9715 case LVM_GETCOLUMNORDERARRAY
:
9716 return LISTVIEW_GetColumnOrderArray(hwnd
, (INT
)wParam
, (LPINT
)lParam
);
9718 case LVM_GETCOLUMNWIDTH
:
9719 return LISTVIEW_GetColumnWidth(hwnd
, (INT
)wParam
);
9721 case LVM_GETCOUNTPERPAGE
:
9722 return LISTVIEW_GetCountPerPage(hwnd
);
9724 case LVM_GETEDITCONTROL
:
9725 return LISTVIEW_GetEditControl(hwnd
);
9727 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
9728 return LISTVIEW_GetExtendedListViewStyle(hwnd
);
9731 return LISTVIEW_GetHeader(hwnd
);
9733 case LVM_GETHOTCURSOR
:
9734 FIXME("LVM_GETHOTCURSOR: unimplemented\n");
9737 case LVM_GETHOTITEM
:
9738 return LISTVIEW_GetHotItem(hwnd
);
9740 case LVM_GETHOVERTIME
:
9741 return LISTVIEW_GetHoverTime(hwnd
);
9743 case LVM_GETIMAGELIST
:
9744 return LISTVIEW_GetImageList(hwnd
, (INT
)wParam
);
9746 case LVM_GETISEARCHSTRINGA
:
9747 case LVM_GETISEARCHSTRINGW
:
9748 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9752 return LISTVIEW_GetItemT(hwnd
, (LPLVITEMW
)lParam
, FALSE
, FALSE
);
9755 return LISTVIEW_GetItemT(hwnd
, (LPLVITEMW
)lParam
, FALSE
, TRUE
);
9757 case LVM_GETITEMCOUNT
:
9758 return LISTVIEW_GetItemCount(hwnd
);
9760 case LVM_GETITEMPOSITION
:
9761 return LISTVIEW_GetItemPosition(hwnd
, (INT
)wParam
, (LPPOINT
)lParam
);
9763 case LVM_GETITEMRECT
:
9764 return LISTVIEW_GetItemRect(hwnd
, (INT
)wParam
, (LPRECT
)lParam
);
9766 case LVM_GETITEMSPACING
:
9767 return LISTVIEW_GetItemSpacing(hwnd
, (BOOL
)wParam
);
9769 case LVM_GETITEMSTATE
:
9770 return LISTVIEW_GetItemState(hwnd
, (INT
)wParam
, (UINT
)lParam
);
9772 case LVM_GETITEMTEXTA
:
9773 return LISTVIEW_GetItemTextT(hwnd
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9775 case LVM_GETITEMTEXTW
:
9776 return LISTVIEW_GetItemTextT(hwnd
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9778 case LVM_GETNEXTITEM
:
9779 return LISTVIEW_GetNextItem(hwnd
, (INT
)wParam
, LOWORD(lParam
));
9781 case LVM_GETNUMBEROFWORKAREAS
:
9782 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9786 return LISTVIEW_GetOrigin(hwnd
, (LPPOINT
)lParam
);
9788 case LVM_GETSELECTEDCOUNT
:
9789 return LISTVIEW_GetSelectedCount(hwnd
);
9791 case LVM_GETSELECTIONMARK
:
9792 return LISTVIEW_GetSelectionMark(hwnd
);
9794 case LVM_GETSTRINGWIDTHA
:
9795 return LISTVIEW_GetStringWidthT(hwnd
, (LPCWSTR
)lParam
, FALSE
);
9797 case LVM_GETSTRINGWIDTHW
:
9798 return LISTVIEW_GetStringWidthT(hwnd
, (LPCWSTR
)lParam
, TRUE
);
9800 case LVM_GETSUBITEMRECT
:
9801 return LISTVIEW_GetSubItemRect(hwnd
, (UINT
)wParam
, ((LPRECT
)lParam
)->top
,
9802 ((LPRECT
)lParam
)->left
, (LPRECT
)lParam
);
9804 case LVM_GETTEXTBKCOLOR
:
9805 return LISTVIEW_GetTextBkColor(hwnd
);
9807 case LVM_GETTEXTCOLOR
:
9808 return LISTVIEW_GetTextColor(hwnd
);
9810 case LVM_GETTOOLTIPS
:
9811 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9814 case LVM_GETTOPINDEX
:
9815 return LISTVIEW_GetTopIndex(hwnd
);
9817 /*case LVM_GETUNICODEFORMAT:
9818 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9821 case LVM_GETVIEWRECT
:
9822 return LISTVIEW_GetViewRect(hwnd
, (LPRECT
)lParam
);
9824 case LVM_GETWORKAREAS
:
9825 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9829 return LISTVIEW_HitTest(hwnd
, (LPLVHITTESTINFO
)lParam
);
9831 case LVM_INSERTCOLUMNA
:
9832 return LISTVIEW_InsertColumnT(hwnd
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9834 case LVM_INSERTCOLUMNW
:
9835 return LISTVIEW_InsertColumnT(hwnd
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9837 case LVM_INSERTITEMA
:
9838 return LISTVIEW_InsertItemT(hwnd
, (LPLVITEMW
)lParam
, FALSE
);
9840 case LVM_INSERTITEMW
:
9841 return LISTVIEW_InsertItemT(hwnd
, (LPLVITEMW
)lParam
, TRUE
);
9843 case LVM_REDRAWITEMS
:
9844 return LISTVIEW_RedrawItems(hwnd
, (INT
)wParam
, (INT
)lParam
);
9847 return LISTVIEW_Scroll(hwnd
, (INT
)wParam
, (INT
)lParam
);
9849 case LVM_SETBKCOLOR
:
9850 return LISTVIEW_SetBkColor(hwnd
, (COLORREF
)lParam
);
9852 /* case LVM_SETBKIMAGE: */
9854 case LVM_SETCALLBACKMASK
:
9855 return LISTVIEW_SetCallbackMask(hwnd
, (UINT
)wParam
);
9857 case LVM_SETCOLUMNA
:
9858 return LISTVIEW_SetColumnT(hwnd
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9860 case LVM_SETCOLUMNW
:
9861 return LISTVIEW_SetColumnT(hwnd
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9863 case LVM_SETCOLUMNORDERARRAY
:
9864 return LISTVIEW_SetColumnOrderArray(hwnd
, (INT
)wParam
, (LPINT
)lParam
);
9866 case LVM_SETCOLUMNWIDTH
:
9867 return LISTVIEW_SetColumnWidth(hwnd
, (INT
)wParam
, SLOWORD(lParam
));
9869 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
9870 return LISTVIEW_SetExtendedListViewStyle(hwnd
, (DWORD
)wParam
, (DWORD
)lParam
);
9872 /* case LVM_SETHOTCURSOR: */
9874 case LVM_SETHOTITEM
:
9875 return LISTVIEW_SetHotItem(hwnd
, (INT
)wParam
);
9877 case LVM_SETHOVERTIME
:
9878 return LISTVIEW_SetHoverTime(hwnd
, (DWORD
)wParam
);
9880 case LVM_SETICONSPACING
:
9881 return LISTVIEW_SetIconSpacing(hwnd
, (DWORD
)lParam
);
9883 case LVM_SETIMAGELIST
:
9884 return (LRESULT
)LISTVIEW_SetImageList(hwnd
, (INT
)wParam
, (HIMAGELIST
)lParam
);
9887 return LISTVIEW_SetItemT(hwnd
, (LPLVITEMW
)lParam
, FALSE
);
9890 return LISTVIEW_SetItemT(hwnd
, (LPLVITEMW
)lParam
, TRUE
);
9892 case LVM_SETITEMCOUNT
:
9893 return LISTVIEW_SetItemCount(hwnd
, (INT
)wParam
, (DWORD
)lParam
);
9895 case LVM_SETITEMPOSITION
:
9896 return LISTVIEW_SetItemPosition(hwnd
, (INT
)wParam
, (INT
)LOWORD(lParam
),
9897 (INT
)HIWORD(lParam
));
9899 case LVM_SETITEMPOSITION32
:
9900 return LISTVIEW_SetItemPosition(hwnd
, (INT
)wParam
, ((POINT
*)lParam
)->x
,
9901 ((POINT
*)lParam
)->y
);
9903 case LVM_SETITEMSTATE
:
9904 return LISTVIEW_SetItemState(hwnd
, (INT
)wParam
, (LPLVITEMW
)lParam
);
9906 case LVM_SETITEMTEXTA
:
9907 return LISTVIEW_SetItemTextT(hwnd
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9909 case LVM_SETITEMTEXTW
:
9910 return LISTVIEW_SetItemTextT(hwnd
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9912 case LVM_SETSELECTIONMARK
:
9913 return LISTVIEW_SetSelectionMark(hwnd
, (INT
)lParam
);
9915 case LVM_SETTEXTBKCOLOR
:
9916 return LISTVIEW_SetTextBkColor(hwnd
, (COLORREF
)lParam
);
9918 case LVM_SETTEXTCOLOR
:
9919 return LISTVIEW_SetTextColor(hwnd
, (COLORREF
)lParam
);
9921 /* case LVM_SETTOOLTIPS: */
9922 /* case LVM_SETUNICODEFORMAT: */
9923 /* case LVM_SETWORKAREAS: */
9926 return LISTVIEW_SortItems(hwnd
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
);
9928 case LVM_SUBITEMHITTEST
:
9929 return LISTVIEW_SubItemHitTest(hwnd
, (LPLVHITTESTINFO
)lParam
);
9932 return LISTVIEW_Update(hwnd
, (INT
)wParam
);
9935 return LISTVIEW_ProcessLetterKeys( hwnd
, wParam
, lParam
);
9938 return LISTVIEW_Command(hwnd
, wParam
, lParam
);
9941 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
9944 return LISTVIEW_EraseBackground(hwnd
, wParam
, lParam
);
9947 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
9950 return LISTVIEW_GetFont(hwnd
);
9953 if (SLOWORD(wParam
) < 0) return 0; /* validate not internal codes */
9954 return LISTVIEW_HScroll(hwnd
, (INT
)LOWORD(wParam
),
9955 (INT
)HIWORD(wParam
), (HWND
)lParam
);
9958 return LISTVIEW_KeyDown(hwnd
, (INT
)wParam
, (LONG
)lParam
);
9961 return LISTVIEW_KillFocus(hwnd
);
9963 case WM_LBUTTONDBLCLK
:
9964 return LISTVIEW_LButtonDblClk(hwnd
, (WORD
)wParam
, LOWORD(lParam
),
9967 case WM_LBUTTONDOWN
:
9968 return LISTVIEW_LButtonDown(hwnd
, (WORD
)wParam
, LOWORD(lParam
),
9971 return LISTVIEW_LButtonUp(hwnd
, (WORD
)wParam
, LOWORD(lParam
),
9974 return LISTVIEW_MouseMove (hwnd
, wParam
, lParam
);
9977 return LISTVIEW_MouseHover(hwnd
, wParam
, lParam
);
9980 return LISTVIEW_NCCreate(hwnd
, wParam
, lParam
);
9983 return LISTVIEW_NCDestroy(hwnd
);
9986 return LISTVIEW_Notify(hwnd
, (INT
)wParam
, (LPNMHDR
)lParam
);
9988 case WM_NOTIFYFORMAT
:
9989 return LISTVIEW_NotifyFormat(hwnd
, (HWND
)wParam
, (INT
)lParam
);
9992 return LISTVIEW_Paint(hwnd
, (HDC
)wParam
);
9994 case WM_RBUTTONDBLCLK
:
9995 return LISTVIEW_RButtonDblClk(hwnd
, (WORD
)wParam
, LOWORD(lParam
),
9998 case WM_RBUTTONDOWN
:
9999 return LISTVIEW_RButtonDown(hwnd
, (WORD
)wParam
, LOWORD(lParam
),
10003 return LISTVIEW_RButtonUp(hwnd
, (WORD
)wParam
, LOWORD(lParam
),
10007 return LISTVIEW_SetFocus(hwnd
, (HWND
)wParam
);
10010 return LISTVIEW_SetFont(hwnd
, (HFONT
)wParam
, (WORD
)lParam
);
10013 return LISTVIEW_SetRedraw(hwnd
, (BOOL
)wParam
);
10016 return LISTVIEW_Size(hwnd
, (int)SLOWORD(lParam
), (int)SHIWORD(lParam
));
10018 case WM_STYLECHANGED
:
10019 return LISTVIEW_StyleChanged(hwnd
, wParam
, (LPSTYLESTRUCT
)lParam
);
10021 case WM_SYSCOLORCHANGE
:
10022 COMCTL32_RefreshSysColors();
10025 /* case WM_TIMER: */
10028 if (SLOWORD(wParam
) < 0) return 0; /* validate not internal codes */
10029 return LISTVIEW_VScroll(hwnd
, (INT
)LOWORD(wParam
),
10030 (INT
)HIWORD(wParam
), (HWND
)lParam
);
10032 case WM_MOUSEWHEEL
:
10033 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
10034 return DefWindowProcW( hwnd
, uMsg
, wParam
, lParam
);
10035 return LISTVIEW_MouseWheel(hwnd
, (short int)HIWORD(wParam
));
10037 case WM_WINDOWPOSCHANGED
:
10038 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
)) {
10039 SetWindowPos(hwnd
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
10040 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
10041 LISTVIEW_UpdateSize(hwnd
);
10042 LISTVIEW_UpdateScroll(hwnd
);
10044 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10046 /* case WM_WININICHANGE: */
10049 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
))
10051 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg
, wParam
,
10055 /* call default window procedure */
10056 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10064 * Registers the window class.
10072 VOID
LISTVIEW_Register(void)
10074 WNDCLASSW wndClass
;
10076 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
10077 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
10078 wndClass
.lpfnWndProc
= (WNDPROC
)LISTVIEW_WindowProc
;
10079 wndClass
.cbClsExtra
= 0;
10080 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
10081 wndClass
.hCursor
= LoadCursorW(0, IDC_ARROWW
);
10082 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
10083 wndClass
.lpszClassName
= WC_LISTVIEWW
;
10084 RegisterClassW(&wndClass
);
10089 * Unregisters the window class.
10097 VOID
LISTVIEW_Unregister(void)
10099 UnregisterClassW(WC_LISTVIEWW
, (HINSTANCE
)NULL
);
10104 * Handle any WM_COMMAND messages
10110 static LRESULT
LISTVIEW_Command(HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
10112 switch (HIWORD(wParam
))
10117 * Adjust the edit window size
10119 WCHAR buffer
[1024];
10120 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(hwnd
, 0);
10121 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
10122 HFONT hFont
, hOldFont
= 0;
10127 len
= GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
10128 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
10130 /* Select font to get the right dimension of the string */
10131 hFont
= SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
10134 hOldFont
= SelectObject(hdc
, hFont
);
10137 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
10139 TEXTMETRICW textMetric
;
10141 /* Add Extra spacing for the next character */
10142 GetTextMetricsW(hdc
, &textMetric
);
10143 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10151 rect
.bottom
- rect
.top
,
10152 SWP_DRAWFRAME
|SWP_NOMOVE
);
10155 SelectObject(hdc
, hOldFont
);
10157 ReleaseDC(hwnd
, hdc
);
10163 return SendMessageW (GetParent (hwnd
), WM_COMMAND
, wParam
, lParam
);
10172 * Subclassed edit control windproc function
10178 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
,
10179 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
10181 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(GetParent(hwnd
), 0);
10182 EDITLABEL_ITEM
*einfo
= infoPtr
->pedititem
;
10183 static BOOL bIgnoreKillFocus
= FALSE
;
10184 BOOL cancel
= FALSE
;
10186 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
10187 hwnd
, uMsg
, wParam
, lParam
, isW
);
10191 case WM_GETDLGCODE
:
10192 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
10195 if(bIgnoreKillFocus
) return TRUE
;
10200 WNDPROC editProc
= einfo
->EditWndProc
;
10201 SetWindowLongW(hwnd
, GWL_WNDPROC
, (LONG
)editProc
);
10202 COMCTL32_Free(einfo
);
10203 infoPtr
->pedititem
= NULL
;
10204 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10208 if (VK_ESCAPE
== (INT
)wParam
)
10213 else if (VK_RETURN
== (INT
)wParam
)
10217 return CallWindowProcT(einfo
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10220 if (einfo
->EditLblCb
)
10222 LPWSTR buffer
= NULL
;
10226 DWORD len
= isW
? GetWindowTextLengthW(hwnd
) : GetWindowTextLengthA(hwnd
);
10230 if ( (buffer
= COMCTL32_Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))) )
10232 if (isW
) GetWindowTextW(hwnd
, buffer
, len
+1);
10233 else GetWindowTextA(hwnd
, (CHAR
*)buffer
, len
+1);
10237 /* Processing LVN_ENDLABELEDIT message could kill the focus */
10238 /* eg. Using a messagebox */
10239 bIgnoreKillFocus
= TRUE
;
10240 einfo
->EditLblCb(GetParent(hwnd
), buffer
, einfo
->param
);
10242 if (buffer
) COMCTL32_Free(buffer
);
10244 einfo
->EditLblCb
= NULL
;
10245 bIgnoreKillFocus
= FALSE
;
10248 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
10254 * Subclassed edit control windproc function
10260 LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10262 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
10267 * Subclassed edit control windproc function
10273 LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10275 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
10280 * Creates a subclassed edit cotrol
10286 HWND
CreateEditLabelT(LPCWSTR text
, DWORD style
, INT x
, INT y
,
10287 INT width
, INT height
, HWND parent
, HINSTANCE hinst
,
10288 EditlblCallbackW EditLblCb
, DWORD param
, BOOL isW
)
10290 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongW(parent
, 0);
10291 WCHAR editName
[5] = { 'E', 'd', 'i', 't', '\0' };
10296 TEXTMETRICW textMetric
;
10298 TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text
, isW
), isW
);
10300 if (NULL
== (infoPtr
->pedititem
= COMCTL32_Alloc(sizeof(EDITLABEL_ITEM
))))
10303 style
|= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|WS_BORDER
;
10304 hdc
= GetDC(parent
);
10306 /* Select the font to get appropriate metric dimensions */
10307 if(infoPtr
->hFont
!= 0)
10308 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
10310 /*Get String Lenght in pixels */
10311 GetTextExtentPoint32W(hdc
, text
, lstrlenW(text
), &sz
);
10313 /*Add Extra spacing for the next character */
10314 GetTextMetricsW(hdc
, &textMetric
);
10315 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10317 if(infoPtr
->hFont
!= 0)
10318 SelectObject(hdc
, hOldFont
);
10320 ReleaseDC(parent
, hdc
);
10322 hedit
= CreateWindowW(editName
, text
, style
, x
, y
, sz
.cx
, height
, parent
, 0, hinst
, 0);
10324 hedit
= CreateWindowA("Edit", (LPCSTR
)text
, style
, x
, y
, sz
.cx
, height
, parent
, 0, hinst
, 0);
10328 COMCTL32_Free(infoPtr
->pedititem
);
10332 infoPtr
->pedititem
->param
= param
;
10333 infoPtr
->pedititem
->EditLblCb
= EditLblCb
;
10334 infoPtr
->pedititem
->EditWndProc
= (WNDPROC
)
10335 (isW
? SetWindowLongW(hedit
, GWL_WNDPROC
, (LONG
)EditLblWndProcW
) :
10336 SetWindowLongA(hedit
, GWL_WNDPROC
, (LONG
)EditLblWndProcA
) );
10338 SendMessageW(hedit
, WM_SETFONT
, infoPtr
->hFont
, FALSE
);