4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 Codeweavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on Oct. 21, 2002, by Dimitrie O. Paun.
29 * Unless otherwise noted, we belive this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
36 * -- Hot item handling, mouse hovering
37 * -- Workareas support
42 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
43 * -- Support CustonDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs.
44 * -- in LISTVIEW_AddGroupSelection, se whould send LVN_ODSTATECHANGED
45 * -- LVA_SNAPTOGRID not implemented
46 * -- LISTVIEW_ApproximateViewRect partially implemented
47 * -- LISTVIEW_[GS]etColumnOrderArray stubs
48 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
49 * -- LISTVIEW_SetIconSpacing is incomplete
50 * -- LISTVIEW_SortItems is broken
51 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
54 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
55 * linear in the number of items in the list, and this is
56 * unacceptable for large lists.
57 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
58 * instead of inserting in the right spot
59 * -- we should keep an ordered array of coordinates in iconic mode
60 * this would allow to frame items (iterator_frameditems),
61 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
69 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
76 * -- LVS_NOSCROLL (see Q137520)
77 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
80 * -- LVS_EX_BORDERSELECT
81 * -- LVS_EX_CHECKBOXES
84 * -- LVS_EX_HEADERDRAGDROP
87 * -- LVS_EX_MULTIWORKAREAS
88 * -- LVS_EX_ONECLICKACTIVATE
90 * -- LVS_EX_SIMPLESELECT
91 * -- LVS_EX_SUBITEMIMAGES
92 * -- LVS_EX_TRACKSELECT
93 * -- LVS_EX_TWOCLICKACTIVATE
94 * -- LVS_EX_UNDERLINECOLD
95 * -- LVS_EX_UNDERLINEHOT
98 * -- LVN_BEGINDRAG, LVN_BEGINRDRAG
99 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
102 * -- LVN_MARQUEEBEGIN
104 * -- LVN_ODSTATECHANGED
109 * -- LVM_CANCELEDITLABEL
110 * -- LVM_CREATEDRAGIMAGE
111 * -- LVM_ENABLEGROUPVIEW
112 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
113 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
114 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
115 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
116 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
117 * -- LVM_GETINSERTMARKRECT
118 * -- LVM_GETNUMBEROFWORKAREAS
119 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
120 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
121 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
122 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
123 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
124 * -- LVM_GETTOOLTIPS, LVM_SETTOOLTIPS
125 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
126 * -- LVM_GETVIEW, LVM_SETVIEW
127 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
128 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
129 * -- LVM_INSERTGROUPSORTED
130 * -- LVM_INSERTMARKHITTEST
131 * -- LVM_ISGROUPVIEWENABLED
132 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
134 * -- LVM_MOVEITEMTOGROUP
136 * -- LVM_SETTILEWIDTH
140 * Known differences in message stream from native control (not known if
141 * these differences cause problems):
142 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
143 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
144 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
145 * processing for "USEDOUBLECLICKTIME".
149 #include "wine/port.h"
159 #include "commctrl.h"
160 #include "comctl32.h"
162 #include "wine/debug.h"
163 #include "wine/unicode.h"
165 WINE_DEFAULT_DEBUG_CHANNEL(listview);
167 /* make sure you set this to 0 for production use! */
168 #define DEBUG_RANGES 1
170 typedef struct tagCOLUMN_INFO
172 RECT rcHeader; /* tracks the header's rectangle */
173 int fmt; /* same as LVCOLUMN.fmt */
176 typedef struct tagITEMHDR
180 } ITEMHDR, *LPITEMHDR;
182 typedef struct tagSUBITEM_INFO
188 typedef struct tagITEM_INFO
196 typedef struct tagRANGE
202 typedef struct tagRANGES
207 typedef struct tagITERATOR
216 typedef struct tagLISTVIEW_INFO
223 COLORREF clrTextBkDefault;
224 HIMAGELIST himlNormal;
225 HIMAGELIST himlSmall;
226 HIMAGELIST himlState;
229 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
232 RANGES selectionRanges;
236 RECT rcList; /* This rectangle is really the window
237 * client rectangle possibly reduced by the
238 * horizontal scroll bar and/or header - see
239 * LISTVIEW_UpdateSize. This rectangle offset
240 * by the LISTVIEW_GetOrigin value is in
241 * client coordinates */
250 INT ntmHeight; /* Some cached metrics of the font used */
251 INT ntmAveCharWidth; /* by the listview to draw items */
252 BOOL bRedraw; /* Turns on/off repaints & invalidations */
253 BOOL bFirstPaint; /* Flags if the control has never painted before */
254 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
256 BOOL bDoChangeNotify; /* send change notification messages? */
259 DWORD dwStyle; /* the cached window GWL_STYLE */
260 DWORD dwLvExStyle; /* extended listview style */
261 INT nItemCount; /* the number of items in the list */
262 HDPA hdpaItems; /* array ITEM_INFO pointers */
263 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
264 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
265 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
266 POINT currIconPos; /* this is the position next icon will be placed */
267 PFNLVCOMPARE pfnCompare;
275 DWORD lastKeyPressTimestamp;
277 INT nSearchParamLength;
278 WCHAR szSearchParam[ MAX_PATH ];
285 /* How many we debug buffer to allocate */
286 #define DEBUG_BUFFERS 20
287 /* The size of a single debug bbuffer */
288 #define DEBUG_BUFFER_SIZE 256
290 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
291 #define SB_INTERNAL -1
293 /* maximum size of a label */
294 #define DISP_TEXT_SIZE 512
296 /* padding for items in list and small icon display modes */
297 #define WIDTH_PADDING 12
299 /* padding for items in list, report and small icon display modes */
300 #define HEIGHT_PADDING 1
302 /* offset of items in report display mode */
303 #define REPORT_MARGINX 2
305 /* padding for icon in large icon display mode
306 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
307 * that HITTEST will see.
308 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
309 * ICON_TOP_PADDING - sum of the two above.
310 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
311 * LABEL_HOR_PADDING - between text and sides of box
312 * LABEL_VERT_PADDING - between bottom of text and end of box
314 * ICON_LR_PADDING - additional width above icon size.
315 * ICON_LR_HALF - half of the above value
317 #define ICON_TOP_PADDING_NOTHITABLE 2
318 #define ICON_TOP_PADDING_HITABLE 2
319 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
320 #define ICON_BOTTOM_PADDING 4
321 #define LABEL_HOR_PADDING 5
322 #define LABEL_VERT_PADDING 7
323 #define ICON_LR_PADDING 16
324 #define ICON_LR_HALF (ICON_LR_PADDING/2)
326 /* default label width for items in list and small icon display modes */
327 #define DEFAULT_LABEL_WIDTH 40
329 /* default column width for items in list display mode */
330 #define DEFAULT_COLUMN_WIDTH 128
332 /* Size of "line" scroll for V & H scrolls */
333 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
335 /* Padding betwen image and label */
336 #define IMAGE_PADDING 2
338 /* Padding behind the label */
339 #define TRAILING_LABEL_PADDING 12
340 #define TRAILING_HEADER_PADDING 11
342 /* Border for the icon caption */
343 #define CAPTION_BORDER 2
345 /* Standard DrawText flags */
346 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
347 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
348 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
350 /* The time in milliseconds to reset the search in the list */
351 #define KEY_DELAY 450
353 /* Dump the LISTVIEW_INFO structure to the debug channel */
354 #define LISTVIEW_DUMP(iP) do { \
355 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
356 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
357 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
358 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
359 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
360 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
361 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
362 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
363 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
364 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
368 * forward declarations
370 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
371 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
372 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
373 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
374 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
375 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
376 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
377 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
378 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
379 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
380 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
381 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
382 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
383 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
384 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
385 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
386 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
387 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
388 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
389 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
390 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
391 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
392 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
393 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
394 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
396 /******** Text handling functions *************************************/
398 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
399 * text string. The string may be ANSI or Unicode, in which case
400 * the boolean isW tells us the type of the string.
402 * The name of the function tell what type of strings it expects:
403 * W: Unicode, T: ANSI/Unicode - function of isW
406 static inline BOOL is_textW(LPCWSTR text)
408 return text != NULL && text != LPSTR_TEXTCALLBACKW;
411 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
413 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
414 return is_textW(text);
417 static inline int textlenT(LPCWSTR text, BOOL isW)
419 return !is_textT(text, isW) ? 0 :
420 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
423 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
426 if (isSrcW) lstrcpynW(dest, src, max);
427 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
429 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
430 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
433 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
435 LPWSTR wstr = (LPWSTR)text;
437 if (!isW && is_textT(text, isW))
439 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
440 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
441 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
443 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
447 static inline void textfreeT(LPWSTR wstr, BOOL isW)
449 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
453 * dest is a pointer to a Unicode string
454 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
456 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
460 if (src == LPSTR_TEXTCALLBACKW)
462 if (is_textW(*dest)) COMCTL32_Free(*dest);
463 *dest = LPSTR_TEXTCALLBACKW;
467 LPWSTR pszText = textdupTtoW(src, isW);
468 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
469 bResult = Str_SetPtrW(dest, pszText);
470 textfreeT(pszText, isW);
476 * compares a Unicode to a Unicode/ANSI text string
478 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
480 if (!aw) return bt ? -1 : 0;
481 if (!bt) return aw ? 1 : 0;
482 if (aw == LPSTR_TEXTCALLBACKW)
483 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
484 if (bt != LPSTR_TEXTCALLBACKW)
486 LPWSTR bw = textdupTtoW(bt, isW);
487 int r = bw ? lstrcmpW(aw, bw) : 1;
495 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
499 n = min(min(n, strlenW(s1)), strlenW(s2));
500 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
501 return res ? res - sizeof(WCHAR) : res;
504 /******** Debugging functions *****************************************/
506 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
508 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
509 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
512 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
514 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
515 n = min(textlenT(text, isW), n);
516 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
519 static char* debug_getbuf()
521 static int index = 0;
522 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
523 return buffers[index++ % DEBUG_BUFFERS];
526 static inline char* debugrange(const RANGE *lprng)
530 char* buf = debug_getbuf();
531 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
533 } else return "(null)";
536 static inline char* debugpoint(const POINT *lppt)
540 char* buf = debug_getbuf();
541 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
543 } else return "(null)";
546 static inline char* debugrect(const RECT *rect)
550 char* buf = debug_getbuf();
551 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
552 rect->left, rect->top, rect->right, rect->bottom);
554 } else return "(null)";
557 static char * debugscrollinfo(const SCROLLINFO *pScrollInfo)
559 char* buf = debug_getbuf(), *text = buf;
560 int len, size = DEBUG_BUFFER_SIZE;
562 if (pScrollInfo == NULL) return "(null)";
563 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
564 if (len == -1) goto end; buf += len; size -= len;
565 if (pScrollInfo->fMask & SIF_RANGE)
566 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
568 if (len == -1) goto end; buf += len; size -= len;
569 if (pScrollInfo->fMask & SIF_PAGE)
570 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
572 if (len == -1) goto end; buf += len; size -= len;
573 if (pScrollInfo->fMask & SIF_POS)
574 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
576 if (len == -1) goto end; buf += len; size -= len;
577 if (pScrollInfo->fMask & SIF_TRACKPOS)
578 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
580 if (len == -1) goto end; buf += len; size -= len;
583 buf = text + strlen(text);
585 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
589 static char* debugnmlistview(const NMLISTVIEW *plvnm)
593 char* buf = debug_getbuf();
594 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
595 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
596 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
597 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
599 } else return "(null)";
602 static char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
604 char* buf = debug_getbuf(), *text = buf;
605 int len, size = DEBUG_BUFFER_SIZE;
607 if (lpLVItem == NULL) return "(null)";
608 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
609 if (len == -1) goto end; buf += len; size -= len;
610 if (lpLVItem->mask & LVIF_STATE)
611 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
613 if (len == -1) goto end; buf += len; size -= len;
614 if (lpLVItem->mask & LVIF_TEXT)
615 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
617 if (len == -1) goto end; buf += len; size -= len;
618 if (lpLVItem->mask & LVIF_IMAGE)
619 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
621 if (len == -1) goto end; buf += len; size -= len;
622 if (lpLVItem->mask & LVIF_PARAM)
623 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
625 if (len == -1) goto end; buf += len; size -= len;
626 if (lpLVItem->mask & LVIF_INDENT)
627 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
629 if (len == -1) goto end; buf += len; size -= len;
632 buf = text + strlen(text);
634 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
638 static char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
640 char* buf = debug_getbuf(), *text = buf;
641 int len, size = DEBUG_BUFFER_SIZE;
643 if (lpColumn == NULL) return "(null)";
644 len = snprintf(buf, size, "{");
645 if (len == -1) goto end; buf += len; size -= len;
646 if (lpColumn->mask & LVCF_SUBITEM)
647 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
649 if (len == -1) goto end; buf += len; size -= len;
650 if (lpColumn->mask & LVCF_FMT)
651 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
653 if (len == -1) goto end; buf += len; size -= len;
654 if (lpColumn->mask & LVCF_WIDTH)
655 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
657 if (len == -1) goto end; buf += len; size -= len;
658 if (lpColumn->mask & LVCF_TEXT)
659 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
661 if (len == -1) goto end; buf += len; size -= len;
662 if (lpColumn->mask & LVCF_IMAGE)
663 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
665 if (len == -1) goto end; buf += len; size -= len;
666 if (lpColumn->mask & LVCF_ORDER)
667 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
669 if (len == -1) goto end; buf += len; size -= len;
672 buf = text + strlen(text);
674 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
678 static char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
682 char* buf = debug_getbuf();
683 snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
684 debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
686 } else return "(null)";
689 /* Return the corresponding text for a given scroll value */
690 static inline LPCSTR debugscrollcode(int nScrollCode)
694 case SB_LINELEFT: return "SB_LINELEFT";
695 case SB_LINERIGHT: return "SB_LINERIGHT";
696 case SB_PAGELEFT: return "SB_PAGELEFT";
697 case SB_PAGERIGHT: return "SB_PAGERIGHT";
698 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
699 case SB_THUMBTRACK: return "SB_THUMBTRACK";
700 case SB_ENDSCROLL: return "SB_ENDSCROLL";
701 case SB_INTERNAL: return "SB_INTERNAL";
702 default: return "unknown";
707 /******** Notification functions i************************************/
709 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
713 TRACE("(code=%d)\n", code);
715 pnmh->hwndFrom = infoPtr->hwndSelf;
716 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
718 result = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
719 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
721 TRACE(" <= %ld\n", result);
726 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
729 return notify_hdr(infoPtr, code, &nmh);
732 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
734 notify(infoPtr, LVN_ITEMACTIVATE);
737 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
739 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
740 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
743 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
748 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
749 ZeroMemory(&nmlv, sizeof(nmlv));
750 nmlv.iItem = lvht->iItem;
751 nmlv.iSubItem = lvht->iSubItem;
752 nmlv.ptAction = lvht->pt;
753 item.mask = LVIF_PARAM;
754 item.iItem = lvht->iItem;
756 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
757 return notify_listview(infoPtr, code, &nmlv);
760 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
765 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
767 item.mask = LVIF_PARAM;
770 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
771 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
774 static int get_ansi_notification(INT unicodeNotificationCode)
776 switch (unicodeNotificationCode)
778 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
779 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
780 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
781 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
782 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
783 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
785 ERR("unknown notification %x\n", unicodeNotificationCode);
791 With testing on Windows 2000 it looks like the notify format
792 has nothing to do with this message. It ALWAYS seems to be
795 infoPtr : listview struct
796 notificationCode : *Unicode* notification code
797 pdi : dispinfo structure (can be unicode or ansi)
798 isW : TRUE if dispinfo is Unicode
800 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
802 BOOL bResult = FALSE;
803 BOOL convertToAnsi = FALSE;
804 INT cchTempBufMax = 0, savCchTextMax = 0;
805 LPWSTR pszTempBuf = NULL, savPszText = NULL;
807 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
812 if (notificationCode != LVN_GETDISPINFOW)
814 cchTempBufMax = WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText,
815 -1, NULL, 0, NULL, NULL);
819 cchTempBufMax = pdi->item.cchTextMax;
820 *pdi->item.pszText = 0; /* make sure we don't process garbage */
823 pszTempBuf = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) *
825 if (!pszTempBuf) return FALSE;
827 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR)
828 pszTempBuf, cchTempBufMax, NULL, NULL);
830 savCchTextMax = pdi->item.cchTextMax;
831 savPszText = pdi->item.pszText;
832 pdi->item.pszText = pszTempBuf;
833 pdi->item.cchTextMax = cchTempBufMax;
836 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat !=
839 bResult = notify_hdr(infoPtr, get_ansi_notification(notificationCode),
844 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
845 savPszText, savCchTextMax);
846 pdi->item.pszText = savPszText; /* restores our buffer */
847 pdi->item.cchTextMax = savCchTextMax;
848 HeapFree(GetProcessHeap(), 0, pszTempBuf);
853 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
854 const RECT *rcBounds, const LVITEMW *lplvItem)
856 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
857 lpnmlvcd->nmcd.hdc = hdc;
858 lpnmlvcd->nmcd.rc = *rcBounds;
859 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
860 lpnmlvcd->clrText = infoPtr->clrText;
861 if (!lplvItem) return;
862 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
863 lpnmlvcd->iSubItem = lplvItem->iSubItem;
864 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
865 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
866 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
867 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
870 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
872 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
875 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
876 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
877 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
878 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
879 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
880 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
884 static DWORD notify_prepaint (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
886 BOOL isSelected = lpnmlvcd->nmcd.uItemState & CDIS_SELECTED;
887 DWORD cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, lpnmlvcd);
889 if (cditemmode & CDRF_SKIPDEFAULT) return cditemmode;
891 /* apprently, for selected items, we have to override the returned values */
896 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
897 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
899 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
901 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
902 lpnmlvcd->clrText = comctl32_color.clrBtnText;
906 /* Set the text attributes */
907 if (lpnmlvcd->clrTextBk != CLR_NONE)
909 SetBkMode(hdc, OPAQUE);
910 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
911 SetBkColor(hdc, infoPtr->clrTextBkDefault);
913 SetBkColor(hdc,lpnmlvcd->clrTextBk);
916 SetBkMode(hdc, TRANSPARENT);
917 SetTextColor(hdc, lpnmlvcd->clrText);
922 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
924 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
927 /******** Item iterator functions **********************************/
929 static RANGES ranges_create(int count);
930 static void ranges_destroy(RANGES ranges);
931 static BOOL ranges_add(RANGES ranges, RANGE range);
932 static BOOL ranges_del(RANGES ranges, RANGE range);
933 static void ranges_dump(RANGES ranges);
935 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
937 RANGE range = { nItem, nItem + 1 };
939 return ranges_add(ranges, range);
942 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
944 RANGE range = { nItem, nItem + 1 };
946 return ranges_del(ranges, range);
950 * ITERATOR DOCUMENTATION
952 * The iterator functions allow for easy, and convenient iteration
953 * over items of iterest in the list. Typically, you create a
954 * iterator, use it, and destroy it, as such:
957 * iterator_xxxitems(&i, ...);
958 * while (iterator_{prev,next}(&i)
960 * //code which uses i.nItem
962 * iterator_destroy(&i);
964 * where xxx is either: framed, or visible.
965 * Note that it is important that the code destroys the iterator
966 * after it's done with it, as the creation of the iterator may
967 * allocate memory, which thus needs to be freed.
969 * You can iterate both forwards, and backwards through the list,
970 * by using iterator_next or iterator_prev respectively.
972 * Lower numbered items are draw on top of higher number items in
973 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
974 * items may overlap). So, to test items, you should use
976 * which lists the items top to bottom (in Z-order).
977 * For drawing items, you should use
979 * which lists the items bottom to top (in Z-order).
980 * If you keep iterating over the items after the end-of-items
981 * marker (-1) is returned, the iterator will start from the
982 * beginning. Typically, you don't need to test for -1,
983 * because iterator_{next,prev} will return TRUE if more items
984 * are to be iterated over, or FALSE otherwise.
986 * Note: the iterator is defined to be bidirectional. That is,
987 * any number of prev followed by any number of next, or
988 * five versa, should leave the iterator at the same item:
989 * prev * n, next * n = next * n, prev * n
991 * The iterator has a notion of a out-of-order, special item,
992 * which sits at the start of the list. This is used in
993 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
994 * which needs to be first, as it may overlap other items.
996 * The code is a bit messy because we have:
997 * - a special item to deal with
998 * - simple range, or composite range
1000 * If you find bugs, or want to add features, please make sure you
1001 * always check/modify *both* iterator_prev, and iterator_next.
1005 * This function iterates through the items in increasing order,
1006 * but prefixed by the special item, then -1. That is:
1007 * special, 1, 2, 3, ..., n, -1.
1008 * Each item is listed only once.
1010 static inline BOOL iterator_next(ITERATOR* i)
1014 i->nItem = i->nSpecial;
1015 if (i->nItem != -1) return TRUE;
1017 if (i->nItem == i->nSpecial)
1019 if (i->ranges) i->index = 0;
1025 if (i->nItem == i->nSpecial) i->nItem++;
1026 if (i->nItem < i->range.upper) return TRUE;
1031 if (i->index < i->ranges->hdpa->nItemCount)
1032 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1035 else if (i->nItem >= i->range.upper) goto end;
1037 i->nItem = i->range.lower;
1038 if (i->nItem >= 0) goto testitem;
1045 * This function iterates through the items in decreasing order,
1046 * followed by the special item, then -1. That is:
1047 * n, n-1, ..., 3, 2, 1, special, -1.
1048 * Each item is listed only once.
1050 static inline BOOL iterator_prev(ITERATOR* i)
1057 if (i->ranges) i->index = i->ranges->hdpa->nItemCount;
1060 if (i->nItem == i->nSpecial)
1068 if (i->nItem == i->nSpecial) i->nItem--;
1069 if (i->nItem >= i->range.lower) return TRUE;
1075 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1078 else if (!start && i->nItem < i->range.lower) goto end;
1080 i->nItem = i->range.upper;
1081 if (i->nItem > 0) goto testitem;
1083 return (i->nItem = i->nSpecial) != -1;
1086 static RANGE iterator_range(ITERATOR* i)
1090 if (!i->ranges) return i->range;
1092 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1093 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->ranges->hdpa->nItemCount - 1)).upper;
1098 * Releases resources associated with this ierator.
1100 static inline void iterator_destroy(ITERATOR* i)
1102 ranges_destroy(i->ranges);
1106 * Create an empty iterator.
1108 static inline BOOL iterator_empty(ITERATOR* i)
1110 ZeroMemory(i, sizeof(*i));
1111 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1116 * Create an iterator over a range.
1118 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1126 * Create an iterator over a bunch of ranges.
1127 * Please note that the iterator will take ownership of the ranges,
1128 * and will free them upon destruction.
1130 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1138 * Creates an iterator over the items which intersect lprc.
1140 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1142 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1143 RECT frame = *lprc, rcItem, rcTemp;
1146 /* in case we fail, we want to return an empty iterator */
1147 if (!iterator_empty(i)) return FALSE;
1149 LISTVIEW_GetOrigin(infoPtr, &Origin);
1151 TRACE("(lprc=%s)\n", debugrect(lprc));
1152 OffsetRect(&frame, -Origin.x, -Origin.y);
1154 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1158 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1160 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1161 if (IntersectRect(&rcTemp, &rcItem, lprc))
1162 i->nSpecial = infoPtr->nFocusedItem;
1164 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1165 /* to do better here, we need to have PosX, and PosY sorted */
1166 TRACE("building icon ranges:\n");
1167 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1169 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1170 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1171 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1172 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1173 if (IntersectRect(&rcTemp, &rcItem, &frame))
1174 ranges_additem(i->ranges, nItem);
1178 else if (uView == LVS_REPORT)
1182 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1183 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1185 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1186 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1187 if (range.upper <= range.lower) return TRUE;
1188 if (!iterator_rangeitems(i, range)) return FALSE;
1189 TRACE(" report=%s\n", debugrange(&i->range));
1193 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1194 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1195 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1196 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1197 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1198 INT lower = nFirstCol * nPerCol + nFirstRow;
1202 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1203 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1205 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1207 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1208 TRACE("building list ranges:\n");
1209 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1211 item_range.lower = nCol * nPerCol + nFirstRow;
1212 if(item_range.lower >= infoPtr->nItemCount) break;
1213 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1214 TRACE(" list=%s\n", debugrange(&item_range));
1215 ranges_add(i->ranges, item_range);
1223 * Creates an iterator over the items which intersect the visible region of hdc.
1225 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1227 POINT Origin, Position;
1228 RECT rcItem, rcClip;
1231 rgntype = GetClipBox(hdc, &rcClip);
1232 if (rgntype == NULLREGION) return iterator_empty(i);
1233 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1234 if (rgntype == SIMPLEREGION) return TRUE;
1236 /* first deal with the special item */
1237 if (i->nSpecial != -1)
1239 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1240 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1243 /* if we can't deal with the region, we'll just go with the simple range */
1244 LISTVIEW_GetOrigin(infoPtr, &Origin);
1245 TRACE("building visible range:\n");
1246 if (!i->ranges && i->range.lower < i->range.upper)
1248 if (!(i->ranges = ranges_create(50))) return TRUE;
1249 if (!ranges_add(i->ranges, i->range))
1251 ranges_destroy(i->ranges);
1257 /* now delete the invisible items from the list */
1258 while(iterator_next(i))
1260 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1261 rcItem.left = Position.x + Origin.x;
1262 rcItem.top = Position.y + Origin.y;
1263 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1264 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1265 if (!RectVisible(hdc, &rcItem))
1266 ranges_delitem(i->ranges, i->nItem);
1268 /* the iterator should restart on the next iterator_next */
1274 /******** Misc helper functions ************************************/
1276 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1277 WPARAM wParam, LPARAM lParam, BOOL isW)
1279 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1280 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1283 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1285 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1287 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1288 (uView == LVS_ICON || uView == LVS_SMALLICON);
1291 /******** Internal API functions ************************************/
1293 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1295 static COLUMN_INFO mainItem;
1297 if (nSubItem == 0 && infoPtr->hdpaColumns->nItemCount == 0) return &mainItem;
1298 assert (nSubItem >= 0 && nSubItem < infoPtr->hdpaColumns->nItemCount);
1299 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1302 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1304 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1307 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1309 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1312 /* Listview invalidation functions: use _only_ these functions to invalidate */
1314 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1316 return infoPtr->bRedraw && !infoPtr->bFirstPaint;
1319 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1321 if(!is_redrawing(infoPtr)) return;
1322 TRACE(" invalidating rect=%s\n", debugrect(rect));
1323 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1326 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1330 if(!is_redrawing(infoPtr)) return;
1331 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1332 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1335 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1337 POINT Origin, Position;
1340 if(!is_redrawing(infoPtr)) return;
1341 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1342 LISTVIEW_GetOrigin(infoPtr, &Origin);
1343 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1344 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1346 rcBox.bottom = infoPtr->nItemHeight;
1347 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1348 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1351 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1353 LISTVIEW_InvalidateRect(infoPtr, NULL);
1356 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1360 if(!is_redrawing(infoPtr)) return;
1361 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1362 rcCol.top = infoPtr->rcList.top;
1363 rcCol.bottom = infoPtr->rcList.bottom;
1364 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1369 * Retrieves the number of items that can fit vertically in the client area.
1372 * [I] infoPtr : valid pointer to the listview structure
1375 * Number of items per row.
1377 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1379 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1381 return max(nListWidth/infoPtr->nItemWidth, 1);
1386 * Retrieves the number of items that can fit horizontally in the client
1390 * [I] infoPtr : valid pointer to the listview structure
1393 * Number of items per column.
1395 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1397 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1399 return max(nListHeight / infoPtr->nItemHeight, 1);
1403 /*************************************************************************
1404 * LISTVIEW_ProcessLetterKeys
1406 * Processes keyboard messages generated by pressing the letter keys
1408 * What this does is perform a case insensitive search from the
1409 * current position with the following quirks:
1410 * - If two chars or more are pressed in quick succession we search
1411 * for the corresponding string (e.g. 'abc').
1412 * - If there is a delay we wipe away the current search string and
1413 * restart with just that char.
1414 * - If the user keeps pressing the same character, whether slowly or
1415 * fast, so that the search string is entirely composed of this
1416 * character ('aaaaa' for instance), then we search for first item
1417 * that starting with that character.
1418 * - If the user types the above character in quick succession, then
1419 * we must also search for the corresponding string ('aaaaa'), and
1420 * go to that string if there is a match.
1423 * [I] hwnd : handle to the window
1424 * [I] charCode : the character code, the actual character
1425 * [I] keyData : key data
1433 * - The current implementation has a list of characters it will
1434 * accept and it ignores averything else. In particular it will
1435 * ignore accentuated characters which seems to match what
1436 * Windows does. But I'm not sure it makes sense to follow
1438 * - We don't sound a beep when the search fails.
1442 * TREEVIEW_ProcessLetterKeys
1444 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1449 WCHAR buffer[MAX_PATH];
1450 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1452 /* simple parameter checking */
1453 if (!charCode || !keyData) return 0;
1455 /* only allow the valid WM_CHARs through */
1456 if (!isalnum(charCode) &&
1457 charCode != '.' && charCode != '`' && charCode != '!' &&
1458 charCode != '@' && charCode != '#' && charCode != '$' &&
1459 charCode != '%' && charCode != '^' && charCode != '&' &&
1460 charCode != '*' && charCode != '(' && charCode != ')' &&
1461 charCode != '-' && charCode != '_' && charCode != '+' &&
1462 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1463 charCode != '}' && charCode != '[' && charCode != '{' &&
1464 charCode != '/' && charCode != '?' && charCode != '>' &&
1465 charCode != '<' && charCode != ',' && charCode != '~')
1468 /* if there's one item or less, there is no where to go */
1469 if (infoPtr->nItemCount <= 1) return 0;
1471 /* update the search parameters */
1472 infoPtr->lastKeyPressTimestamp = GetTickCount();
1473 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1474 if (infoPtr->nSearchParamLength < MAX_PATH)
1475 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1476 if (infoPtr->charCode != charCode)
1477 infoPtr->charCode = charCode = 0;
1479 infoPtr->charCode=charCode;
1480 infoPtr->szSearchParam[0]=charCode;
1481 infoPtr->nSearchParamLength=1;
1482 /* Redundant with the 1 char string */
1486 /* and search from the current position */
1488 if (infoPtr->nFocusedItem >= 0) {
1489 endidx=infoPtr->nFocusedItem;
1491 /* if looking for single character match,
1492 * then we must always move forward
1494 if (infoPtr->nSearchParamLength == 1)
1497 endidx=infoPtr->nItemCount;
1501 if (idx == infoPtr->nItemCount) {
1502 if (endidx == infoPtr->nItemCount || endidx == 0)
1508 item.mask = LVIF_TEXT;
1511 item.pszText = buffer;
1512 item.cchTextMax = MAX_PATH;
1513 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1515 /* check for a match */
1516 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1519 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1520 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1521 /* This would work but we must keep looking for a longer match */
1525 } while (idx != endidx);
1528 LISTVIEW_KeySelection(infoPtr, nItem);
1533 /*************************************************************************
1534 * LISTVIEW_UpdateHeaderSize [Internal]
1536 * Function to resize the header control
1539 * [I] hwnd : handle to a window
1540 * [I] nNewScrollPos : scroll pos to set
1545 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1550 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1552 GetWindowRect(infoPtr->hwndHeader, &winRect);
1553 point[0].x = winRect.left;
1554 point[0].y = winRect.top;
1555 point[1].x = winRect.right;
1556 point[1].y = winRect.bottom;
1558 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1559 point[0].x = -nNewScrollPos;
1560 point[1].x += nNewScrollPos;
1562 SetWindowPos(infoPtr->hwndHeader,0,
1563 point[0].x,point[0].y,point[1].x,point[1].y,
1564 SWP_NOZORDER | SWP_NOACTIVATE);
1569 * Update the scrollbars. This functions should be called whenever
1570 * the content, size or view changes.
1573 * [I] infoPtr : valid pointer to the listview structure
1578 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1580 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1581 SCROLLINFO horzInfo, vertInfo;
1583 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1585 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1586 horzInfo.cbSize = sizeof(SCROLLINFO);
1587 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1589 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1590 if (uView == LVS_LIST)
1592 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1593 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1595 /* scroll by at least one column per page */
1596 if(horzInfo.nPage < infoPtr->nItemWidth)
1597 horzInfo.nPage = infoPtr->nItemWidth;
1599 horzInfo.nPage /= infoPtr->nItemWidth;
1601 else if (uView == LVS_REPORT)
1603 horzInfo.nMax = infoPtr->nItemWidth;
1605 else /* LVS_ICON, or LVS_SMALLICON */
1609 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1612 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1613 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1614 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1615 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1617 /* Setting the horizontal scroll can change the listview size
1618 * (and potentially everything else) so we need to recompute
1619 * everything again for the vertical scroll
1622 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1623 vertInfo.cbSize = sizeof(SCROLLINFO);
1624 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1626 if (uView == LVS_REPORT)
1628 vertInfo.nMax = infoPtr->nItemCount;
1630 /* scroll by at least one page */
1631 if(vertInfo.nPage < infoPtr->nItemHeight)
1632 vertInfo.nPage = infoPtr->nItemHeight;
1634 vertInfo.nPage /= infoPtr->nItemHeight;
1636 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1640 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1643 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1644 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1645 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1646 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1648 /* Update the Header Control */
1649 if (uView == LVS_REPORT)
1651 horzInfo.fMask = SIF_POS;
1652 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1653 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1660 * Shows/hides the focus rectangle.
1663 * [I] infoPtr : valid pointer to the listview structure
1664 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1669 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1671 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1674 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1676 if (infoPtr->nFocusedItem < 0) return;
1678 /* we need some gymnastics in ICON mode to handle large items */
1679 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1683 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1684 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1686 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1691 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1693 /* for some reason, owner draw should work only in report mode */
1694 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1699 item.iItem = infoPtr->nFocusedItem;
1701 item.mask = LVIF_PARAM;
1702 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1704 ZeroMemory(&dis, sizeof(dis));
1705 dis.CtlType = ODT_LISTVIEW;
1706 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1707 dis.itemID = item.iItem;
1708 dis.itemAction = ODA_FOCUS;
1709 if (fShow) dis.itemState |= ODS_FOCUS;
1710 dis.hwndItem = infoPtr->hwndSelf;
1712 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1713 dis.itemData = item.lParam;
1715 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1719 DrawFocusRect(hdc, &infoPtr->rcFocus);
1722 ReleaseDC(infoPtr->hwndSelf, hdc);
1726 * Invalidates all visible selected items.
1728 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1732 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1733 while(iterator_next(&i))
1735 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1736 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1738 iterator_destroy(&i);
1743 * DESCRIPTION: [INTERNAL]
1744 * Computes an item's (left,top) corner, relative to rcView.
1745 * That is, the position has NOT been made relative to the Origin.
1746 * This is deliberate, to avoid computing the Origin over, and
1747 * over again, when this function is call in a loop. Instead,
1748 * one ca factor the computation of the Origin before the loop,
1749 * and offset the value retured by this function, on every iteration.
1752 * [I] infoPtr : valid pointer to the listview structure
1753 * [I] nItem : item number
1754 * [O] lpptOrig : item top, left corner
1759 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1761 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1763 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1765 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1767 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1768 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1770 else if (uView == LVS_LIST)
1772 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1773 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1774 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1776 else /* LVS_REPORT */
1778 lpptPosition->x = 0;
1779 lpptPosition->y = nItem * infoPtr->nItemHeight;
1784 * DESCRIPTION: [INTERNAL]
1785 * Compute the rectangles of an item. This is to localize all
1786 * the computations in one place. If you are not interested in some
1787 * of these values, simply pass in a NULL -- the fucntion is smart
1788 * enough to compute only what's necessary. The function computes
1789 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1790 * one, the BOX rectangle. This rectangle is very cheap to compute,
1791 * and is guaranteed to contain all the other rectangles. Computing
1792 * the ICON rect is also cheap, but all the others are potentaily
1793 * expensive. This gives an easy and effective optimization when
1794 * searching (like point inclusion, or rectangle intersection):
1795 * first test against the BOX, and if TRUE, test agains the desired
1797 * If the function does not have all the necessary information
1798 * to computed the requested rectangles, will crash with a
1799 * failed assertion. This is done so we catch all programming
1800 * errors, given that the function is called only from our code.
1802 * We have the following 'special' meanings for a few fields:
1803 * * If LVIS_FOCUSED is set, we assume the item has the focus
1804 * This is important in ICON mode, where it might get a larger
1805 * then usual rectange
1807 * Please note that subitem support works only in REPORT mode.
1810 * [I] infoPtr : valid pointer to the listview structure
1811 * [I] lpLVItem : item to compute the measures for
1812 * [O] lprcBox : ptr to Box rectangle
1813 * The internal LVIR_BOX rectangle
1814 * [0] lprcState : ptr to State icon rectangle
1815 * The internal LVIR_STATE rectangle
1816 * [O] lprcIcon : ptr to Icon rectangle
1817 * Same as LVM_GETITEMRECT with LVIR_ICON
1818 * [O] lprcLabel : ptr to Label rectangle
1819 * Same as LVM_GETITEMRECT with LVIR_LABEL
1824 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1825 LPRECT lprcBox, LPRECT lprcState,
1826 LPRECT lprcIcon, LPRECT lprcLabel)
1828 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1829 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1830 RECT Box, State, Icon, Label;
1831 COLUMN_INFO *lpColumnInfo = NULL;
1833 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1835 /* Be smart and try to figure out the minimum we have to do */
1836 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1837 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1839 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1840 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1842 if (lprcLabel) doLabel = TRUE;
1843 if (doLabel || lprcIcon) doIcon = TRUE;
1844 if (doIcon || lprcState) doState = TRUE;
1846 /************************************************************/
1847 /* compute the box rectangle (it should be cheap to do) */
1848 /************************************************************/
1849 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1850 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1852 if (lpLVItem->iSubItem)
1854 Box = lpColumnInfo->rcHeader;
1859 Box.right = infoPtr->nItemWidth;
1862 Box.bottom = infoPtr->nItemHeight;
1864 /************************************************************/
1865 /* compute STATEICON bounding box */
1866 /************************************************************/
1869 if (uView == LVS_ICON)
1871 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1872 if (infoPtr->himlNormal)
1873 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1874 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1878 /* we need the ident in report mode, if we don't have it, we fail */
1879 State.left = Box.left;
1880 if (uView == LVS_REPORT)
1882 if (lpLVItem->iSubItem == 0)
1884 State.left += REPORT_MARGINX;
1885 assert(lpLVItem->mask & LVIF_INDENT);
1886 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1889 State.top = Box.top;
1891 State.right = State.left;
1892 State.bottom = State.top;
1893 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1895 State.right += infoPtr->iconStateSize.cx;
1896 State.bottom += infoPtr->iconStateSize.cy;
1898 if (lprcState) *lprcState = State;
1899 TRACE(" - state=%s\n", debugrect(&State));
1902 /************************************************************/
1903 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1904 /************************************************************/
1907 if (uView == LVS_ICON)
1909 Icon.left = Box.left;
1910 if (infoPtr->himlNormal)
1911 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1912 Icon.top = Box.top + ICON_TOP_PADDING;
1913 Icon.right = Icon.left;
1914 Icon.bottom = Icon.top;
1915 if (infoPtr->himlNormal)
1917 Icon.right += infoPtr->iconSize.cx;
1918 Icon.bottom += infoPtr->iconSize.cy;
1921 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1923 Icon.left = State.right;
1925 Icon.right = Icon.left;
1926 if (infoPtr->himlSmall && (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE)))
1927 Icon.right += infoPtr->iconSize.cx;
1928 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1930 if(lprcIcon) *lprcIcon = Icon;
1931 TRACE(" - icon=%s\n", debugrect(&Icon));
1934 /************************************************************/
1935 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1936 /************************************************************/
1939 SIZE labelSize = { 0, 0 };
1941 /* calculate how far to the right can the label strech */
1942 Label.right = Box.right;
1943 if (uView == LVS_REPORT)
1945 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1948 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1950 labelSize.cx = infoPtr->nItemWidth;
1951 labelSize.cy = infoPtr->nItemHeight;
1955 /* we need the text in non owner draw mode */
1956 assert(lpLVItem->mask & LVIF_TEXT);
1957 if (is_textT(lpLVItem->pszText, TRUE))
1959 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1960 HDC hdc = GetDC(infoPtr->hwndSelf);
1961 HFONT hOldFont = SelectObject(hdc, hFont);
1965 /* compute rough rectangle where the label will go */
1966 SetRectEmpty(&rcText);
1967 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
1968 rcText.bottom = infoPtr->nItemHeight;
1969 if (uView == LVS_ICON)
1970 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1972 /* now figure out the flags */
1973 if (uView == LVS_ICON)
1974 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1976 uFormat = LV_SL_DT_FLAGS;
1978 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1980 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
1981 labelSize.cy = rcText.bottom - rcText.top;
1983 SelectObject(hdc, hOldFont);
1984 ReleaseDC(infoPtr->hwndSelf, hdc);
1988 if (uView == LVS_ICON)
1990 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1991 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1992 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1993 Label.right = Label.left + labelSize.cx;
1994 Label.bottom = Label.top + infoPtr->nItemHeight;
1995 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1997 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1998 labelSize.cy /= infoPtr->ntmHeight;
1999 labelSize.cy = max(labelSize.cy, 1);
2000 labelSize.cy *= infoPtr->ntmHeight;
2002 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2004 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2006 Label.left = Icon.right;
2007 Label.top = Box.top;
2008 Label.right = min(Label.left + labelSize.cx, Label.right);
2009 Label.bottom = Label.top + infoPtr->nItemHeight;
2012 if (lprcLabel) *lprcLabel = Label;
2013 TRACE(" - label=%s\n", debugrect(&Label));
2016 /* Fix the Box if necessary */
2019 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2020 else *lprcBox = Box;
2022 TRACE(" - box=%s\n", debugrect(&Box));
2026 * DESCRIPTION: [INTERNAL]
2029 * [I] infoPtr : valid pointer to the listview structure
2030 * [I] nItem : item number
2031 * [O] lprcBox : ptr to Box rectangle
2036 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2038 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2039 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2040 POINT Position, Origin;
2043 LISTVIEW_GetOrigin(infoPtr, &Origin);
2044 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2046 /* Be smart and try to figure out the minimum we have to do */
2048 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2049 lvItem.mask |= LVIF_TEXT;
2050 lvItem.iItem = nItem;
2051 lvItem.iSubItem = 0;
2052 lvItem.pszText = szDispText;
2053 lvItem.cchTextMax = DISP_TEXT_SIZE;
2054 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2055 if (uView == LVS_ICON)
2057 lvItem.mask |= LVIF_STATE;
2058 lvItem.stateMask = LVIS_FOCUSED;
2059 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2061 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2063 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2069 * Returns the current icon position, and advances it along the top.
2070 * The returned position is not offset by Origin.
2073 * [I] infoPtr : valid pointer to the listview structure
2074 * [O] lpPos : will get the current icon position
2079 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2081 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2083 *lpPos = infoPtr->currIconPos;
2085 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2086 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2088 infoPtr->currIconPos.x = 0;
2089 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2095 * Returns the current icon position, and advances it down the left edge.
2096 * The returned position is not offset by Origin.
2099 * [I] infoPtr : valid pointer to the listview structure
2100 * [O] lpPos : will get the current icon position
2105 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2107 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2109 *lpPos = infoPtr->currIconPos;
2111 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2112 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2114 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2115 infoPtr->currIconPos.y = 0;
2121 * Moves an icon to the specified position.
2122 * It takes care of invalidating the item, etc.
2125 * [I] infoPtr : valid pointer to the listview structure
2126 * [I] nItem : the item to move
2127 * [I] lpPos : the new icon position
2128 * [I] isNew : flags the item as being new
2134 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2140 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2141 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2143 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2144 LISTVIEW_InvalidateItem(infoPtr, nItem);
2147 /* Allocating a POINTER for every item is too resource intensive,
2148 * so we'll keep the (x,y) in different arrays */
2149 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2150 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2152 LISTVIEW_InvalidateItem(infoPtr, nItem);
2159 * Arranges listview items in icon display mode.
2162 * [I] infoPtr : valid pointer to the listview structure
2163 * [I] nAlignCode : alignment code
2169 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2171 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2172 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2176 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2178 TRACE("nAlignCode=%d\n", nAlignCode);
2180 if (nAlignCode == LVA_DEFAULT)
2182 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2183 else nAlignCode = LVA_ALIGNTOP;
2188 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2189 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2190 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2191 default: return FALSE;
2194 infoPtr->bAutoarrange = TRUE;
2195 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2196 for (i = 0; i < infoPtr->nItemCount; i++)
2198 next_pos(infoPtr, &pos);
2199 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2207 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2210 * [I] infoPtr : valid pointer to the listview structure
2211 * [O] lprcView : bounding rectangle
2217 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2221 SetRectEmpty(lprcView);
2223 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2227 for (i = 0; i < infoPtr->nItemCount; i++)
2229 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2230 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2231 lprcView->right = max(lprcView->right, x);
2232 lprcView->bottom = max(lprcView->bottom, y);
2234 if (infoPtr->nItemCount > 0)
2236 lprcView->right += infoPtr->nItemWidth;
2237 lprcView->bottom += infoPtr->nItemHeight;
2242 y = LISTVIEW_GetCountPerColumn(infoPtr);
2243 x = infoPtr->nItemCount / y;
2244 if (infoPtr->nItemCount % y) x++;
2245 lprcView->right = x * infoPtr->nItemWidth;
2246 lprcView->bottom = y * infoPtr->nItemHeight;
2250 lprcView->right = infoPtr->nItemWidth;
2251 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2258 * Retrieves the bounding rectangle of all the items.
2261 * [I] infoPtr : valid pointer to the listview structure
2262 * [O] lprcView : bounding rectangle
2268 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2272 TRACE("(lprcView=%p)\n", lprcView);
2274 if (!lprcView) return FALSE;
2276 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2277 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2278 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2280 TRACE("lprcView=%s\n", debugrect(lprcView));
2287 * Retrieves the subitem pointer associated with the subitem index.
2290 * [I] hdpaSubItems : DPA handle for a specific item
2291 * [I] nSubItem : index of subitem
2294 * SUCCESS : subitem pointer
2297 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2299 SUBITEM_INFO *lpSubItem;
2302 /* we should binary search here if need be */
2303 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2305 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2306 if (lpSubItem->iSubItem == nSubItem)
2316 * Caclulates the desired item width.
2319 * [I] infoPtr : valid pointer to the listview structure
2322 * The desired item width.
2324 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2326 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2329 TRACE("uView=%d\n", uView);
2331 if (uView == LVS_ICON)
2332 nItemWidth = infoPtr->iconSpacing.cx;
2333 else if (uView == LVS_REPORT)
2337 if (infoPtr->hdpaColumns->nItemCount > 0)
2339 LISTVIEW_GetHeaderRect(infoPtr, infoPtr->hdpaColumns->nItemCount - 1, &rcHeader);
2340 nItemWidth = rcHeader.right;
2343 else /* LVS_SMALLICON, or LVS_LIST */
2347 for (i = 0; i < infoPtr->nItemCount; i++)
2348 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2350 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2351 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2353 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2356 return max(nItemWidth, 1);
2361 * Caclulates the desired item height.
2364 * [I] infoPtr : valid pointer to the listview structure
2367 * The desired item height.
2369 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2371 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2374 TRACE("uView=%d\n", uView);
2376 if (uView == LVS_ICON)
2377 nItemHeight = infoPtr->iconSpacing.cy;
2380 nItemHeight = infoPtr->ntmHeight;
2381 if (infoPtr->himlState)
2382 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2383 if (infoPtr->himlSmall)
2384 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2385 if (infoPtr->himlState || infoPtr->himlSmall)
2386 nItemHeight += HEIGHT_PADDING;
2389 return max(nItemHeight, 1);
2394 * Updates the width, and height of an item.
2397 * [I] infoPtr : valid pointer to the listview structure
2402 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2404 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2405 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2411 * Retrieves and saves important text metrics info for the current
2415 * [I] infoPtr : valid pointer to the listview structure
2418 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2420 HDC hdc = GetDC(infoPtr->hwndSelf);
2421 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2422 HFONT hOldFont = SelectObject(hdc, hFont);
2425 if (GetTextMetricsW(hdc, &tm))
2427 infoPtr->ntmHeight = tm.tmHeight;
2428 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2430 SelectObject(hdc, hOldFont);
2431 ReleaseDC(infoPtr->hwndSelf, hdc);
2433 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2438 * A compare function for ranges
2441 * [I] range1 : pointer to range 1;
2442 * [I] range2 : pointer to range 2;
2446 * > 0 : if range 1 > range 2
2447 * < 0 : if range 2 > range 1
2448 * = 0 : if range intersects range 2
2450 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2454 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2456 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2461 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2467 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2469 #define ranges_check(ranges, desc) do { } while(0)
2472 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2477 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2479 assert (ranges->hdpa->nItemCount >= 0);
2480 ranges_dump(ranges);
2481 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2482 if (ranges->hdpa->nItemCount > 0)
2483 assert (prev->lower >= 0 && prev->lower < prev->upper);
2484 for (i = 1; i < ranges->hdpa->nItemCount; i++)
2486 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2487 assert (prev->upper <= curr->lower);
2488 assert (curr->lower < curr->upper);
2491 TRACE("--- Done checking---\n");
2494 static RANGES ranges_create(int count)
2496 RANGES ranges = (RANGES)COMCTL32_Alloc(sizeof(struct tagRANGES));
2497 if (!ranges) return NULL;
2498 ranges->hdpa = DPA_Create(count);
2499 if (ranges->hdpa) return ranges;
2500 COMCTL32_Free(ranges);
2504 static void ranges_clear(RANGES ranges)
2508 for(i = 0; i < ranges->hdpa->nItemCount; i++)
2509 COMCTL32_Free(DPA_GetPtr(ranges->hdpa, i));
2510 DPA_DeleteAllPtrs(ranges->hdpa);
2514 static void ranges_destroy(RANGES ranges)
2516 if (!ranges) return;
2517 ranges_clear(ranges);
2518 DPA_Destroy(ranges->hdpa);
2519 COMCTL32_Free(ranges);
2522 static RANGES ranges_clone(RANGES ranges)
2527 if (!(clone = ranges_create(ranges->hdpa->nItemCount))) goto fail;
2529 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2531 RANGE *newrng = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2532 if (!newrng) goto fail;
2533 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2534 DPA_SetPtr(clone->hdpa, i, newrng);
2539 TRACE ("clone failed\n");
2540 ranges_destroy(clone);
2544 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2548 for (i = 0; i < sub->hdpa->nItemCount; i++)
2549 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2554 static void ranges_dump(RANGES ranges)
2558 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2559 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2562 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2564 RANGE srchrng = { nItem, nItem + 1 };
2566 TRACE("(nItem=%d)\n", nItem);
2567 ranges_check(ranges, "before contain");
2568 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2571 static INT ranges_itemcount(RANGES ranges)
2575 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2577 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2578 count += sel->upper - sel->lower;
2584 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2586 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2589 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2590 if (index == -1) return TRUE;
2592 for (; index < ranges->hdpa->nItemCount; index++)
2594 chkrng = DPA_GetPtr(ranges->hdpa, index);
2595 if (chkrng->lower >= nItem)
2596 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2597 if (chkrng->upper > nItem)
2598 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2603 static BOOL ranges_add(RANGES ranges, RANGE range)
2608 TRACE("(%s)\n", debugrange(&range));
2609 ranges_check(ranges, "before add");
2611 /* try find overlapping regions first */
2612 srchrgn.lower = range.lower - 1;
2613 srchrgn.upper = range.upper + 1;
2614 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2620 TRACE("Adding new range\n");
2622 /* create the brand new range to insert */
2623 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2624 if(!newrgn) goto fail;
2627 /* figure out where to insert it */
2628 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2629 TRACE("index=%d\n", index);
2630 if (index == -1) index = 0;
2632 /* and get it over with */
2633 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2635 COMCTL32_Free(newrgn);
2641 RANGE *chkrgn, *mrgrgn;
2642 INT fromindex, mergeindex;
2644 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2645 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2647 chkrgn->lower = min(range.lower, chkrgn->lower);
2648 chkrgn->upper = max(range.upper, chkrgn->upper);
2650 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2652 /* merge now common anges */
2654 srchrgn.lower = chkrgn->lower - 1;
2655 srchrgn.upper = chkrgn->upper + 1;
2659 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2660 if (mergeindex == -1) break;
2661 if (mergeindex == index)
2663 fromindex = index + 1;
2667 TRACE("Merge with index %i\n", mergeindex);
2669 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2670 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2671 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2672 COMCTL32_Free(mrgrgn);
2673 DPA_DeletePtr(ranges->hdpa, mergeindex);
2674 if (mergeindex < index) index --;
2678 ranges_check(ranges, "after add");
2682 ranges_check(ranges, "failed add");
2686 static BOOL ranges_del(RANGES ranges, RANGE range)
2691 TRACE("(%s)\n", debugrange(&range));
2692 ranges_check(ranges, "before del");
2694 /* we don't use DPAS_SORTED here, since we need *
2695 * to find the first overlapping range */
2696 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2699 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2701 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2703 /* case 1: Same range */
2704 if ( (chkrgn->upper == range.upper) &&
2705 (chkrgn->lower == range.lower) )
2707 DPA_DeletePtr(ranges->hdpa, index);
2710 /* case 2: engulf */
2711 else if ( (chkrgn->upper <= range.upper) &&
2712 (chkrgn->lower >= range.lower) )
2714 DPA_DeletePtr(ranges->hdpa, index);
2716 /* case 3: overlap upper */
2717 else if ( (chkrgn->upper <= range.upper) &&
2718 (chkrgn->lower < range.lower) )
2720 chkrgn->upper = range.lower;
2722 /* case 4: overlap lower */
2723 else if ( (chkrgn->upper > range.upper) &&
2724 (chkrgn->lower >= range.lower) )
2726 chkrgn->lower = range.upper;
2729 /* case 5: fully internal */
2732 RANGE tmprgn = *chkrgn, *newrgn;
2734 if (!(newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE)))) goto fail;
2735 newrgn->lower = chkrgn->lower;
2736 newrgn->upper = range.lower;
2737 chkrgn->lower = range.upper;
2738 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2740 COMCTL32_Free(newrgn);
2747 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2750 ranges_check(ranges, "after del");
2754 ranges_check(ranges, "failed del");
2760 * Removes all selection ranges
2763 * [I] infoPtr : valid pointer to the listview structure
2764 * [I] toSkip : item range to skip removing the selection
2770 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2779 lvItem.stateMask = LVIS_SELECTED;
2781 /* need to clone the DPA because callbacks can change it */
2782 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2783 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2784 while(iterator_next(&i))
2785 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2786 /* note that the iterator destructor will free the cloned range */
2787 iterator_destroy(&i);
2792 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2796 if (!(toSkip = ranges_create(1))) return FALSE;
2797 if (nItem != -1) ranges_additem(toSkip, nItem);
2798 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2799 ranges_destroy(toSkip);
2803 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2805 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2810 * Retrieves the number of items that are marked as selected.
2813 * [I] infoPtr : valid pointer to the listview structure
2816 * Number of items selected.
2818 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2820 INT nSelectedCount = 0;
2822 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2825 for (i = 0; i < infoPtr->nItemCount; i++)
2827 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2832 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2834 TRACE("nSelectedCount=%d\n", nSelectedCount);
2835 return nSelectedCount;
2840 * Manages the item focus.
2843 * [I] infoPtr : valid pointer to the listview structure
2844 * [I] nItem : item index
2847 * TRUE : focused item changed
2848 * FALSE : focused item has NOT changed
2850 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2852 INT oldFocus = infoPtr->nFocusedItem;
2855 if (nItem == infoPtr->nFocusedItem) return FALSE;
2857 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2858 lvItem.stateMask = LVIS_FOCUSED;
2859 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2861 return oldFocus != infoPtr->nFocusedItem;
2864 /* Helper function for LISTVIEW_ShiftIndices *only* */
2865 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2867 if (nShiftItem < nItem) return nShiftItem;
2869 if (nShiftItem > nItem) return nShiftItem + direction;
2871 if (direction > 0) return nShiftItem + direction;
2873 return min(nShiftItem, infoPtr->nItemCount - 1);
2878 * Updates the various indices after an item has been inserted or deleted.
2881 * [I] infoPtr : valid pointer to the listview structure
2882 * [I] nItem : item index
2883 * [I] direction : Direction of shift, +1 or -1.
2888 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2893 /* temporarily disable change notification while shifting items */
2894 bOldChange = infoPtr->bDoChangeNotify;
2895 infoPtr->bDoChangeNotify = FALSE;
2897 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2899 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2901 assert(abs(direction) == 1);
2903 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2905 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2906 if (nNewFocus != infoPtr->nFocusedItem)
2907 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2909 /* But we are not supposed to modify nHotItem! */
2911 infoPtr->bDoChangeNotify = bOldChange;
2917 * Adds a block of selections.
2920 * [I] infoPtr : valid pointer to the listview structure
2921 * [I] nItem : item index
2926 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2928 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2929 INT nLast = max(infoPtr->nSelectionMark, nItem);
2933 if (nFirst == -1) nFirst = nItem;
2935 item.state = LVIS_SELECTED;
2936 item.stateMask = LVIS_SELECTED;
2938 /* FIXME: this is not correct LVS_OWNERDATA
2939 * setting the item states individually will generate
2940 * a LVN_ITEMCHANGED notification for each one. Instead,
2941 * we have to send a LVN_ODSTATECHANGED notification.
2942 * See MSDN documentation for LVN_ITEMCHANGED.
2944 for (i = nFirst; i <= nLast; i++)
2945 LISTVIEW_SetItemState(infoPtr,i,&item);
2951 * Sets a single group selection.
2954 * [I] infoPtr : valid pointer to the listview structure
2955 * [I] nItem : item index
2960 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2962 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2967 if (!(selection = ranges_create(100))) return;
2969 item.state = LVIS_SELECTED;
2970 item.stateMask = LVIS_SELECTED;
2972 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2974 if (infoPtr->nSelectionMark == -1)
2976 infoPtr->nSelectionMark = nItem;
2977 ranges_additem(selection, nItem);
2983 sel.lower = min(infoPtr->nSelectionMark, nItem);
2984 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
2985 ranges_add(selection, sel);
2990 RECT rcItem, rcSel, rcSelMark;
2993 rcItem.left = LVIR_BOUNDS;
2994 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2995 rcSelMark.left = LVIR_BOUNDS;
2996 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2997 UnionRect(&rcSel, &rcItem, &rcSelMark);
2998 iterator_frameditems(&i, infoPtr, &rcSel);
2999 while(iterator_next(&i))
3001 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3002 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3004 iterator_destroy(&i);
3007 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3008 iterator_rangesitems(&i, selection);
3009 while(iterator_next(&i))
3010 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3011 /* this will also destroy the selection */
3012 iterator_destroy(&i);
3014 LISTVIEW_SetItemFocus(infoPtr, nItem);
3019 * Sets a single selection.
3022 * [I] infoPtr : valid pointer to the listview structure
3023 * [I] nItem : item index
3028 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3032 TRACE("nItem=%d\n", nItem);
3034 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3036 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3037 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3038 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3040 infoPtr->nSelectionMark = nItem;
3045 * Set selection(s) with keyboard.
3048 * [I] infoPtr : valid pointer to the listview structure
3049 * [I] nItem : item index
3052 * SUCCESS : TRUE (needs to be repainted)
3053 * FAILURE : FALSE (nothing has changed)
3055 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3057 /* FIXME: pass in the state */
3058 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3059 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3060 BOOL bResult = FALSE;
3062 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3064 if (infoPtr->dwStyle & LVS_SINGLESEL)
3067 LISTVIEW_SetSelection(infoPtr, nItem);
3074 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3078 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3083 LISTVIEW_SetSelection(infoPtr, nItem);
3086 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3089 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3096 * Called when the mouse is being actively tracked and has hovered for a specified
3100 * [I] infoPtr : valid pointer to the listview structure
3101 * [I] fwKeys : key indicator
3102 * [I] pts : mouse position
3105 * 0 if the message was processed, non-zero if there was an error
3108 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3109 * over the item for a certain period of time.
3112 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
3114 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3115 /* FIXME: select the item!!! */
3116 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3123 * Called whenever WM_MOUSEMOVE is received.
3126 * [I] infoPtr : valid pointer to the listview structure
3127 * [I] fwKeys : key indicator
3128 * [I] pts : mouse position
3131 * 0 if the message is processed, non-zero if there was an error
3133 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
3135 TRACKMOUSEEVENT trackinfo;
3137 /* see if we are supposed to be tracking mouse hovering */
3138 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3139 /* fill in the trackinfo struct */
3140 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3141 trackinfo.dwFlags = TME_QUERY;
3142 trackinfo.hwndTrack = infoPtr->hwndSelf;
3143 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3145 /* see if we are already tracking this hwnd */
3146 _TrackMouseEvent(&trackinfo);
3148 if(!(trackinfo.dwFlags & TME_HOVER)) {
3149 trackinfo.dwFlags = TME_HOVER;
3151 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3152 _TrackMouseEvent(&trackinfo);
3161 * Tests wheather the item is assignable to a list with style lStyle
3163 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3165 if ( (lpLVItem->mask & LVIF_TEXT) &&
3166 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3167 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3175 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3178 * [I] infoPtr : valid pointer to the listview structure
3179 * [I] lpLVItem : valid pointer to new item atttributes
3180 * [I] isNew : the item being set is being inserted
3181 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3182 * [O] bChanged : will be set to TRUE if the item really changed
3188 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3190 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3198 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3200 if (lpLVItem->mask == 0) return TRUE;
3202 if (infoPtr->dwStyle & LVS_OWNERDATA)
3204 /* a virtual listview we stores only selection and focus */
3205 if (lpLVItem->mask & ~LVIF_STATE)
3211 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3212 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3216 /* we need to get the lParam and state of the item */
3217 item.iItem = lpLVItem->iItem;
3218 item.iSubItem = lpLVItem->iSubItem;
3219 item.mask = LVIF_STATE | LVIF_PARAM;
3220 item.stateMask = ~0;
3223 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3225 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3226 /* determine what fields will change */
3227 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3228 uChanged |= LVIF_STATE;
3230 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3231 uChanged |= LVIF_IMAGE;
3233 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3234 uChanged |= LVIF_PARAM;
3236 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3237 uChanged |= LVIF_INDENT;
3239 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3240 uChanged |= LVIF_TEXT;
3242 TRACE("uChanged=0x%x\n", uChanged);
3243 if (!uChanged) return TRUE;
3246 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3247 nmlv.iItem = lpLVItem->iItem;
3248 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3249 nmlv.uOldState = item.state;
3250 nmlv.uChanged = uChanged;
3251 nmlv.lParam = item.lParam;
3253 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3254 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3256 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3257 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3260 /* copy information */
3261 if (lpLVItem->mask & LVIF_TEXT)
3262 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3264 if (lpLVItem->mask & LVIF_IMAGE)
3265 lpItem->hdr.iImage = lpLVItem->iImage;
3267 if (lpLVItem->mask & LVIF_PARAM)
3268 lpItem->lParam = lpLVItem->lParam;
3270 if (lpLVItem->mask & LVIF_INDENT)
3271 lpItem->iIndent = lpLVItem->iIndent;
3273 if (uChanged & LVIF_STATE)
3275 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3277 lpItem->state &= ~lpLVItem->stateMask;
3278 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3280 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3282 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3283 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3285 else if (lpLVItem->stateMask & LVIS_SELECTED)
3286 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3288 /* if we are asked to change focus, and we manage it, do it */
3289 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3291 if (lpLVItem->state & LVIS_FOCUSED)
3293 LISTVIEW_SetItemFocus(infoPtr, -1);
3294 infoPtr->nFocusedItem = lpLVItem->iItem;
3295 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3297 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3298 infoPtr->nFocusedItem = -1;
3302 /* if we're inserting the item, we're done */
3303 if (isNew) return TRUE;
3305 /* send LVN_ITEMCHANGED notification */
3306 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3307 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3314 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3317 * [I] infoPtr : valid pointer to the listview structure
3318 * [I] lpLVItem : valid pointer to new subitem atttributes
3319 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3320 * [O] bChanged : will be set to TRUE if the item really changed
3326 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3329 SUBITEM_INFO *lpSubItem;
3331 /* we do not support subitems for virtual listviews */
3332 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3334 /* set subitem only if column is present */
3335 if (lpLVItem->iSubItem >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3337 /* First do some sanity checks */
3338 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3339 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3341 /* get the subitem structure, and create it if not there */
3342 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3343 assert (hdpaSubItems);
3345 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3348 SUBITEM_INFO *tmpSubItem;
3351 lpSubItem = (SUBITEM_INFO *)COMCTL32_Alloc(sizeof(SUBITEM_INFO));
3352 if (!lpSubItem) return FALSE;
3353 /* we could binary search here, if need be...*/
3354 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3356 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3357 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3359 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3361 COMCTL32_Free(lpSubItem);
3364 lpSubItem->iSubItem = lpLVItem->iSubItem;
3368 if (lpLVItem->mask & LVIF_IMAGE)
3369 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3371 lpSubItem->hdr.iImage = lpLVItem->iImage;
3375 if (lpLVItem->mask & LVIF_TEXT)
3376 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3378 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3387 * Sets item attributes.
3390 * [I] infoPtr : valid pointer to the listview structure
3391 * [I] lpLVItem : new item atttributes
3392 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3398 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3400 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3401 LPWSTR pszText = NULL;
3402 BOOL bResult, bChanged = FALSE;
3404 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3406 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3409 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3410 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3412 pszText = lpLVItem->pszText;
3413 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3416 /* actually set the fields */
3417 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3419 if (lpLVItem->iSubItem)
3420 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3422 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3424 /* redraw item, if necessary */
3425 if (bChanged && !infoPtr->bIsDrawing)
3427 /* this little optimization eliminates some nasty flicker */
3428 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3429 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3430 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3432 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3437 textfreeT(lpLVItem->pszText, isW);
3438 ((LVITEMW *)lpLVItem)->pszText = pszText;
3446 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3449 * [I] infoPtr : valid pointer to the listview structure
3454 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3456 LONG lStyle = infoPtr->dwStyle;
3457 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3459 SCROLLINFO scrollInfo;
3461 scrollInfo.cbSize = sizeof(SCROLLINFO);
3462 scrollInfo.fMask = SIF_POS;
3464 if (uView == LVS_LIST)
3466 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3467 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3469 else if (uView == LVS_REPORT)
3471 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3472 nItem = scrollInfo.nPos;
3476 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3477 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3480 TRACE("nItem=%d\n", nItem);
3488 * Erases the background of the given rectangle
3491 * [I] infoPtr : valid pointer to the listview structure
3492 * [I] hdc : device context handle
3493 * [I] lprcBox : clipping rectangle
3499 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3501 if (!infoPtr->hBkBrush) return FALSE;
3503 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3505 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3513 * [I] infoPtr : valid pointer to the listview structure
3514 * [I] hdc : device context handle
3515 * [I] nItem : item index
3516 * [I] nSubItem : subitem index
3517 * [I] pos : item position in client coordinates
3518 * [I] cdmode : custom draw mode
3524 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3526 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3527 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3528 WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3529 DWORD cditemmode = CDRF_DODEFAULT;
3530 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3531 NMLVCUSTOMDRAW nmlvcd;
3535 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3537 /* get information needed for drawing the item */
3538 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3539 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3540 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3541 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3542 lvItem.iItem = nItem;
3543 lvItem.iSubItem = nSubItem;
3546 lvItem.cchTextMax = DISP_TEXT_SIZE;
3547 lvItem.pszText = szDispText;
3548 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3549 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3550 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3551 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3552 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3554 /* now check if we need to update the focus rectangle */
3555 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3557 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3558 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3559 OffsetRect(&rcBox, pos.x, pos.y);
3560 OffsetRect(&rcState, pos.x, pos.y);
3561 OffsetRect(&rcIcon, pos.x, pos.y);
3562 OffsetRect(&rcLabel, pos.x, pos.y);
3563 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3564 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3566 /* fill in the custom draw structure */
3567 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3569 if (cdmode & CDRF_NOTIFYITEMDRAW)
3570 cditemmode = notify_prepaint (infoPtr, hdc, &nmlvcd);
3571 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3573 /* in full row select, subitems, will just use main item's colors */
3574 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3575 nmlvcd.clrTextBk = CLR_NONE;
3578 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3580 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3583 TRACE("uStateImage=%d\n", uStateImage);
3584 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3589 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3590 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3592 TRACE("iImage=%d\n", lvItem.iImage);
3593 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3594 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3597 /* Don't bother painting item being edited */
3598 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3600 /* draw the selection background, if we're drawing the main item */
3604 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3605 rcSelect.right = rcBox.right;
3607 if (nmlvcd.clrTextBk != CLR_NONE)
3608 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3609 if(lprcFocus) *lprcFocus = rcSelect;
3612 /* figure out the text drawing flags */
3613 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3614 if (uView == LVS_ICON)
3615 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3618 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3620 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3621 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3622 default: uFormat |= DT_LEFT;
3625 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3627 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3628 else rcLabel.left += LABEL_HOR_PADDING;
3630 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3631 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3634 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3635 notify_postpaint(infoPtr, &nmlvcd);
3641 * Draws listview items when in owner draw mode.
3644 * [I] infoPtr : valid pointer to the listview structure
3645 * [I] hdc : device context handle
3650 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3652 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3653 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3654 DWORD cditemmode = CDRF_DODEFAULT;
3655 NMLVCUSTOMDRAW nmlvcd;
3656 POINT Origin, Position;
3662 ZeroMemory(&dis, sizeof(dis));
3664 /* Get scroll info once before loop */
3665 LISTVIEW_GetOrigin(infoPtr, &Origin);
3667 /* iterate through the invalidated rows */
3668 while(iterator_next(i))
3670 item.iItem = i->nItem;
3672 item.mask = LVIF_PARAM | LVIF_STATE;
3673 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3674 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3676 dis.CtlType = ODT_LISTVIEW;
3678 dis.itemID = item.iItem;
3679 dis.itemAction = ODA_DRAWENTIRE;
3681 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3682 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3683 dis.hwndItem = infoPtr->hwndSelf;
3685 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3686 dis.rcItem.left = Position.x + Origin.x;
3687 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3688 dis.rcItem.top = Position.y + Origin.y;
3689 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3690 dis.itemData = item.lParam;
3692 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3694 if (cdmode & CDRF_NOTIFYITEMDRAW)
3696 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3697 cditemmode = notify_prepaint (infoPtr, hdc, &nmlvcd);
3700 if (!(cditemmode & CDRF_SKIPDEFAULT))
3701 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3703 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3704 notify_postpaint(infoPtr, &nmlvcd);
3710 * Draws listview items when in report display mode.
3713 * [I] infoPtr : valid pointer to the listview structure
3714 * [I] hdc : device context handle
3715 * [I] cdmode : custom draw mode
3720 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3723 RECT rcClip, rcItem;
3724 POINT Origin, Position;
3730 /* figure out what to draw */
3731 rgntype = GetClipBox(hdc, &rcClip);
3732 if (rgntype == NULLREGION) return;
3734 /* Get scroll info once before loop */
3735 LISTVIEW_GetOrigin(infoPtr, &Origin);
3737 /* narrow down the columns we need to paint */
3738 for(colRange.lower = 0; colRange.lower < infoPtr->hdpaColumns->nItemCount; colRange.lower++)
3740 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3741 if (rcItem.right + Origin.x >= rcClip.left) break;
3743 for(colRange.upper = infoPtr->hdpaColumns->nItemCount; colRange.upper > 0; colRange.upper--)
3745 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3746 if (rcItem.left + Origin.x < rcClip.right) break;
3748 iterator_rangeitems(&j, colRange);
3750 /* in full row select, we _have_ to draw the main item */
3751 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3754 /* iterate through the invalidated rows */
3755 while(iterator_next(i))
3757 /* iterate through the invalidated columns */
3758 while(iterator_next(&j))
3760 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3761 Position.x += Origin.x;
3762 Position.y += Origin.y;
3764 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3766 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3768 rcItem.bottom = infoPtr->nItemHeight;
3769 OffsetRect(&rcItem, Position.x, Position.y);
3770 if (!RectVisible(hdc, &rcItem)) continue;
3773 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3776 iterator_destroy(&j);
3781 * Draws listview items when in list display mode.
3784 * [I] infoPtr : valid pointer to the listview structure
3785 * [I] hdc : device context handle
3786 * [I] cdmode : custom draw mode
3791 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3793 POINT Origin, Position;
3795 /* Get scroll info once before loop */
3796 LISTVIEW_GetOrigin(infoPtr, &Origin);
3798 while(iterator_prev(i))
3800 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3801 Position.x += Origin.x;
3802 Position.y += Origin.y;
3804 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3811 * Draws listview items.
3814 * [I] infoPtr : valid pointer to the listview structure
3815 * [I] hdc : device context handle
3820 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3822 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3823 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3824 NMLVCUSTOMDRAW nmlvcd;
3831 LISTVIEW_DUMP(infoPtr);
3833 infoPtr->bIsDrawing = TRUE;
3835 /* save dc values we're gonna trash while drawing */
3836 hOldFont = SelectObject(hdc, infoPtr->hFont);
3837 oldBkMode = GetBkMode(hdc);
3838 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3839 oldTextColor = GetTextColor(hdc);
3841 oldClrTextBk = infoPtr->clrTextBk;
3842 oldClrText = infoPtr->clrText;
3844 GetClientRect(infoPtr->hwndSelf, &rcClient);
3845 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3846 cdmode = notify_prepaint(infoPtr, hdc, &nmlvcd);
3847 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3849 /* Use these colors to draw the items */
3850 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3851 infoPtr->clrText = nmlvcd.clrText;
3853 /* nothing to draw */
3854 if(infoPtr->nItemCount == 0) goto enddraw;
3856 /* figure out what we need to draw */
3857 iterator_visibleitems(&i, infoPtr, hdc);
3859 /* send cache hint notification */
3860 if (infoPtr->dwStyle & LVS_OWNERDATA)
3862 RANGE range = iterator_range(&i);
3865 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3866 nmlv.iFrom = range.lower;
3867 nmlv.iTo = range.upper - 1;
3868 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3871 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3872 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3875 if (uView == LVS_REPORT)
3876 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3877 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3878 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3880 /* if we have a focus rect, draw it */
3881 if (infoPtr->bFocus)
3882 DrawFocusRect(hdc, &infoPtr->rcFocus);
3884 iterator_destroy(&i);
3887 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3888 notify_postpaint(infoPtr, &nmlvcd);
3890 infoPtr->clrTextBk = oldClrTextBk;
3891 infoPtr->clrText = oldClrText;
3893 SelectObject(hdc, hOldFont);
3894 SetBkMode(hdc, oldBkMode);
3895 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3896 SetTextColor(hdc, oldTextColor);
3897 infoPtr->bIsDrawing = FALSE;
3903 * Calculates the approximate width and height of a given number of items.
3906 * [I] infoPtr : valid pointer to the listview structure
3907 * [I] nItemCount : number of items
3908 * [I] wWidth : width
3909 * [I] wHeight : height
3912 * Returns a DWORD. The width in the low word and the height in high word.
3914 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3915 WORD wWidth, WORD wHeight)
3917 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3918 INT nItemCountPerColumn = 1;
3919 INT nColumnCount = 0;
3920 DWORD dwViewRect = 0;
3922 if (nItemCount == -1)
3923 nItemCount = infoPtr->nItemCount;
3925 if (uView == LVS_LIST)
3927 if (wHeight == 0xFFFF)
3929 /* use current height */
3930 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3933 if (wHeight < infoPtr->nItemHeight)
3934 wHeight = infoPtr->nItemHeight;
3938 if (infoPtr->nItemHeight > 0)
3940 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3941 if (nItemCountPerColumn == 0)
3942 nItemCountPerColumn = 1;
3944 if (nItemCount % nItemCountPerColumn != 0)
3945 nColumnCount = nItemCount / nItemCountPerColumn;
3947 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3951 /* Microsoft padding magic */
3952 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3953 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3955 dwViewRect = MAKELONG(wWidth, wHeight);
3957 else if (uView == LVS_REPORT)
3958 FIXME("uView == LVS_REPORT: not implemented\n");
3959 else if (uView == LVS_SMALLICON)
3960 FIXME("uView == LVS_SMALLICON: not implemented\n");
3961 else if (uView == LVS_ICON)
3962 FIXME("uView == LVS_ICON: not implemented\n");
3967 /* << LISTVIEW_CreateDragImage >> */
3972 * Removes all listview items and subitems.
3975 * [I] infoPtr : valid pointer to the listview structure
3981 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3984 HDPA hdpaSubItems = NULL;
3991 /* we do it directly, to avoid notifications */
3992 ranges_clear(infoPtr->selectionRanges);
3993 infoPtr->nSelectionMark = -1;
3994 infoPtr->nFocusedItem = -1;
3995 SetRectEmpty(&infoPtr->rcFocus);
3996 /* But we are supposed to leave nHotItem as is! */
3999 /* send LVN_DELETEALLITEMS notification */
4000 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4002 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4004 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4006 /* send LVN_DELETEITEM notification, if not supressed */
4007 if (!bSuppress) notify_deleteitem(infoPtr, i);
4008 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4010 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4011 for (j = 0; j < hdpaSubItems->nItemCount; j++)
4013 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4014 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
4015 COMCTL32_Free(hdrItem);
4017 DPA_Destroy(hdpaSubItems);
4018 DPA_DeletePtr(infoPtr->hdpaItems, i);
4020 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4021 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4022 infoPtr->nItemCount --;
4025 LISTVIEW_UpdateScroll(infoPtr);
4027 LISTVIEW_InvalidateList(infoPtr);
4034 * Scrolls, and updates the columns, when a column is changing width.
4037 * [I] infoPtr : valid pointer to the listview structure
4038 * [I] nColumn : column to scroll
4039 * [I] dx : amount of scroll, in pixels
4044 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4046 COLUMN_INFO *lpColumnInfo;
4050 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, infoPtr->hdpaColumns->nItemCount - 1));
4051 rcCol = lpColumnInfo->rcHeader;
4052 if (nColumn >= infoPtr->hdpaColumns->nItemCount)
4053 rcCol.left = rcCol.right;
4055 /* ajust the other columns */
4056 for (nCol = nColumn; nCol < infoPtr->hdpaColumns->nItemCount; nCol++)
4058 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4059 lpColumnInfo->rcHeader.left += dx;
4060 lpColumnInfo->rcHeader.right += dx;
4063 /* do not update screen if not in report mode */
4064 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4066 /* if we have a focus, must first erase the focus rect */
4067 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4069 /* Need to reset the item width when inserting a new column */
4070 infoPtr->nItemWidth += dx;
4072 LISTVIEW_UpdateScroll(infoPtr);
4074 /* scroll to cover the deleted column, and invalidate for redraw */
4075 rcOld = infoPtr->rcList;
4076 rcOld.left = rcCol.left;
4077 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4079 /* we can restore focus now */
4080 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4085 * Removes a column from the listview control.
4088 * [I] infoPtr : valid pointer to the listview structure
4089 * [I] nColumn : column index
4095 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4099 TRACE("nColumn=%d\n", nColumn);
4101 if (nColumn < 0 || infoPtr->hdpaColumns->nItemCount == 0
4102 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
4104 /* While the MSDN specifically says that column zero should not be deleted,
4105 it does in fact work on WinNT, and at least one app depends on it. On
4106 WinNT, deleting column zero deletes the last column of items but the
4107 first header. Since no app will ever depend on that bizarre behavior,
4108 we just delete the last column including the header.
4111 nColumn = infoPtr->hdpaColumns->nItemCount - 1;
4113 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4115 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4118 COMCTL32_Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4119 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4121 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4123 SUBITEM_INFO *lpSubItem, *lpDelItem;
4125 INT nItem, nSubItem, i;
4128 return LISTVIEW_DeleteAllItems(infoPtr);
4130 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4132 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4135 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4137 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4138 if (lpSubItem->iSubItem == nColumn)
4141 lpDelItem = lpSubItem;
4143 else if (lpSubItem->iSubItem > nColumn)
4145 lpSubItem->iSubItem--;
4149 /* if we found our subitem, zapp it */
4153 if (is_textW(lpDelItem->hdr.pszText))
4154 COMCTL32_Free(lpDelItem->hdr.pszText);
4157 COMCTL32_Free(lpDelItem);
4159 /* free dpa memory */
4160 DPA_DeletePtr(hdpaSubItems, nSubItem);
4165 /* update the other column info */
4166 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4173 * Invalidates the listview after an item's insertion or deletion.
4176 * [I] infoPtr : valid pointer to the listview structure
4177 * [I] nItem : item index
4178 * [I] dir : -1 if deleting, 1 if inserting
4183 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4185 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4186 INT nPerCol, nItemCol, nItemRow;
4190 /* if we don't refresh, what's the point of scrolling? */
4191 if (!is_redrawing(infoPtr)) return;
4193 assert (abs(dir) == 1);
4195 /* arrange icons if autoarrange is on */
4196 if (is_autoarrange(infoPtr))
4198 BOOL arrange = TRUE;
4199 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4200 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4201 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4204 /* scrollbars need updating */
4205 LISTVIEW_UpdateScroll(infoPtr);
4207 /* figure out the item's position */
4208 if (uView == LVS_REPORT)
4209 nPerCol = infoPtr->nItemCount + 1;
4210 else if (uView == LVS_LIST)
4211 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4212 else /* LVS_ICON, or LVS_SMALLICON */
4215 nItemCol = nItem / nPerCol;
4216 nItemRow = nItem % nPerCol;
4217 LISTVIEW_GetOrigin(infoPtr, &Origin);
4219 /* move the items below up a slot */
4220 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4221 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4222 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4223 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4224 OffsetRect(&rcScroll, Origin.x, Origin.y);
4225 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4226 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4228 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4229 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4230 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4233 /* report has only that column, so we're done */
4234 if (uView == LVS_REPORT) return;
4236 /* now for LISTs, we have to deal with the columns to the right */
4237 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4239 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4240 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4241 OffsetRect(&rcScroll, Origin.x, Origin.y);
4242 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4243 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4244 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4249 * Removes an item from the listview control.
4252 * [I] infoPtr : valid pointer to the listview structure
4253 * [I] nItem : item index
4259 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4261 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4264 TRACE("(nItem=%d)\n", nItem);
4266 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4268 /* remove selection, and focus */
4270 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4271 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4273 /* send LVN_DELETEITEM notification. */
4274 notify_deleteitem(infoPtr, nItem);
4276 /* we need to do this here, because we'll be deleting stuff */
4277 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4278 LISTVIEW_InvalidateItem(infoPtr, nItem);
4280 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4286 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4287 for (i = 0; i < hdpaSubItems->nItemCount; i++)
4289 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4290 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
4291 COMCTL32_Free(hdrItem);
4293 DPA_Destroy(hdpaSubItems);
4296 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4298 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4299 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4302 infoPtr->nItemCount--;
4303 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4305 /* now is the invalidation fun */
4306 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4313 * Callback implementation for editlabel control
4316 * [I] infoPtr : valid pointer to the listview structure
4317 * [I] pszText : modified text
4318 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4324 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4326 NMLVDISPINFOW dispInfo;
4328 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4330 ZeroMemory(&dispInfo, sizeof(dispInfo));
4331 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4332 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4333 dispInfo.item.iSubItem = 0;
4334 dispInfo.item.stateMask = ~0;
4335 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4336 /* add the text from the edit in */
4337 dispInfo.item.mask |= LVIF_TEXT;
4338 dispInfo.item.pszText = pszText;
4339 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4341 /* Do we need to update the Item Text */
4342 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4343 if (!pszText) return TRUE;
4345 ZeroMemory(&dispInfo, sizeof(dispInfo));
4346 dispInfo.item.mask = LVIF_TEXT;
4347 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4348 dispInfo.item.iSubItem = 0;
4349 dispInfo.item.pszText = pszText;
4350 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4351 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4356 * Begin in place editing of specified list view item
4359 * [I] infoPtr : valid pointer to the listview structure
4360 * [I] nItem : item index
4361 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4367 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4369 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4370 NMLVDISPINFOW dispInfo;
4373 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4375 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4376 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4378 infoPtr->nEditLabelItem = nItem;
4380 /* Is the EditBox still there, if so remove it */
4381 if(infoPtr->hwndEdit != 0)
4383 SetFocus(infoPtr->hwndSelf);
4384 infoPtr->hwndEdit = 0;
4387 LISTVIEW_SetSelection(infoPtr, nItem);
4388 LISTVIEW_SetItemFocus(infoPtr, nItem);
4389 LISTVIEW_InvalidateItem(infoPtr, nItem);
4391 rect.left = LVIR_LABEL;
4392 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4394 ZeroMemory(&dispInfo, sizeof(dispInfo));
4395 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4396 dispInfo.item.iItem = nItem;
4397 dispInfo.item.iSubItem = 0;
4398 dispInfo.item.stateMask = ~0;
4399 dispInfo.item.pszText = szDispText;
4400 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4401 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4403 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4404 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4405 if (!infoPtr->hwndEdit) return 0;
4407 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4409 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4410 infoPtr->hwndEdit = 0;
4414 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4415 SetFocus(infoPtr->hwndEdit);
4416 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4417 return infoPtr->hwndEdit;
4423 * Ensures the specified item is visible, scrolling into view if necessary.
4426 * [I] infoPtr : valid pointer to the listview structure
4427 * [I] nItem : item index
4428 * [I] bPartial : partially or entirely visible
4434 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4436 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4437 INT nScrollPosHeight = 0;
4438 INT nScrollPosWidth = 0;
4439 INT nHorzAdjust = 0;
4440 INT nVertAdjust = 0;
4443 RECT rcItem, rcTemp;
4445 rcItem.left = LVIR_BOUNDS;
4446 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4448 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4450 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4452 /* scroll left/right, but in LVS_REPORT mode */
4453 if (uView == LVS_LIST)
4454 nScrollPosWidth = infoPtr->nItemWidth;
4455 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4456 nScrollPosWidth = 1;
4458 if (rcItem.left < infoPtr->rcList.left)
4461 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4466 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4470 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4472 /* scroll up/down, but not in LVS_LIST mode */
4473 if (uView == LVS_REPORT)
4474 nScrollPosHeight = infoPtr->nItemHeight;
4475 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4476 nScrollPosHeight = 1;
4478 if (rcItem.top < infoPtr->rcList.top)
4481 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4486 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4490 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4492 if (nScrollPosWidth)
4494 INT diff = nHorzDiff / nScrollPosWidth;
4495 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4496 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4499 if (nScrollPosHeight)
4501 INT diff = nVertDiff / nScrollPosHeight;
4502 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4503 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4511 * Searches for an item with specific characteristics.
4514 * [I] hwnd : window handle
4515 * [I] nStart : base item index
4516 * [I] lpFindInfo : item information to look for
4519 * SUCCESS : index of item
4522 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4523 const LVFINDINFOW *lpFindInfo)
4525 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4526 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4527 BOOL bWrap = FALSE, bNearest = FALSE;
4528 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4529 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4530 POINT Position, Destination;
4533 if (!lpFindInfo || nItem < 0) return -1;
4536 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4538 lvItem.mask |= LVIF_TEXT;
4539 lvItem.pszText = szDispText;
4540 lvItem.cchTextMax = DISP_TEXT_SIZE;
4543 if (lpFindInfo->flags & LVFI_WRAP)
4546 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4547 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4552 LISTVIEW_GetOrigin(infoPtr, &Origin);
4553 Destination.x = lpFindInfo->pt.x - Origin.x;
4554 Destination.y = lpFindInfo->pt.y - Origin.y;
4555 switch(lpFindInfo->vkDirection)
4557 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4558 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4559 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4560 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4561 case VK_HOME: Destination.x = Destination.y = 0; break;
4562 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4563 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4565 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4566 Destination.x = rcArea.right;
4567 Destination.y = rcArea.bottom;
4569 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4574 /* if LVFI_PARAM is specified, all other flags are ignored */
4575 if (lpFindInfo->flags & LVFI_PARAM)
4577 lvItem.mask |= LVIF_PARAM;
4579 lvItem.mask &= ~LVIF_TEXT;
4583 for (; nItem < nLast; nItem++)
4585 lvItem.iItem = nItem;
4586 lvItem.iSubItem = 0;
4587 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4589 if (lvItem.mask & LVIF_PARAM)
4591 if (lpFindInfo->lParam == lvItem.lParam)
4597 if (lvItem.mask & LVIF_TEXT)
4599 if (lpFindInfo->flags & LVFI_PARTIAL)
4601 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4605 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4609 if (!bNearest) return nItem;
4611 /* This is very inefficient. To do a good job here,
4612 * we need a sorted array of (x,y) item positions */
4613 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4615 /* compute the distance^2 to the destination */
4616 xdist = Destination.x - Position.x;
4617 ydist = Destination.y - Position.y;
4618 dist = xdist * xdist + ydist * ydist;
4620 /* remember the distance, and item if it's closer */
4624 nNearestItem = nItem;
4631 nLast = min(nStart + 1, infoPtr->nItemCount);
4636 return nNearestItem;
4641 * Searches for an item with specific characteristics.
4644 * [I] hwnd : window handle
4645 * [I] nStart : base item index
4646 * [I] lpFindInfo : item information to look for
4649 * SUCCESS : index of item
4652 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4653 const LVFINDINFOA *lpFindInfo)
4655 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4659 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4660 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4661 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4662 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4668 * Retrieves the background image of the listview control.
4671 * [I] infoPtr : valid pointer to the listview structure
4672 * [O] lpBkImage : background image attributes
4678 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4680 /* FIXME (listview, "empty stub!\n"); */
4686 * Retrieves column attributes.
4689 * [I] infoPtr : valid pointer to the listview structure
4690 * [I] nColumn : column index
4691 * [IO] lpColumn : column information
4692 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4693 * otherwise it is in fact a LPLVCOLUMNA
4699 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4701 COLUMN_INFO *lpColumnInfo;
4704 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
4705 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4707 /* initialize memory */
4708 ZeroMemory(&hdi, sizeof(hdi));
4710 if (lpColumn->mask & LVCF_TEXT)
4712 hdi.mask |= HDI_TEXT;
4713 hdi.pszText = lpColumn->pszText;
4714 hdi.cchTextMax = lpColumn->cchTextMax;
4717 if (lpColumn->mask & LVCF_IMAGE)
4718 hdi.mask |= HDI_IMAGE;
4720 if (lpColumn->mask & LVCF_ORDER)
4721 hdi.mask |= HDI_ORDER;
4723 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4725 if (lpColumn->mask & LVCF_FMT)
4726 lpColumn->fmt = lpColumnInfo->fmt;
4728 if (lpColumn->mask & LVCF_WIDTH)
4729 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4731 if (lpColumn->mask & LVCF_IMAGE)
4732 lpColumn->iImage = hdi.iImage;
4734 if (lpColumn->mask & LVCF_ORDER)
4735 lpColumn->iOrder = hdi.iOrder;
4741 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4748 /* FIXME: little hack */
4749 for (i = 0; i < iCount; i++)
4757 * Retrieves the column width.
4760 * [I] infoPtr : valid pointer to the listview structure
4761 * [I] int : column index
4764 * SUCCESS : column width
4767 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4769 INT nColumnWidth = 0;
4772 TRACE("nColumn=%d\n", nColumn);
4774 /* we have a 'column' in LIST and REPORT mode only */
4775 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4778 nColumnWidth = infoPtr->nItemWidth;
4781 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return 0;
4782 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4783 nColumnWidth = rcHeader.right - rcHeader.left;
4787 TRACE("nColumnWidth=%d\n", nColumnWidth);
4788 return nColumnWidth;
4793 * In list or report display mode, retrieves the number of items that can fit
4794 * vertically in the visible area. In icon or small icon display mode,
4795 * retrieves the total number of visible items.
4798 * [I] infoPtr : valid pointer to the listview structure
4801 * Number of fully visible items.
4803 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4805 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4809 return infoPtr->nItemCount;
4811 return LISTVIEW_GetCountPerColumn(infoPtr);
4813 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4821 * Retrieves an image list handle.
4824 * [I] infoPtr : valid pointer to the listview structure
4825 * [I] nImageList : image list identifier
4828 * SUCCESS : image list handle
4831 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4835 case LVSIL_NORMAL: return infoPtr->himlNormal;
4836 case LVSIL_SMALL: return infoPtr->himlSmall;
4837 case LVSIL_STATE: return infoPtr->himlState;
4842 /* LISTVIEW_GetISearchString */
4846 * Retrieves item attributes.
4849 * [I] hwnd : window handle
4850 * [IO] lpLVItem : item info
4851 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4852 * if FALSE, the lpLVItem is a LPLVITEMA.
4855 * This is the internal 'GetItem' interface -- it tries to
4856 * be smart, and avoids text copies, if possible, by modifing
4857 * lpLVItem->pszText to point to the text string. Please note
4858 * that this is not always possible (e.g. OWNERDATA), so on
4859 * entry you *must* supply valid values for pszText, and cchTextMax.
4860 * The only difference to the documented interface is that upon
4861 * return, you should use *only* the lpLVItem->pszText, rather than
4862 * the buffer pointer you provided on input. Most code already does
4863 * that, so it's not a problem.
4864 * For the two cases when the text must be copied (that is,
4865 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4871 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4873 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
4874 NMLVDISPINFOW dispInfo;
4879 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4881 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4884 if (lpLVItem->mask == 0) return TRUE;
4886 /* a quick optimization if all we're asked is the focus state
4887 * these queries are worth optimising since they are common,
4888 * and can be answered in constant time, without the heavy accesses */
4889 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
4890 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4892 lpLVItem->state = 0;
4893 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4894 lpLVItem->state |= LVIS_FOCUSED;
4898 ZeroMemory(&dispInfo, sizeof(dispInfo));
4900 /* if the app stores all the data, handle it separately */
4901 if (infoPtr->dwStyle & LVS_OWNERDATA)
4903 dispInfo.item.state = 0;
4905 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
4906 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
4908 /* NOTE: copy only fields which we _know_ are initialized, some apps
4909 * depend on the uninitialized fields being 0 */
4910 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
4911 dispInfo.item.iItem = lpLVItem->iItem;
4912 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4913 if (lpLVItem->mask & LVIF_TEXT)
4915 dispInfo.item.pszText = lpLVItem->pszText;
4916 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4918 if (lpLVItem->mask & LVIF_STATE)
4919 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4920 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4921 dispInfo.item.stateMask = lpLVItem->stateMask;
4922 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
4924 /* full size structure expected - _WIN32IE >= 0x560 */
4925 *lpLVItem = dispInfo.item;
4927 else if (lpLVItem->mask & LVIF_INDENT)
4929 /* indent member expected - _WIN32IE >= 0x300 */
4930 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
4934 /* minimal structure expected */
4935 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
4937 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4940 /* make sure lParam is zeroed out */
4941 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
4943 /* we store only a little state, so if we're not asked, we're done */
4944 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4946 /* if focus is handled by us, report it */
4947 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4949 lpLVItem->state &= ~LVIS_FOCUSED;
4950 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4951 lpLVItem->state |= LVIS_FOCUSED;
4954 /* and do the same for selection, if we handle it */
4955 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4957 lpLVItem->state &= ~LVIS_SELECTED;
4958 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4959 lpLVItem->state |= LVIS_SELECTED;
4965 /* find the item and subitem structures before we proceed */
4966 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4967 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4970 if (lpLVItem->iSubItem)
4972 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4973 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
4976 pItemHdr = &lpItem->hdr;
4978 /* Do we need to query the state from the app? */
4979 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4981 dispInfo.item.mask |= LVIF_STATE;
4982 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4985 /* Do we need to enquire about the image? */
4986 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK)
4987 dispInfo.item.mask |= LVIF_IMAGE;
4989 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
4990 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4992 dispInfo.item.mask |= LVIF_TEXT;
4993 dispInfo.item.pszText = lpLVItem->pszText;
4994 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4995 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4996 *dispInfo.item.pszText = '\0';
4999 /* If we don't have all the requested info, query the application */
5000 if (dispInfo.item.mask != 0)
5002 dispInfo.item.iItem = lpLVItem->iItem;
5003 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5004 dispInfo.item.lParam = lpItem->lParam;
5005 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5006 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5009 /* we should not store values for subitems */
5010 if (lpLVItem->iSubItem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5012 /* Now, handle the iImage field */
5013 if (dispInfo.item.mask & LVIF_IMAGE)
5015 lpLVItem->iImage = dispInfo.item.iImage;
5016 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5017 pItemHdr->iImage = dispInfo.item.iImage;
5019 else if (lpLVItem->mask & LVIF_IMAGE)
5020 lpLVItem->iImage = pItemHdr->iImage;
5022 /* The pszText field */
5023 if (dispInfo.item.mask & LVIF_TEXT)
5025 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5026 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5028 lpLVItem->pszText = dispInfo.item.pszText;
5030 else if (lpLVItem->mask & LVIF_TEXT)
5032 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5033 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5036 /* if this is a subitem, we're done */
5037 if (lpLVItem->iSubItem) return TRUE;
5039 /* Next is the lParam field */
5040 if (dispInfo.item.mask & LVIF_PARAM)
5042 lpLVItem->lParam = dispInfo.item.lParam;
5043 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5044 lpItem->lParam = dispInfo.item.lParam;
5046 else if (lpLVItem->mask & LVIF_PARAM)
5047 lpLVItem->lParam = lpItem->lParam;
5049 /* ... the state field (this one is different due to uCallbackmask) */
5050 if (lpLVItem->mask & LVIF_STATE)
5052 lpLVItem->state = lpItem->state;
5053 if (dispInfo.item.mask & LVIF_STATE)
5055 lpLVItem->state &= ~dispInfo.item.stateMask;
5056 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5058 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5060 lpLVItem->state &= ~LVIS_FOCUSED;
5061 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5062 lpLVItem->state |= LVIS_FOCUSED;
5064 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5066 lpLVItem->state &= ~LVIS_SELECTED;
5067 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5068 lpLVItem->state |= LVIS_SELECTED;
5072 /* and last, but not least, the indent field */
5073 if (lpLVItem->mask & LVIF_INDENT)
5074 lpLVItem->iIndent = lpItem->iIndent;
5081 * Retrieves item attributes.
5084 * [I] hwnd : window handle
5085 * [IO] lpLVItem : item info
5086 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5087 * if FALSE, the lpLVItem is a LPLVITEMA.
5090 * This is the external 'GetItem' interface -- it properly copies
5091 * the text in the provided buffer.
5097 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5102 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5105 pszText = lpLVItem->pszText;
5106 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5107 if (bResult && lpLVItem->pszText != pszText)
5108 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5109 lpLVItem->pszText = pszText;
5117 * Retrieves the position (upper-left) of the listview control item.
5118 * Note that for LVS_ICON style, the upper-left is that of the icon
5119 * and not the bounding box.
5122 * [I] infoPtr : valid pointer to the listview structure
5123 * [I] nItem : item index
5124 * [O] lpptPosition : coordinate information
5130 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5132 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5135 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5137 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5139 LISTVIEW_GetOrigin(infoPtr, &Origin);
5140 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5142 if (uView == LVS_ICON)
5144 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5145 lpptPosition->y += ICON_TOP_PADDING;
5147 lpptPosition->x += Origin.x;
5148 lpptPosition->y += Origin.y;
5150 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5157 * Retrieves the bounding rectangle for a listview control item.
5160 * [I] infoPtr : valid pointer to the listview structure
5161 * [I] nItem : item index
5162 * [IO] lprc : bounding rectangle coordinates
5163 * lprc->left specifies the portion of the item for which the bounding
5164 * rectangle will be retrieved.
5166 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5167 * including the icon and label.
5170 * * Experiment shows that native control returns:
5171 * * width = min (48, length of text line)
5172 * * .left = position.x - (width - iconsize.cx)/2
5173 * * .right = .left + width
5174 * * height = #lines of text * ntmHeight + icon height + 8
5175 * * .top = position.y - 2
5176 * * .bottom = .top + height
5177 * * separation between items .y = itemSpacing.cy - height
5178 * * .x = itemSpacing.cx - width
5179 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5182 * * Experiment shows that native control returns:
5183 * * width = iconSize.cx + 16
5184 * * .left = position.x - (width - iconsize.cx)/2
5185 * * .right = .left + width
5186 * * height = iconSize.cy + 4
5187 * * .top = position.y - 2
5188 * * .bottom = .top + height
5189 * * separation between items .y = itemSpacing.cy - height
5190 * * .x = itemSpacing.cx - width
5191 * LVIR_LABEL Returns the bounding rectangle of the item text.
5194 * * Experiment shows that native control returns:
5195 * * width = text length
5196 * * .left = position.x - width/2
5197 * * .right = .left + width
5198 * * height = ntmH * linecount + 2
5199 * * .top = position.y + iconSize.cy + 6
5200 * * .bottom = .top + height
5201 * * separation between items .y = itemSpacing.cy - height
5202 * * .x = itemSpacing.cx - width
5203 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5204 * rectangles, but excludes columns in report view.
5211 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5212 * upon whether the window has the focus currently and on whether the item
5213 * is the one with the focus. Ensure that the control's record of which
5214 * item has the focus agrees with the items' records.
5216 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5218 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5219 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5220 BOOL doLabel = TRUE, oversizedBox = FALSE;
5221 POINT Position, Origin;
5225 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5227 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5229 LISTVIEW_GetOrigin(infoPtr, &Origin);
5230 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5232 /* Be smart and try to figure out the minimum we have to do */
5233 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5234 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5235 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5236 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5237 oversizedBox = TRUE;
5239 /* get what we need from the item before hand, so we make
5240 * only one request. This can speed up things, if data
5241 * is stored on the app side */
5243 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5244 if (doLabel) lvItem.mask |= LVIF_TEXT;
5245 lvItem.iItem = nItem;
5246 lvItem.iSubItem = 0;
5247 lvItem.pszText = szDispText;
5248 lvItem.cchTextMax = DISP_TEXT_SIZE;
5249 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5250 /* we got the state already up, simulate it here, to avoid a reget */
5251 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5253 lvItem.mask |= LVIF_STATE;
5254 lvItem.stateMask = LVIS_FOCUSED;
5255 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5258 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5259 lprc->left = LVIR_BOUNDS;
5263 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5267 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5271 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5274 case LVIR_SELECTBOUNDS:
5275 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5276 UnionRect(lprc, lprc, &label_rect);
5280 WARN("Unknown value: %ld\n", lprc->left);
5284 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5286 TRACE(" rect=%s\n", debugrect(lprc));
5293 * Retrieves the spacing between listview control items.
5296 * [I] infoPtr : valid pointer to the listview structure
5297 * [IO] lprc : rectangle to receive the output
5298 * on input, lprc->top = nSubItem
5299 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5301 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5302 * not only those of the first column.
5303 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5309 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5314 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5316 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5317 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5319 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5321 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5323 lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5324 lvItem.iItem = nItem;
5325 lvItem.iSubItem = lprc->top;
5327 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5331 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5336 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5340 ERR("Unknown bounds=%ld\n", lprc->left);
5344 OffsetRect(lprc, Position.x, Position.y);
5351 * Retrieves the width of a label.
5354 * [I] infoPtr : valid pointer to the listview structure
5357 * SUCCESS : string width (in pixels)
5360 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5362 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5365 TRACE("(nItem=%d)\n", nItem);
5367 lvItem.mask = LVIF_TEXT;
5368 lvItem.iItem = nItem;
5369 lvItem.iSubItem = 0;
5370 lvItem.pszText = szDispText;
5371 lvItem.cchTextMax = DISP_TEXT_SIZE;
5372 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5374 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5379 * Retrieves the spacing between listview control items.
5382 * [I] infoPtr : valid pointer to the listview structure
5383 * [I] bSmall : flag for small or large icon
5386 * Horizontal + vertical spacing
5388 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5394 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5398 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5399 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5401 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5408 * Retrieves the state of a listview control item.
5411 * [I] infoPtr : valid pointer to the listview structure
5412 * [I] nItem : item index
5413 * [I] uMask : state mask
5416 * State specified by the mask.
5418 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5422 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5424 lvItem.iItem = nItem;
5425 lvItem.iSubItem = 0;
5426 lvItem.mask = LVIF_STATE;
5427 lvItem.stateMask = uMask;
5428 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5430 return lvItem.state & uMask;
5435 * Retrieves the text of a listview control item or subitem.
5438 * [I] hwnd : window handle
5439 * [I] nItem : item index
5440 * [IO] lpLVItem : item information
5441 * [I] isW : TRUE if lpLVItem is Unicode
5444 * SUCCESS : string length
5447 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5449 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5451 lpLVItem->mask = LVIF_TEXT;
5452 lpLVItem->iItem = nItem;
5453 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5455 return textlenT(lpLVItem->pszText, isW);
5460 * Searches for an item based on properties + relationships.
5463 * [I] infoPtr : valid pointer to the listview structure
5464 * [I] nItem : item index
5465 * [I] uFlags : relationship flag
5468 * SUCCESS : item index
5471 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5473 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5475 LVFINDINFOW lvFindInfo;
5476 INT nCountPerColumn;
5480 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5481 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5483 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5485 if (uFlags & LVNI_CUT)
5488 if (uFlags & LVNI_DROPHILITED)
5489 uMask |= LVIS_DROPHILITED;
5491 if (uFlags & LVNI_FOCUSED)
5492 uMask |= LVIS_FOCUSED;
5494 if (uFlags & LVNI_SELECTED)
5495 uMask |= LVIS_SELECTED;
5497 /* if we're asked for the focused item, that's only one,
5498 * so it's worth optimizing */
5499 if (uFlags & LVNI_FOCUSED)
5501 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5502 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5505 if (uFlags & LVNI_ABOVE)
5507 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5512 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5518 /* Special case for autoarrange - move 'til the top of a list */
5519 if (is_autoarrange(infoPtr))
5521 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5522 while (nItem - nCountPerRow >= 0)
5524 nItem -= nCountPerRow;
5525 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5530 lvFindInfo.flags = LVFI_NEARESTXY;
5531 lvFindInfo.vkDirection = VK_UP;
5532 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5533 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5535 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5540 else if (uFlags & LVNI_BELOW)
5542 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5544 while (nItem < infoPtr->nItemCount)
5547 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5553 /* Special case for autoarrange - move 'til the bottom of a list */
5554 if (is_autoarrange(infoPtr))
5556 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5557 while (nItem + nCountPerRow < infoPtr->nItemCount )
5559 nItem += nCountPerRow;
5560 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5565 lvFindInfo.flags = LVFI_NEARESTXY;
5566 lvFindInfo.vkDirection = VK_DOWN;
5567 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5568 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5570 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5575 else if (uFlags & LVNI_TOLEFT)
5577 if (uView == LVS_LIST)
5579 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5580 while (nItem - nCountPerColumn >= 0)
5582 nItem -= nCountPerColumn;
5583 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5587 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5589 /* Special case for autoarrange - move 'ti the beginning of a row */
5590 if (is_autoarrange(infoPtr))
5592 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5593 while (nItem % nCountPerRow > 0)
5596 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5601 lvFindInfo.flags = LVFI_NEARESTXY;
5602 lvFindInfo.vkDirection = VK_LEFT;
5603 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5604 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5606 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5611 else if (uFlags & LVNI_TORIGHT)
5613 if (uView == LVS_LIST)
5615 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5616 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5618 nItem += nCountPerColumn;
5619 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5623 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5625 /* Special case for autoarrange - move 'til the end of a row */
5626 if (is_autoarrange(infoPtr))
5628 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5629 while (nItem % nCountPerRow < nCountPerRow - 1 )
5632 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5637 lvFindInfo.flags = LVFI_NEARESTXY;
5638 lvFindInfo.vkDirection = VK_RIGHT;
5639 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5640 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5642 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5651 /* search by index */
5652 for (i = nItem; i < infoPtr->nItemCount; i++)
5654 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5662 /* LISTVIEW_GetNumberOfWorkAreas */
5666 * Retrieves the origin coordinates when in icon or small icon display mode.
5669 * [I] infoPtr : valid pointer to the listview structure
5670 * [O] lpptOrigin : coordinate information
5675 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5677 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5678 INT nHorzPos = 0, nVertPos = 0;
5679 SCROLLINFO scrollInfo;
5681 scrollInfo.cbSize = sizeof(SCROLLINFO);
5682 scrollInfo.fMask = SIF_POS;
5684 if ((infoPtr->dwStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5685 nHorzPos = scrollInfo.nPos;
5686 if ((infoPtr->dwStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5687 nVertPos = scrollInfo.nPos;
5689 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5691 lpptOrigin->x = infoPtr->rcList.left;
5692 lpptOrigin->y = infoPtr->rcList.top;
5693 if (uView == LVS_LIST)
5694 nHorzPos *= infoPtr->nItemWidth;
5695 else if (uView == LVS_REPORT)
5696 nVertPos *= infoPtr->nItemHeight;
5698 lpptOrigin->x -= nHorzPos;
5699 lpptOrigin->y -= nVertPos;
5701 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5706 * Retrieves the width of a string.
5709 * [I] hwnd : window handle
5710 * [I] lpszText : text string to process
5711 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5714 * SUCCESS : string width (in pixels)
5717 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5722 if (is_textT(lpszText, isW))
5724 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5725 HDC hdc = GetDC(infoPtr->hwndSelf);
5726 HFONT hOldFont = SelectObject(hdc, hFont);
5729 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5731 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5732 SelectObject(hdc, hOldFont);
5733 ReleaseDC(infoPtr->hwndSelf, hdc);
5735 return stringSize.cx;
5740 * Determines which listview item is located at the specified position.
5743 * [I] infoPtr : valid pointer to the listview structure
5744 * [IO] lpht : hit test information
5745 * [I] subitem : fill out iSubItem.
5746 * [I] select : return the index only if the hit selects the item
5749 * (mm 20001022): We must not allow iSubItem to be touched, for
5750 * an app might pass only a structure with space up to iItem!
5751 * (MS Office 97 does that for instance in the file open dialog)
5754 * SUCCESS : item index
5757 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5759 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5760 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5761 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5762 POINT Origin, Position, opt;
5766 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5770 if (subitem) lpht->iSubItem = 0;
5772 if (infoPtr->rcList.left > lpht->pt.x)
5773 lpht->flags |= LVHT_TOLEFT;
5774 else if (infoPtr->rcList.right < lpht->pt.x)
5775 lpht->flags |= LVHT_TORIGHT;
5777 if (infoPtr->rcList.top > lpht->pt.y)
5778 lpht->flags |= LVHT_ABOVE;
5779 else if (infoPtr->rcList.bottom < lpht->pt.y)
5780 lpht->flags |= LVHT_BELOW;
5782 TRACE("lpht->flags=0x%x\n", lpht->flags);
5783 if (lpht->flags) return -1;
5785 lpht->flags |= LVHT_NOWHERE;
5787 LISTVIEW_GetOrigin(infoPtr, &Origin);
5789 /* first deal with the large items */
5790 rcSearch.left = lpht->pt.x;
5791 rcSearch.top = lpht->pt.y;
5792 rcSearch.right = rcSearch.left + 1;
5793 rcSearch.bottom = rcSearch.top + 1;
5795 iterator_frameditems(&i, infoPtr, &rcSearch);
5796 iterator_next(&i); /* go to first item in the sequence */
5797 lpht->iItem = i.nItem;
5798 iterator_destroy(&i);
5800 TRACE("lpht->iItem=%d\n", lpht->iItem);
5801 if (lpht->iItem == -1) return -1;
5803 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5804 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5805 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5806 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5807 lvItem.iItem = lpht->iItem;
5808 lvItem.iSubItem = 0;
5809 lvItem.pszText = szDispText;
5810 lvItem.cchTextMax = DISP_TEXT_SIZE;
5811 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5812 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5814 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5815 LISTVIEW_GetItemOrigin(infoPtr, lpht->iItem, &Position);
5816 opt.x = lpht->pt.x - Position.x - Origin.x;
5817 opt.y = lpht->pt.y - Position.y - Origin.y;
5819 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
5822 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5823 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5824 if (!PtInRect(&rcBounds, opt)) return -1;
5826 if (PtInRect(&rcIcon, opt))
5827 lpht->flags |= LVHT_ONITEMICON;
5828 else if (PtInRect(&rcLabel, opt))
5829 lpht->flags |= LVHT_ONITEMLABEL;
5830 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5831 lpht->flags |= LVHT_ONITEMSTATEICON;
5832 if (lpht->flags & LVHT_ONITEM)
5833 lpht->flags &= ~LVHT_NOWHERE;
5835 TRACE("lpht->flags=0x%x\n", lpht->flags);
5836 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5840 rcBounds.right = rcBounds.left;
5841 for (j = 0; j < infoPtr->hdpaColumns->nItemCount; j++)
5843 rcBounds.left = rcBounds.right;
5844 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5845 if (PtInRect(&rcBounds, opt))
5853 if (!select || lpht->iItem == -1) return lpht->iItem;
5855 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5857 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5858 return PtInRect(&rcBounds, opt) ? lpht->iItem : -1;
5862 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5863 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5864 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5865 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5866 their own sort proc. when sending LVM_SORTITEMS.
5869 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5871 LVS_SORTXXX must be specified,
5872 LVS_OWNERDRAW is not set,
5873 <item>.pszText is not LPSTR_TEXTCALLBACK.
5875 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5876 are sorted based on item text..."
5878 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5880 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
5881 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
5882 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5884 /* if we're sorting descending, negate the return value */
5885 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5890 * Inserts a new item in the listview control.
5893 * [I] infoPtr : valid pointer to the listview structure
5894 * [I] lpLVItem : item information
5895 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5898 * SUCCESS : new item index
5901 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
5903 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5908 BOOL is_sorted, has_changed;
5911 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5913 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
5915 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5916 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
5918 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
5920 if ( !(lpItem = (ITEM_INFO *)COMCTL32_Alloc(sizeof(ITEM_INFO))) )
5923 /* insert item in listview control data structure */
5924 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
5925 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
5927 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5928 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5930 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
5931 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
5932 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
5933 if (nItem == -1) goto fail;
5934 infoPtr->nItemCount++;
5936 /* set the item attributes */
5937 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5939 /* full size structure expected - _WIN32IE >= 0x560 */
5942 else if (lpLVItem->mask & LVIF_INDENT)
5944 /* indent member expected - _WIN32IE >= 0x300 */
5945 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
5949 /* minimal structure expected */
5950 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
5953 item.state &= ~LVIS_STATEIMAGEMASK;
5954 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
5956 /* if we're sorted, sort the list, and update the index */
5959 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5960 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5961 assert(nItem != -1);
5964 /* make room for the position, if we are in the right mode */
5965 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5967 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5969 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5971 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5976 /* Add the subitem list to the items array. Do this last in case we go to
5977 * fail during the above.
5979 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5981 /* send LVN_INSERTITEM notification */
5982 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5984 nmlv.lParam = lpItem->lParam;
5985 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5987 /* align items (set position of each item) */
5988 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
5992 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
5993 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
5995 LISTVIEW_NextIconPosTop(infoPtr, &pt);
5997 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6000 /* now is the invalidation fun */
6001 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6005 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6006 infoPtr->nItemCount--;
6008 DPA_DeletePtr(hdpaSubItems, 0);
6009 DPA_Destroy (hdpaSubItems);
6010 COMCTL32_Free (lpItem);
6016 * Redraws a range of items.
6019 * [I] infoPtr : valid pointer to the listview structure
6020 * [I] nFirst : first item
6021 * [I] nLast : last item
6027 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6031 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6032 max(nFirst, nLast) >= infoPtr->nItemCount)
6035 for (i = nFirst; i <= nLast; i++)
6036 LISTVIEW_InvalidateItem(infoPtr, i);
6043 * Scroll the content of a listview.
6046 * [I] infoPtr : valid pointer to the listview structure
6047 * [I] dx : horizontal scroll amount in pixels
6048 * [I] dy : vertical scroll amount in pixels
6055 * If the control is in report mode (LVS_REPORT) the control can
6056 * be scrolled only in line increments. "dy" will be rounded to the
6057 * nearest number of pixels that are a whole line. Ex: if line height
6058 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6059 * is passed the the scroll will be 0. (per MSDN 7/2002)
6061 * For: (per experimentaion with native control and CSpy ListView)
6062 * LVS_ICON dy=1 = 1 pixel (vertical only)
6064 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6066 * LVS_LIST dx=1 = 1 column (horizontal only)
6067 * but will only scroll 1 column per message
6068 * no matter what the value.
6069 * dy must be 0 or FALSE returned.
6070 * LVS_REPORT dx=1 = 1 pixel
6074 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6076 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6078 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6079 dy /= infoPtr->nItemHeight;
6082 if (dy != 0) return FALSE;
6089 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6090 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6097 * Sets the background color.
6100 * [I] infoPtr : valid pointer to the listview structure
6101 * [I] clrBk : background color
6107 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6109 TRACE("(clrBk=%lx)\n", clrBk);
6111 if(infoPtr->clrBk != clrBk) {
6112 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6113 infoPtr->clrBk = clrBk;
6114 if (clrBk == CLR_NONE)
6115 infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6117 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6118 LISTVIEW_InvalidateList(infoPtr);
6124 /* LISTVIEW_SetBkImage */
6126 /*** Helper for {Insert,Set}ColumnT *only* */
6127 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6129 if (lpColumn->mask & LVCF_FMT)
6131 /* format member is valid */
6132 lphdi->mask |= HDI_FORMAT;
6134 /* set text alignment (leftmost column must be left-aligned) */
6135 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
6136 lphdi->fmt |= HDF_LEFT;
6137 else if (lpColumn->fmt & LVCFMT_RIGHT)
6138 lphdi->fmt |= HDF_RIGHT;
6139 else if (lpColumn->fmt & LVCFMT_CENTER)
6140 lphdi->fmt |= HDF_CENTER;
6142 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6143 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6145 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6147 lphdi->fmt |= HDF_IMAGE;
6148 lphdi->iImage = I_IMAGECALLBACK;
6152 if (lpColumn->mask & LVCF_WIDTH)
6154 lphdi->mask |= HDI_WIDTH;
6155 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6157 /* make it fill the remainder of the controls width */
6161 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6163 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6164 lphdi->cxy += rcHeader.right - rcHeader.left;
6167 /* retrieve the layout of the header */
6168 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6169 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6171 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6174 lphdi->cxy = lpColumn->cx;
6177 if (lpColumn->mask & LVCF_TEXT)
6179 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6180 lphdi->fmt |= HDF_STRING;
6181 lphdi->pszText = lpColumn->pszText;
6182 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6185 if (lpColumn->mask & LVCF_IMAGE)
6187 lphdi->mask |= HDI_IMAGE;
6188 lphdi->iImage = lpColumn->iImage;
6191 if (lpColumn->mask & LVCF_ORDER)
6193 lphdi->mask |= HDI_ORDER;
6194 lphdi->iOrder = lpColumn->iOrder;
6201 * Inserts a new column.
6204 * [I] infoPtr : valid pointer to the listview structure
6205 * [I] nColumn : column index
6206 * [I] lpColumn : column information
6207 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6210 * SUCCESS : new column index
6213 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6214 const LVCOLUMNW *lpColumn, BOOL isW)
6216 COLUMN_INFO *lpColumnInfo;
6220 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6222 if (!lpColumn || nColumn < 0) return -1;
6223 nColumn = min(nColumn, infoPtr->hdpaColumns->nItemCount);
6225 ZeroMemory(&hdi, sizeof(HDITEMW));
6226 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6228 /* insert item in header control */
6229 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6230 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6231 (WPARAM)nColumn, (LPARAM)&hdi);
6232 if (nNewColumn == -1) return -1;
6233 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6235 /* create our own column info */
6236 if (!(lpColumnInfo = COMCTL32_Alloc(sizeof(COLUMN_INFO)))) goto fail;
6237 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6239 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6240 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6242 /* now we have to actually adjust the data */
6243 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6245 SUBITEM_INFO *lpSubItem, *lpMainItem, **lpNewItems = 0;
6249 /* preallocate memory, so we can fail gracefully */
6250 if (nNewColumn == 0)
6252 lpNewItems = COMCTL32_Alloc(sizeof(SUBITEM_INFO *) * infoPtr->nItemCount);
6253 if (!lpNewItems) goto fail;
6254 for (i = 0; i < infoPtr->nItemCount; i++)
6255 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(SUBITEM_INFO)))) break;
6256 if (i != infoPtr->nItemCount)
6258 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
6259 COMCTL32_Free(lpNewItems);
6264 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6266 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6267 for (i = 1; i < hdpaSubItems->nItemCount; i++)
6269 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6270 if (lpSubItem->iSubItem >= nNewColumn)
6271 lpSubItem->iSubItem++;
6274 /* for inserting column 0, we have to special-case the main item */
6275 if (nNewColumn == 0)
6277 lpMainItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6278 lpSubItem = lpNewItems[nItem];
6279 lpSubItem->hdr = lpMainItem->hdr;
6280 lpSubItem->iSubItem = 1;
6281 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
6282 lpMainItem->iSubItem = 0;
6283 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
6287 COMCTL32_Free(lpNewItems);
6290 /* make space for the new column */
6291 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6296 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6299 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6300 COMCTL32_Free(lpColumnInfo);
6307 * Sets the attributes of a header item.
6310 * [I] infoPtr : valid pointer to the listview structure
6311 * [I] nColumn : column index
6312 * [I] lpColumn : column attributes
6313 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6319 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6320 const LVCOLUMNW *lpColumn, BOOL isW)
6322 HDITEMW hdi, hdiget;
6325 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6327 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6329 ZeroMemory(&hdi, sizeof(HDITEMW));
6330 if (lpColumn->mask & LVCF_FMT)
6332 hdi.mask |= HDI_FORMAT;
6333 hdiget.mask = HDI_FORMAT;
6334 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6335 hdi.fmt = hdiget.fmt & HDF_STRING;
6337 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6339 /* set header item attributes */
6340 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6341 if (!bResult) return FALSE;
6343 if (lpColumn->mask & LVCF_FMT)
6345 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6346 int oldFmt = lpColumnInfo->fmt;
6348 lpColumnInfo->fmt = lpColumn->fmt;
6349 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6351 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6352 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6361 * Sets the column order array
6364 * [I] infoPtr : valid pointer to the listview structure
6365 * [I] iCount : number of elements in column order array
6366 * [I] lpiArray : pointer to column order array
6372 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6374 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6385 * Sets the width of a column
6388 * [I] infoPtr : valid pointer to the listview structure
6389 * [I] nColumn : column index
6390 * [I] cx : column width
6396 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6398 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6399 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6403 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6405 /* set column width only if in report or list mode */
6406 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6408 /* take care of invalid cx values */
6409 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6410 else if (uView == LVS_LIST && cx < 1) return FALSE;
6412 /* resize all columns if in LVS_LIST mode */
6413 if(uView == LVS_LIST)
6415 infoPtr->nItemWidth = cx;
6416 LISTVIEW_InvalidateList(infoPtr);
6420 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6422 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < infoPtr->hdpaColumns->nItemCount -1))
6427 lvItem.mask = LVIF_TEXT;
6429 lvItem.iSubItem = nColumn;
6430 lvItem.pszText = szDispText;
6431 lvItem.cchTextMax = DISP_TEXT_SIZE;
6432 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6434 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6435 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6436 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6438 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6439 max_cx += infoPtr->iconSize.cx;
6440 max_cx += TRAILING_LABEL_PADDING;
6443 /* autosize based on listview items width */
6444 if(cx == LVSCW_AUTOSIZE)
6446 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6448 /* if iCol is the last column make it fill the remainder of the controls width */
6449 if(nColumn == infoPtr->hdpaColumns->nItemCount - 1)
6454 LISTVIEW_GetOrigin(infoPtr, &Origin);
6455 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6457 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6461 /* Despite what the MS docs say, if this is not the last
6462 column, then MS resizes the column to the width of the
6463 largest text string in the column, including headers
6464 and items. This is different from LVSCW_AUTOSIZE in that
6465 LVSCW_AUTOSIZE ignores the header string length. */
6468 /* retrieve header text */
6469 hdi.mask = HDI_TEXT;
6470 hdi.cchTextMax = DISP_TEXT_SIZE;
6471 hdi.pszText = szDispText;
6472 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6474 HDC hdc = GetDC(infoPtr->hwndSelf);
6475 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6478 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6479 cx = size.cx + TRAILING_HEADER_PADDING;
6480 /* FIXME: Take into account the header image, if one is present */
6481 SelectObject(hdc, old_font);
6482 ReleaseDC(infoPtr->hwndSelf, hdc);
6484 cx = max (cx, max_cx);
6488 if (cx < 0) return FALSE;
6490 /* call header to update the column change */
6491 hdi.mask = HDI_WIDTH;
6493 TRACE("hdi.cxy=%d\n", hdi.cxy);
6494 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6499 * Sets the extended listview style.
6502 * [I] infoPtr : valid pointer to the listview structure
6504 * [I] dwStyle : style
6507 * SUCCESS : previous style
6510 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6512 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6516 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6518 infoPtr->dwLvExStyle = dwStyle;
6525 * Sets the new hot cursor used during hot tracking and hover selection.
6528 * [I] infoPtr : valid pointer to the listview structure
6529 * [I} hCurosr : the new hot cursor handle
6532 * Returns the previous hot cursor
6534 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6536 HCURSOR oldCursor = infoPtr->hHotCursor;
6538 infoPtr->hHotCursor = hCursor;
6546 * Sets the hot item index.
6549 * [I] infoPtr : valid pointer to the listview structure
6550 * [I] iIndex : index
6553 * SUCCESS : previous hot item index
6554 * FAILURE : -1 (no hot item)
6556 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6558 INT iOldIndex = infoPtr->nHotItem;
6560 infoPtr->nHotItem = iIndex;
6568 * Sets the amount of time the cursor must hover over an item before it is selected.
6571 * [I] infoPtr : valid pointer to the listview structure
6572 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6575 * Returns the previous hover time
6577 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6579 DWORD oldHoverTime = infoPtr->dwHoverTime;
6581 infoPtr->dwHoverTime = dwHoverTime;
6583 return oldHoverTime;
6588 * Sets spacing for icons of LVS_ICON style.
6591 * [I] infoPtr : valid pointer to the listview structure
6592 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6593 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6596 * MAKELONG(oldcx, oldcy)
6598 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6600 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6601 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6603 TRACE("requested=(%d,%d)\n", cx, cy);
6605 /* this is supported only for LVS_ICON style */
6606 if (uView != LVS_ICON) return oldspacing;
6608 /* set to defaults, if instructed to */
6609 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6610 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6612 /* if 0 then compute width
6613 * FIXME: Should scan each item and determine max width of
6614 * icon or label, then make that the width */
6616 cx = infoPtr->iconSpacing.cx;
6618 /* if 0 then compute height */
6620 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6621 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6624 infoPtr->iconSpacing.cx = cx;
6625 infoPtr->iconSpacing.cy = cy;
6627 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6628 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6629 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6630 infoPtr->ntmHeight);
6632 /* these depend on the iconSpacing */
6633 LISTVIEW_UpdateItemSize(infoPtr);
6638 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6642 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6649 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6650 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6659 * [I] infoPtr : valid pointer to the listview structure
6660 * [I] nType : image list type
6661 * [I] himl : image list handle
6664 * SUCCESS : old image list
6667 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6669 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6670 INT oldHeight = infoPtr->nItemHeight;
6671 HIMAGELIST himlOld = 0;
6673 TRACE("(nType=%d, himl=%p\n", nType, himl);
6678 himlOld = infoPtr->himlNormal;
6679 infoPtr->himlNormal = himl;
6680 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6681 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6685 himlOld = infoPtr->himlSmall;
6686 infoPtr->himlSmall = himl;
6687 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6691 himlOld = infoPtr->himlState;
6692 infoPtr->himlState = himl;
6693 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6694 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6698 ERR("Unknown icon type=%d\n", nType);
6702 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6703 if (infoPtr->nItemHeight != oldHeight)
6704 LISTVIEW_UpdateScroll(infoPtr);
6711 * Preallocates memory (does *not* set the actual count of items !)
6714 * [I] infoPtr : valid pointer to the listview structure
6715 * [I] nItems : item count (projected number of items to allocate)
6716 * [I] dwFlags : update flags
6722 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6724 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6726 if (infoPtr->dwStyle & LVS_OWNERDATA)
6728 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6729 INT nOldCount = infoPtr->nItemCount;
6731 if (nItems < nOldCount)
6733 RANGE range = { nItems, nOldCount };
6734 ranges_del(infoPtr->selectionRanges, range);
6735 if (infoPtr->nFocusedItem >= nItems)
6737 infoPtr->nFocusedItem = -1;
6738 SetRectEmpty(&infoPtr->rcFocus);
6742 infoPtr->nItemCount = nItems;
6743 LISTVIEW_UpdateScroll(infoPtr);
6745 /* the flags are valid only in ownerdata report and list modes */
6746 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6748 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6749 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6751 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6752 LISTVIEW_InvalidateList(infoPtr);
6759 LISTVIEW_GetOrigin(infoPtr, &Origin);
6760 nFrom = min(nOldCount, nItems);
6761 nTo = max(nOldCount, nItems);
6763 if (uView == LVS_REPORT)
6766 rcErase.top = nFrom * infoPtr->nItemHeight;
6767 rcErase.right = infoPtr->nItemWidth;
6768 rcErase.bottom = nTo * infoPtr->nItemHeight;
6769 OffsetRect(&rcErase, Origin.x, Origin.y);
6770 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6771 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6775 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6777 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6778 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6779 rcErase.right = rcErase.left + infoPtr->nItemWidth;
6780 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6781 OffsetRect(&rcErase, Origin.x, Origin.y);
6782 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6783 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6785 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6787 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
6788 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6789 OffsetRect(&rcErase, Origin.x, Origin.y);
6790 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6791 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6797 /* According to MSDN for non-LVS_OWNERDATA this is just
6798 * a performance issue. The control allocates its internal
6799 * data structures for the number of items specified. It
6800 * cuts down on the number of memory allocations. Therefore
6801 * we will just issue a WARN here
6803 WARN("for non-ownerdata performance option not implemented.\n");
6811 * Sets the position of an item.
6814 * [I] infoPtr : valid pointer to the listview structure
6815 * [I] nItem : item index
6816 * [I] pt : coordinate
6822 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6824 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6827 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6829 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6830 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6832 LISTVIEW_GetOrigin(infoPtr, &Origin);
6834 /* This point value seems to be an undocumented feature.
6835 * The best guess is that it means either at the origin,
6836 * or at true beginning of the list. I will assume the origin. */
6837 if ((pt.x == -1) && (pt.y == -1))
6840 if (uView == LVS_ICON)
6842 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6843 pt.y -= ICON_TOP_PADDING;
6848 infoPtr->bAutoarrange = FALSE;
6850 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
6855 * Sets the state of one or many items.
6858 * [I] infoPtr : valid pointer to the listview structure
6859 * [I] nItem : item index
6860 * [I] lpLVItem : item or subitem info
6866 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
6868 BOOL bResult = TRUE;
6871 lvItem.iItem = nItem;
6872 lvItem.iSubItem = 0;
6873 lvItem.mask = LVIF_STATE;
6874 lvItem.state = lpLVItem->state;
6875 lvItem.stateMask = lpLVItem->stateMask;
6876 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6880 /* apply to all items */
6881 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6882 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6885 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6892 * Sets the text of an item or subitem.
6895 * [I] hwnd : window handle
6896 * [I] nItem : item index
6897 * [I] lpLVItem : item or subitem info
6898 * [I] isW : TRUE if input is Unicode
6904 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
6908 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6910 lvItem.iItem = nItem;
6911 lvItem.iSubItem = lpLVItem->iSubItem;
6912 lvItem.mask = LVIF_TEXT;
6913 lvItem.pszText = lpLVItem->pszText;
6914 lvItem.cchTextMax = lpLVItem->cchTextMax;
6916 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6918 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6923 * Set item index that marks the start of a multiple selection.
6926 * [I] infoPtr : valid pointer to the listview structure
6927 * [I] nIndex : index
6930 * Index number or -1 if there is no selection mark.
6932 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6934 INT nOldIndex = infoPtr->nSelectionMark;
6936 TRACE("(nIndex=%d)\n", nIndex);
6938 infoPtr->nSelectionMark = nIndex;
6945 * Sets the text background color.
6948 * [I] infoPtr : valid pointer to the listview structure
6949 * [I] clrTextBk : text background color
6955 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6957 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6959 if (infoPtr->clrTextBk != clrTextBk)
6961 infoPtr->clrTextBk = clrTextBk;
6962 LISTVIEW_InvalidateList(infoPtr);
6970 * Sets the text foreground color.
6973 * [I] infoPtr : valid pointer to the listview structure
6974 * [I] clrText : text color
6980 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6982 TRACE("(clrText=%lx)\n", clrText);
6984 if (infoPtr->clrText != clrText)
6986 infoPtr->clrText = clrText;
6987 LISTVIEW_InvalidateList(infoPtr);
6995 * Determines which listview item is located at the specified position.
6998 * [I] infoPtr : valid pointer to the listview structure
6999 * [I] hwndNewToolTip : handle to new ToolTip
7004 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7006 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7007 infoPtr->hwndToolTip = hwndNewToolTip;
7008 return hwndOldToolTip;
7011 /* LISTVIEW_SetUnicodeFormat */
7012 /* LISTVIEW_SetWorkAreas */
7016 * Callback internally used by LISTVIEW_SortItems()
7019 * [I] first : pointer to first ITEM_INFO to compare
7020 * [I] second : pointer to second ITEM_INFO to compare
7021 * [I] lParam : HWND of control
7024 * if first comes before second : negative
7025 * if first comes after second : positive
7026 * if first and second are equivalent : zero
7028 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7030 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7031 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7032 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7034 /* Forward the call to the client defined callback */
7035 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7040 * Sorts the listview items.
7043 * [I] infoPtr : valid pointer to the listview structure
7044 * [I] pfnCompare : application-defined value
7045 * [I] lParamSort : pointer to comparision callback
7051 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7053 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7056 LPVOID selectionMarkItem;
7060 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7062 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7064 if (!infoPtr->hdpaItems) return FALSE;
7066 /* if there are 0 or 1 items, there is no need to sort */
7067 if (infoPtr->nItemCount < 2) return TRUE;
7069 if (infoPtr->nFocusedItem >= 0)
7071 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7072 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7073 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7075 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7076 /* clear the lpItem->state for non-selected ones */
7077 /* remove the selection ranges */
7079 infoPtr->pfnCompare = pfnCompare;
7080 infoPtr->lParamSort = lParamSort;
7081 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
7083 /* Adjust selections and indices so that they are the way they should
7084 * be after the sort (otherwise, the list items move around, but
7085 * whatever is at the item's previous original position will be
7088 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7089 for (i=0; i < infoPtr->nItemCount; i++)
7091 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7092 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7094 if (lpItem->state & LVIS_SELECTED)
7096 item.state = LVIS_SELECTED;
7097 item.stateMask = LVIS_SELECTED;
7098 LISTVIEW_SetItemState(infoPtr, i, &item);
7100 if (lpItem->state & LVIS_FOCUSED)
7102 infoPtr->nFocusedItem = i;
7103 lpItem->state &= ~LVIS_FOCUSED;
7106 if (selectionMarkItem != NULL)
7107 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7108 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7110 /* refresh the display */
7111 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7112 LISTVIEW_InvalidateList(infoPtr);
7119 * Updates an items or rearranges the listview control.
7122 * [I] infoPtr : valid pointer to the listview structure
7123 * [I] nItem : item index
7129 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7131 TRACE("(nItem=%d)\n", nItem);
7133 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7135 /* rearrange with default alignment style */
7136 if (is_autoarrange(infoPtr))
7137 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7139 LISTVIEW_InvalidateItem(infoPtr, nItem);
7147 * Creates the listview control.
7150 * [I] hwnd : window handle
7151 * [I] lpcs : the create parameters
7157 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7159 LISTVIEW_INFO *infoPtr;
7160 UINT uView = lpcs->style & LVS_TYPEMASK;
7163 TRACE("(lpcs=%p)\n", lpcs);
7165 /* initialize info pointer */
7166 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7167 if (!infoPtr) return -1;
7169 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7171 infoPtr->hwndSelf = hwnd;
7172 infoPtr->dwStyle = lpcs->style;
7173 /* determine the type of structures to use */
7174 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7175 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7177 /* initialize color information */
7178 infoPtr->clrBk = CLR_NONE;
7179 infoPtr->clrText = comctl32_color.clrWindowText;
7180 infoPtr->clrTextBk = CLR_DEFAULT;
7181 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7183 /* set default values */
7184 infoPtr->nFocusedItem = -1;
7185 infoPtr->nSelectionMark = -1;
7186 infoPtr->nHotItem = -1;
7187 infoPtr->bRedraw = TRUE;
7188 infoPtr->bFirstPaint = TRUE;
7189 infoPtr->bNoItemMetrics = TRUE;
7190 infoPtr->bDoChangeNotify = TRUE;
7191 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7192 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7193 infoPtr->nEditLabelItem = -1;
7194 infoPtr->dwHoverTime = -1; /* default system hover time */
7196 /* get default font (icon title) */
7197 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7198 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7199 infoPtr->hFont = infoPtr->hDefaultFont;
7200 LISTVIEW_SaveTextMetrics(infoPtr);
7203 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7204 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7205 0, 0, 0, 0, hwnd, NULL,
7206 lpcs->hInstance, NULL);
7207 if (!infoPtr->hwndHeader) goto fail;
7209 /* set header unicode format */
7210 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7212 /* set header font */
7213 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7215 /* allocate memory for the data structure */
7216 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7217 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7218 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7219 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7220 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7222 /* initialize the icon sizes */
7223 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7224 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7226 /* init item size to avoid division by 0 */
7227 LISTVIEW_UpdateItemSize (infoPtr);
7229 if (uView == LVS_REPORT)
7231 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7233 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7237 /* set HDS_HIDDEN flag to hide the header bar */
7238 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7239 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7246 DestroyWindow(infoPtr->hwndHeader);
7247 ranges_destroy(infoPtr->selectionRanges);
7248 DPA_Destroy(infoPtr->hdpaItems);
7249 DPA_Destroy(infoPtr->hdpaPosX);
7250 DPA_Destroy(infoPtr->hdpaPosY);
7251 DPA_Destroy(infoPtr->hdpaColumns);
7252 COMCTL32_Free(infoPtr);
7258 * Erases the background of the listview control.
7261 * [I] infoPtr : valid pointer to the listview structure
7262 * [I] hdc : device context handle
7268 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7272 TRACE("(hdc=%p)\n", hdc);
7274 if (!GetClipBox(hdc, &rc)) return FALSE;
7276 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7282 * Helper function for LISTVIEW_[HV]Scroll *only*.
7283 * Performs vertical/horizontal scrolling by a give amount.
7286 * [I] infoPtr : valid pointer to the listview structure
7287 * [I] dx : amount of horizontal scroll
7288 * [I] dy : amount of vertical scroll
7290 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7292 /* now we can scroll the list */
7293 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7294 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7295 /* if we have focus, adjust rect */
7296 OffsetRect(&infoPtr->rcFocus, dx, dy);
7297 UpdateWindow(infoPtr->hwndSelf);
7302 * Performs vertical scrolling.
7305 * [I] infoPtr : valid pointer to the listview structure
7306 * [I] nScrollCode : scroll code
7307 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7308 * [I] hScrollWnd : scrollbar control window handle
7314 * SB_LINEUP/SB_LINEDOWN:
7315 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7316 * for LVS_REPORT is 1 line
7317 * for LVS_LIST cannot occur
7320 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7321 INT nScrollDiff, HWND hScrollWnd)
7323 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7324 INT nOldScrollPos, nNewScrollPos;
7325 SCROLLINFO scrollInfo;
7328 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7329 debugscrollcode(nScrollCode), nScrollDiff);
7331 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7333 scrollInfo.cbSize = sizeof(SCROLLINFO);
7334 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7336 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7338 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7340 nOldScrollPos = scrollInfo.nPos;
7341 switch (nScrollCode)
7347 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7351 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7355 nScrollDiff = -scrollInfo.nPage;
7359 nScrollDiff = scrollInfo.nPage;
7362 case SB_THUMBPOSITION:
7364 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7371 /* quit right away if pos isn't changing */
7372 if (nScrollDiff == 0) return 0;
7374 /* calculate new position, and handle overflows */
7375 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7376 if (nScrollDiff > 0) {
7377 if (nNewScrollPos < nOldScrollPos ||
7378 nNewScrollPos > scrollInfo.nMax)
7379 nNewScrollPos = scrollInfo.nMax;
7381 if (nNewScrollPos > nOldScrollPos ||
7382 nNewScrollPos < scrollInfo.nMin)
7383 nNewScrollPos = scrollInfo.nMin;
7386 /* set the new position, and reread in case it changed */
7387 scrollInfo.fMask = SIF_POS;
7388 scrollInfo.nPos = nNewScrollPos;
7389 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7391 /* carry on only if it really changed */
7392 if (nNewScrollPos == nOldScrollPos) return 0;
7394 /* now adjust to client coordinates */
7395 nScrollDiff = nOldScrollPos - nNewScrollPos;
7396 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7398 /* and scroll the window */
7399 scroll_list(infoPtr, 0, nScrollDiff);
7406 * Performs horizontal scrolling.
7409 * [I] infoPtr : valid pointer to the listview structure
7410 * [I] nScrollCode : scroll code
7411 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7412 * [I] hScrollWnd : scrollbar control window handle
7418 * SB_LINELEFT/SB_LINERIGHT:
7419 * for LVS_ICON, LVS_SMALLICON 1 pixel
7420 * for LVS_REPORT is 1 pixel
7421 * for LVS_LIST is 1 column --> which is a 1 because the
7422 * scroll is based on columns not pixels
7425 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7426 INT nScrollDiff, HWND hScrollWnd)
7428 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7429 INT nOldScrollPos, nNewScrollPos;
7430 SCROLLINFO scrollInfo;
7432 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7433 debugscrollcode(nScrollCode), nScrollDiff);
7435 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7437 scrollInfo.cbSize = sizeof(SCROLLINFO);
7438 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7440 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7442 nOldScrollPos = scrollInfo.nPos;
7444 switch (nScrollCode)
7458 nScrollDiff = -scrollInfo.nPage;
7462 nScrollDiff = scrollInfo.nPage;
7465 case SB_THUMBPOSITION:
7467 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7474 /* quit right away if pos isn't changing */
7475 if (nScrollDiff == 0) return 0;
7477 /* calculate new position, and handle overflows */
7478 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7479 if (nScrollDiff > 0) {
7480 if (nNewScrollPos < nOldScrollPos ||
7481 nNewScrollPos > scrollInfo.nMax)
7482 nNewScrollPos = scrollInfo.nMax;
7484 if (nNewScrollPos > nOldScrollPos ||
7485 nNewScrollPos < scrollInfo.nMin)
7486 nNewScrollPos = scrollInfo.nMin;
7489 /* set the new position, and reread in case it changed */
7490 scrollInfo.fMask = SIF_POS;
7491 scrollInfo.nPos = nNewScrollPos;
7492 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7494 /* carry on only if it really changed */
7495 if (nNewScrollPos == nOldScrollPos) return 0;
7497 if(uView == LVS_REPORT)
7498 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7500 /* now adjust to client coordinates */
7501 nScrollDiff = nOldScrollPos - nNewScrollPos;
7502 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7504 /* and scroll the window */
7505 scroll_list(infoPtr, nScrollDiff, 0);
7510 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7512 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7513 INT gcWheelDelta = 0;
7514 UINT pulScrollLines = 3;
7515 SCROLLINFO scrollInfo;
7517 TRACE("(wheelDelta=%d)\n", wheelDelta);
7519 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7520 gcWheelDelta -= wheelDelta;
7522 scrollInfo.cbSize = sizeof(SCROLLINFO);
7523 scrollInfo.fMask = SIF_POS;
7530 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7531 * should be fixed in the future.
7533 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7534 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7538 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7540 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7541 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7542 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7547 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7558 * [I] infoPtr : valid pointer to the listview structure
7559 * [I] nVirtualKey : virtual key
7560 * [I] lKeyData : key data
7565 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7567 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7569 NMLVKEYDOWN nmKeyDown;
7571 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7573 /* send LVN_KEYDOWN notification */
7574 nmKeyDown.wVKey = nVirtualKey;
7575 nmKeyDown.flags = 0;
7576 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7578 switch (nVirtualKey)
7581 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7583 notify(infoPtr, NM_RETURN);
7584 notify(infoPtr, LVN_ITEMACTIVATE);
7589 if (infoPtr->nItemCount > 0)
7594 if (infoPtr->nItemCount > 0)
7595 nItem = infoPtr->nItemCount - 1;
7599 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7603 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7607 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7611 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7615 if (uView == LVS_REPORT)
7616 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7618 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7619 * LISTVIEW_GetCountPerRow(infoPtr);
7620 if(nItem < 0) nItem = 0;
7624 if (uView == LVS_REPORT)
7625 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7627 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7628 * LISTVIEW_GetCountPerRow(infoPtr);
7629 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7633 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7634 LISTVIEW_KeySelection(infoPtr, nItem);
7644 * [I] infoPtr : valid pointer to the listview structure
7649 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7653 /* if we did not have the focus, there's nothing to do */
7654 if (!infoPtr->bFocus) return 0;
7656 /* send NM_KILLFOCUS notification */
7657 notify(infoPtr, NM_KILLFOCUS);
7659 /* if we have a focus rectagle, get rid of it */
7660 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7662 /* set window focus flag */
7663 infoPtr->bFocus = FALSE;
7665 /* invalidate the selected items before reseting focus flag */
7666 LISTVIEW_InvalidateSelectedItems(infoPtr);
7673 * Processes double click messages (left mouse button).
7676 * [I] infoPtr : valid pointer to the listview structure
7677 * [I] wKey : key flag
7678 * [I] pts : mouse coordinate
7683 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7685 LVHITTESTINFO htInfo;
7687 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7689 /* send NM_RELEASEDCAPTURE notification */
7690 notify(infoPtr, NM_RELEASEDCAPTURE);
7692 htInfo.pt.x = pts.x;
7693 htInfo.pt.y = pts.y;
7695 /* send NM_DBLCLK notification */
7696 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7697 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7699 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7700 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7707 * Processes mouse down messages (left mouse button).
7710 * [I] infoPtr : valid pointer to the listview structure
7711 * [I] wKey : key flag
7712 * [I] pts : mouse coordinate
7717 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7719 LVHITTESTINFO lvHitTestInfo;
7720 static BOOL bGroupSelect = TRUE;
7721 POINT pt = { pts.x, pts.y };
7724 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7726 /* send NM_RELEASEDCAPTURE notification */
7727 notify(infoPtr, NM_RELEASEDCAPTURE);
7729 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7731 /* set left button down flag */
7732 infoPtr->bLButtonDown = TRUE;
7734 lvHitTestInfo.pt.x = pts.x;
7735 lvHitTestInfo.pt.y = pts.y;
7737 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7738 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7739 infoPtr->nEditLabelItem = -1;
7740 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7742 if (infoPtr->dwStyle & LVS_SINGLESEL)
7744 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7745 infoPtr->nEditLabelItem = nItem;
7747 LISTVIEW_SetSelection(infoPtr, nItem);
7751 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7755 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7756 LISTVIEW_SetItemFocus(infoPtr, nItem);
7757 infoPtr->nSelectionMark = nItem;
7763 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7764 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7766 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7767 infoPtr->nSelectionMark = nItem;
7770 else if (wKey & MK_CONTROL)
7774 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7776 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7777 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7778 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7779 infoPtr->nSelectionMark = nItem;
7781 else if (wKey & MK_SHIFT)
7783 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7787 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7788 infoPtr->nEditLabelItem = nItem;
7790 /* set selection (clears other pre-existing selections) */
7791 LISTVIEW_SetSelection(infoPtr, nItem);
7797 /* remove all selections */
7798 LISTVIEW_DeselectAll(infoPtr);
7806 * Processes mouse up messages (left mouse button).
7809 * [I] infoPtr : valid pointer to the listview structure
7810 * [I] wKey : key flag
7811 * [I] pts : mouse coordinate
7816 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7818 LVHITTESTINFO lvHitTestInfo;
7820 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7822 if (!infoPtr->bLButtonDown) return 0;
7824 lvHitTestInfo.pt.x = pts.x;
7825 lvHitTestInfo.pt.y = pts.y;
7827 /* send NM_CLICK notification */
7828 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7829 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7831 /* set left button flag */
7832 infoPtr->bLButtonDown = FALSE;
7834 /* if we clicked on a selected item, edit the label */
7835 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7836 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7843 * Destroys the listview control (called after WM_DESTROY).
7846 * [I] infoPtr : valid pointer to the listview structure
7851 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7855 /* delete all items */
7856 LISTVIEW_DeleteAllItems(infoPtr);
7858 /* destroy data structure */
7859 DPA_Destroy(infoPtr->hdpaItems);
7860 DPA_Destroy(infoPtr->hdpaPosX);
7861 DPA_Destroy(infoPtr->hdpaPosY);
7862 DPA_Destroy(infoPtr->hdpaColumns);
7863 ranges_destroy(infoPtr->selectionRanges);
7865 /* destroy image lists */
7866 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
7868 if (infoPtr->himlNormal)
7869 ImageList_Destroy(infoPtr->himlNormal);
7870 if (infoPtr->himlSmall)
7871 ImageList_Destroy(infoPtr->himlSmall);
7872 if (infoPtr->himlState)
7873 ImageList_Destroy(infoPtr->himlState);
7876 /* destroy font, bkgnd brush */
7878 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7879 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7881 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7883 /* free listview info pointer*/
7884 COMCTL32_Free(infoPtr);
7891 * Handles notifications from header.
7894 * [I] infoPtr : valid pointer to the listview structure
7895 * [I] nCtrlId : control identifier
7896 * [I] lpnmh : notification information
7901 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
7903 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7905 TRACE("(lpnmh=%p)\n", lpnmh);
7907 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= infoPtr->hdpaColumns->nItemCount) return 0;
7909 switch (lpnmh->hdr.code)
7913 case HDN_ITEMCHANGEDW:
7914 case HDN_ITEMCHANGEDA:
7916 COLUMN_INFO *lpColumnInfo;
7919 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
7923 hdi.mask = HDI_WIDTH;
7924 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
7928 cxy = lpnmh->pitem->cxy;
7930 /* determine how much we change since the last know position */
7931 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
7932 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7935 RECT rcCol = lpColumnInfo->rcHeader;
7937 lpColumnInfo->rcHeader.right += dx;
7938 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
7939 if (uView == LVS_REPORT && is_redrawing(infoPtr))
7941 /* this trick works for left aligned columns only */
7942 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
7944 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
7945 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
7947 rcCol.top = infoPtr->rcList.top;
7948 rcCol.bottom = infoPtr->rcList.bottom;
7949 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
7955 case HDN_ITEMCLICKW:
7956 case HDN_ITEMCLICKA:
7958 /* Handle sorting by Header Column */
7961 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7963 nmlv.iSubItem = lpnmh->iItem;
7964 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7974 * Determines the type of structure to use.
7977 * [I] infoPtr : valid pointer to the listview structureof the sender
7978 * [I] hwndFrom : listview window handle
7979 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
7984 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7986 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
7988 if (nCommand != NF_REQUERY) return 0;
7990 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
7997 * Paints/Repaints the listview control.
8000 * [I] infoPtr : valid pointer to the listview structure
8001 * [I] hdc : device context handle
8006 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8008 TRACE("(hdc=%p)\n", hdc);
8010 infoPtr->bFirstPaint = FALSE;
8011 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8013 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8015 infoPtr->bNoItemMetrics = FALSE;
8016 LISTVIEW_UpdateItemSize(infoPtr);
8017 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8018 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8019 LISTVIEW_UpdateScroll(infoPtr);
8022 LISTVIEW_Refresh(infoPtr, hdc);
8027 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8029 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8030 LISTVIEW_Refresh(infoPtr, hdc);
8031 EndPaint(infoPtr->hwndSelf, &ps);
8039 * Processes double click messages (right mouse button).
8042 * [I] infoPtr : valid pointer to the listview structure
8043 * [I] wKey : key flag
8044 * [I] pts : mouse coordinate
8049 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8051 LVHITTESTINFO lvHitTestInfo;
8053 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8055 /* send NM_RELEASEDCAPTURE notification */
8056 notify(infoPtr, NM_RELEASEDCAPTURE);
8058 /* send NM_RDBLCLK notification */
8059 lvHitTestInfo.pt.x = pts.x;
8060 lvHitTestInfo.pt.y = pts.y;
8061 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8062 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8069 * Processes mouse down messages (right mouse button).
8072 * [I] infoPtr : valid pointer to the listview structure
8073 * [I] wKey : key flag
8074 * [I] pts : mouse coordinate
8079 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8081 LVHITTESTINFO lvHitTestInfo;
8084 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8086 /* send NM_RELEASEDCAPTURE notification */
8087 notify(infoPtr, NM_RELEASEDCAPTURE);
8089 /* make sure the listview control window has the focus */
8090 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8092 /* set right button down flag */
8093 infoPtr->bRButtonDown = TRUE;
8095 /* determine the index of the selected item */
8096 lvHitTestInfo.pt.x = pts.x;
8097 lvHitTestInfo.pt.y = pts.y;
8098 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8100 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8102 LISTVIEW_SetItemFocus(infoPtr, nItem);
8103 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8104 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8105 LISTVIEW_SetSelection(infoPtr, nItem);
8109 LISTVIEW_DeselectAll(infoPtr);
8117 * Processes mouse up messages (right mouse button).
8120 * [I] infoPtr : valid pointer to the listview structure
8121 * [I] wKey : key flag
8122 * [I] pts : mouse coordinate
8127 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8129 LVHITTESTINFO lvHitTestInfo;
8132 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8134 if (!infoPtr->bRButtonDown) return 0;
8136 /* set button flag */
8137 infoPtr->bRButtonDown = FALSE;
8139 /* Send NM_RClICK notification */
8140 lvHitTestInfo.pt.x = pts.x;
8141 lvHitTestInfo.pt.y = pts.y;
8142 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8143 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8145 /* Change to screen coordinate for WM_CONTEXTMENU */
8146 pt = lvHitTestInfo.pt;
8147 ClientToScreen(infoPtr->hwndSelf, &pt);
8149 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8150 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8151 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8162 * [I] infoPtr : valid pointer to the listview structure
8163 * [I] hwnd : window handle of window containing the cursor
8164 * [I] nHittest : hit-test code
8165 * [I] wMouseMsg : ideintifier of the mouse message
8168 * TRUE if cursor is set
8171 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8173 LVHITTESTINFO lvHitTestInfo;
8175 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8177 if(!infoPtr->hHotCursor) return FALSE;
8179 GetCursorPos(&lvHitTestInfo.pt);
8180 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8182 SetCursor(infoPtr->hHotCursor);
8192 * [I] infoPtr : valid pointer to the listview structure
8193 * [I] hwndLoseFocus : handle of previously focused window
8198 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8200 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8202 /* if we have the focus already, there's nothing to do */
8203 if (infoPtr->bFocus) return 0;
8205 /* send NM_SETFOCUS notification */
8206 notify(infoPtr, NM_SETFOCUS);
8208 /* set window focus flag */
8209 infoPtr->bFocus = TRUE;
8211 /* put the focus rect back on */
8212 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8214 /* redraw all visible selected items */
8215 LISTVIEW_InvalidateSelectedItems(infoPtr);
8225 * [I] infoPtr : valid pointer to the listview structure
8226 * [I] fRedraw : font handle
8227 * [I] fRedraw : redraw flag
8232 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8234 HFONT oldFont = infoPtr->hFont;
8236 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8238 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8239 if (infoPtr->hFont == oldFont) return 0;
8241 LISTVIEW_SaveTextMetrics(infoPtr);
8243 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8244 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8246 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8253 * Message handling for WM_SETREDRAW.
8254 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8257 * [I] infoPtr : valid pointer to the listview structure
8258 * [I] bRedraw: state of redraw flag
8261 * DefWinProc return value
8263 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8265 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8267 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8268 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8270 infoPtr->bRedraw = bRedraw;
8272 if(!bRedraw) return 0;
8274 if (is_autoarrange(infoPtr))
8275 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8276 LISTVIEW_UpdateScroll(infoPtr);
8278 /* despite what the WM_SETREDRAW docs says, apps expect us
8279 * to invalidate the listview here... stupid! */
8280 LISTVIEW_InvalidateList(infoPtr);
8287 * Resizes the listview control. This function processes WM_SIZE
8288 * messages. At this time, the width and height are not used.
8291 * [I] infoPtr : valid pointer to the listview structure
8292 * [I] Width : new width
8293 * [I] Height : new height
8298 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8300 RECT rcOld = infoPtr->rcList;
8302 TRACE("(width=%d, height=%d)\n", Width, Height);
8304 LISTVIEW_UpdateSize(infoPtr);
8305 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8307 /* do not bother with display related stuff if we're not redrawing */
8308 if (!is_redrawing(infoPtr)) return 0;
8310 if (is_autoarrange(infoPtr))
8311 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8313 LISTVIEW_UpdateScroll(infoPtr);
8315 /* refresh all only for lists whose height changed significantly */
8316 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8317 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8318 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8319 LISTVIEW_InvalidateList(infoPtr);
8326 * Sets the size information.
8329 * [I] infoPtr : valid pointer to the listview structure
8334 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8336 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8338 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8340 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8342 if (uView == LVS_LIST)
8344 /* Apparently the "LIST" style is supposed to have the same
8345 * number of items in a column even if there is no scroll bar.
8346 * Since if a scroll bar already exists then the bottom is already
8347 * reduced, only reduce if the scroll bar does not currently exist.
8348 * The "2" is there to mimic the native control. I think it may be
8349 * related to either padding or edges. (GLA 7/2002)
8351 if (!(infoPtr->dwStyle & WS_HSCROLL))
8352 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8353 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8355 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8360 hl.prc = &infoPtr->rcList;
8362 Header_Layout(infoPtr->hwndHeader, &hl);
8364 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8366 infoPtr->rcList.top = max(wp.cy, 0);
8369 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8374 * Processes WM_STYLECHANGED messages.
8377 * [I] infoPtr : valid pointer to the listview structure
8378 * [I] wStyleType : window style type (normal or extended)
8379 * [I] lpss : window style information
8384 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8385 const STYLESTRUCT *lpss)
8387 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8388 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8390 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8391 wStyleType, lpss->styleOld, lpss->styleNew);
8393 if (wStyleType != GWL_STYLE) return 0;
8395 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8396 /* what if LVS_OWNERDATA changed? */
8397 /* or LVS_SINGLESEL */
8398 /* or LVS_SORT{AS,DES}CENDING */
8400 infoPtr->dwStyle = lpss->styleNew;
8402 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8403 ((lpss->styleNew & WS_HSCROLL) == 0))
8404 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8406 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8407 ((lpss->styleNew & WS_VSCROLL) == 0))
8408 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8410 if (uNewView != uOldView)
8412 SIZE oldIconSize = infoPtr->iconSize;
8415 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8416 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8418 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8419 SetRectEmpty(&infoPtr->rcFocus);
8421 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8422 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8424 if (uNewView == LVS_ICON)
8426 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8428 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8429 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8430 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8433 else if (uNewView == LVS_REPORT)
8438 hl.prc = &infoPtr->rcList;
8440 Header_Layout(infoPtr->hwndHeader, &hl);
8441 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8444 LISTVIEW_UpdateItemSize(infoPtr);
8447 if (uNewView == LVS_REPORT)
8448 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8450 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8451 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8452 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8454 /* update the size of the client area */
8455 LISTVIEW_UpdateSize(infoPtr);
8457 /* add scrollbars if needed */
8458 LISTVIEW_UpdateScroll(infoPtr);
8460 /* invalidate client area + erase background */
8461 LISTVIEW_InvalidateList(infoPtr);
8468 * Window procedure of the listview control.
8471 static LRESULT WINAPI
8472 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8474 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8476 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8478 if (!infoPtr && (uMsg != WM_CREATE))
8479 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8483 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8488 case LVM_APPROXIMATEVIEWRECT:
8489 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8490 LOWORD(lParam), HIWORD(lParam));
8492 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8494 /* case LVM_CANCELEDITLABEL: */
8496 /* case LVM_CREATEDRAGIMAGE: */
8498 case LVM_DELETEALLITEMS:
8499 return LISTVIEW_DeleteAllItems(infoPtr);
8501 case LVM_DELETECOLUMN:
8502 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8504 case LVM_DELETEITEM:
8505 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8507 case LVM_EDITLABELW:
8508 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8510 case LVM_EDITLABELA:
8511 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8513 /* case LVM_ENABLEGROUPVIEW: */
8515 case LVM_ENSUREVISIBLE:
8516 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8519 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8522 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8524 case LVM_GETBKCOLOR:
8525 return infoPtr->clrBk;
8527 /* case LVM_GETBKIMAGE: */
8529 case LVM_GETCALLBACKMASK:
8530 return infoPtr->uCallbackMask;
8532 case LVM_GETCOLUMNA:
8533 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8535 case LVM_GETCOLUMNW:
8536 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8538 case LVM_GETCOLUMNORDERARRAY:
8539 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8541 case LVM_GETCOLUMNWIDTH:
8542 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8544 case LVM_GETCOUNTPERPAGE:
8545 return LISTVIEW_GetCountPerPage(infoPtr);
8547 case LVM_GETEDITCONTROL:
8548 return (LRESULT)infoPtr->hwndEdit;
8550 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8551 return infoPtr->dwLvExStyle;
8553 /* case LVM_GETGROUPINFO: */
8555 /* case LVM_GETGROUPMETRICS: */
8558 return (LRESULT)infoPtr->hwndHeader;
8560 case LVM_GETHOTCURSOR:
8561 return (LRESULT)infoPtr->hHotCursor;
8563 case LVM_GETHOTITEM:
8564 return infoPtr->nHotItem;
8566 case LVM_GETHOVERTIME:
8567 return infoPtr->dwHoverTime;
8569 case LVM_GETIMAGELIST:
8570 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8572 /* case LVM_GETINSERTMARK: */
8574 /* case LVM_GETINSERTMARKCOLOR: */
8576 /* case LVM_GETINSERTMARKRECT: */
8578 case LVM_GETISEARCHSTRINGA:
8579 case LVM_GETISEARCHSTRINGW:
8580 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8584 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8587 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8589 case LVM_GETITEMCOUNT:
8590 return infoPtr->nItemCount;
8592 case LVM_GETITEMPOSITION:
8593 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8595 case LVM_GETITEMRECT:
8596 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8598 case LVM_GETITEMSPACING:
8599 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8601 case LVM_GETITEMSTATE:
8602 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8604 case LVM_GETITEMTEXTA:
8605 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8607 case LVM_GETITEMTEXTW:
8608 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8610 case LVM_GETNEXTITEM:
8611 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8613 case LVM_GETNUMBEROFWORKAREAS:
8614 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8618 if (!lParam) return FALSE;
8619 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8622 /* case LVM_GETOUTLINECOLOR: */
8624 /* case LVM_GETSELECTEDCOLUMN: */
8626 case LVM_GETSELECTEDCOUNT:
8627 return LISTVIEW_GetSelectedCount(infoPtr);
8629 case LVM_GETSELECTIONMARK:
8630 return infoPtr->nSelectionMark;
8632 case LVM_GETSTRINGWIDTHA:
8633 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8635 case LVM_GETSTRINGWIDTHW:
8636 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8638 case LVM_GETSUBITEMRECT:
8639 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8641 case LVM_GETTEXTBKCOLOR:
8642 return infoPtr->clrTextBk;
8644 case LVM_GETTEXTCOLOR:
8645 return infoPtr->clrText;
8647 /* case LVM_GETTILEINFO: */
8649 /* case LVM_GETTILEVIEWINFO: */
8651 case LVM_GETTOOLTIPS:
8652 return (LRESULT)infoPtr->hwndToolTip;
8654 case LVM_GETTOPINDEX:
8655 return LISTVIEW_GetTopIndex(infoPtr);
8657 /*case LVM_GETUNICODEFORMAT:
8658 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8661 /* case LVM_GETVIEW: */
8663 case LVM_GETVIEWRECT:
8664 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8666 case LVM_GETWORKAREAS:
8667 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8670 /* case LVM_HASGROUP: */
8673 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8675 case LVM_INSERTCOLUMNA:
8676 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8678 case LVM_INSERTCOLUMNW:
8679 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8681 /* case LVM_INSERTGROUP: */
8683 /* case LVM_INSERTGROUPSORTED: */
8685 case LVM_INSERTITEMA:
8686 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8688 case LVM_INSERTITEMW:
8689 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8691 /* case LVM_INSERTMARKHITTEST: */
8693 /* case LVM_ISGROUPVIEWENABLED: */
8695 /* case LVM_MAPIDTOINDEX: */
8697 /* case LVM_MAPINDEXTOID: */
8699 /* case LVM_MOVEGROUP: */
8701 /* case LVM_MOVEITEMTOGROUP: */
8703 case LVM_REDRAWITEMS:
8704 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8706 /* case LVM_REMOVEALLGROUPS: */
8708 /* case LVM_REMOVEGROUP: */
8711 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8713 case LVM_SETBKCOLOR:
8714 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8716 /* case LVM_SETBKIMAGE: */
8718 case LVM_SETCALLBACKMASK:
8719 infoPtr->uCallbackMask = (UINT)wParam;
8722 case LVM_SETCOLUMNA:
8723 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8725 case LVM_SETCOLUMNW:
8726 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8728 case LVM_SETCOLUMNORDERARRAY:
8729 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8731 case LVM_SETCOLUMNWIDTH:
8732 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8734 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8735 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8737 /* case LVM_SETGROUPINFO: */
8739 /* case LVM_SETGROUPMETRICS: */
8741 case LVM_SETHOTCURSOR:
8742 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8744 case LVM_SETHOTITEM:
8745 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8747 case LVM_SETHOVERTIME:
8748 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8750 case LVM_SETICONSPACING:
8751 return LISTVIEW_SetIconSpacing(infoPtr, SLOWORD(lParam), SHIWORD(lParam));
8753 case LVM_SETIMAGELIST:
8754 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8756 /* case LVM_SETINFOTIP: */
8758 /* case LVM_SETINSERTMARK: */
8760 /* case LVM_SETINSERTMARKCOLOR: */
8763 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8766 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8768 case LVM_SETITEMCOUNT:
8769 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8771 case LVM_SETITEMPOSITION:
8773 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8774 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8777 case LVM_SETITEMPOSITION32:
8778 if (lParam == 0) return FALSE;
8779 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8781 case LVM_SETITEMSTATE:
8782 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8784 case LVM_SETITEMTEXTA:
8785 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8787 case LVM_SETITEMTEXTW:
8788 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8790 /* case LVM_SETOUTLINECOLOR: */
8792 /* case LVM_SETSELECTEDCOLUMN: */
8794 case LVM_SETSELECTIONMARK:
8795 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8797 case LVM_SETTEXTBKCOLOR:
8798 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8800 case LVM_SETTEXTCOLOR:
8801 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8803 /* case LVM_SETTILEINFO: */
8805 /* case LVM_SETTILEVIEWINFO: */
8807 /* case LVM_SETTILEWIDTH: */
8809 case LVM_SETTOOLTIPS:
8810 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
8812 /* case LVM_SETUNICODEFORMAT: */
8814 /* case LVM_SETVIEW: */
8816 /* case LVM_SETWORKAREAS: */
8818 /* case LVM_SORTGROUPS: */
8821 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8823 /* LVM_SORTITEMSEX: */
8825 case LVM_SUBITEMHITTEST:
8826 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8829 return LISTVIEW_Update(infoPtr, (INT)wParam);
8832 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8835 return LISTVIEW_Command(infoPtr, wParam, lParam);
8838 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8841 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8844 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8847 return (LRESULT)infoPtr->hFont;
8850 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8853 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8856 return LISTVIEW_KillFocus(infoPtr);
8858 case WM_LBUTTONDBLCLK:
8859 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8861 case WM_LBUTTONDOWN:
8862 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8865 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8868 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8871 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8874 return LISTVIEW_NCDestroy(infoPtr);
8877 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
8878 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
8881 case WM_NOTIFYFORMAT:
8882 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8885 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8887 case WM_RBUTTONDBLCLK:
8888 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8890 case WM_RBUTTONDOWN:
8891 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8894 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8897 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8902 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8905 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8908 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8911 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8913 case WM_STYLECHANGED:
8914 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8916 case WM_SYSCOLORCHANGE:
8917 COMCTL32_RefreshSysColors();
8920 /* case WM_TIMER: */
8923 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8926 if (wParam & (MK_SHIFT | MK_CONTROL))
8927 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8928 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8930 case WM_WINDOWPOSCHANGED:
8931 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
8933 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8934 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8935 LISTVIEW_UpdateSize(infoPtr);
8936 LISTVIEW_UpdateScroll(infoPtr);
8938 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8940 /* case WM_WININICHANGE: */
8943 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8944 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8947 /* call default window procedure */
8948 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8956 * Registers the window class.
8964 void LISTVIEW_Register(void)
8968 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8969 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8970 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8971 wndClass.cbClsExtra = 0;
8972 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8973 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8974 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8975 wndClass.lpszClassName = WC_LISTVIEWW;
8976 RegisterClassW(&wndClass);
8981 * Unregisters the window class.
8989 void LISTVIEW_Unregister(void)
8991 UnregisterClassW(WC_LISTVIEWW, NULL);
8996 * Handle any WM_COMMAND messages
8999 * [I] infoPtr : valid pointer to the listview structure
9000 * [I] wParam : the first message parameter
9001 * [I] lParam : the second message parameter
9006 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9008 switch (HIWORD(wParam))
9013 * Adjust the edit window size
9016 HDC hdc = GetDC(infoPtr->hwndEdit);
9017 HFONT hFont, hOldFont = 0;
9022 if (!infoPtr->hwndEdit || !hdc) return 0;
9023 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9024 GetWindowRect(infoPtr->hwndEdit, &rect);
9026 /* Select font to get the right dimension of the string */
9027 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9030 hOldFont = SelectObject(hdc, hFont);
9033 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9035 TEXTMETRICW textMetric;
9037 /* Add Extra spacing for the next character */
9038 GetTextMetricsW(hdc, &textMetric);
9039 sz.cx += (textMetric.tmMaxCharWidth * 2);
9047 rect.bottom - rect.top,
9048 SWP_DRAWFRAME|SWP_NOMOVE);
9051 SelectObject(hdc, hOldFont);
9053 ReleaseDC(infoPtr->hwndSelf, hdc);
9059 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
9068 * Subclassed edit control windproc function
9071 * [I] hwnd : the edit window handle
9072 * [I] uMsg : the message that is to be processed
9073 * [I] wParam : first message parameter
9074 * [I] lParam : second message parameter
9075 * [I] isW : TRUE if input is Unicode
9080 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9082 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9083 BOOL cancel = FALSE;
9085 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9086 hwnd, uMsg, wParam, lParam, isW);
9091 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9098 WNDPROC editProc = infoPtr->EditWndProc;
9099 infoPtr->EditWndProc = 0;
9100 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9101 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9105 if (VK_ESCAPE == (INT)wParam)
9110 else if (VK_RETURN == (INT)wParam)
9114 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9118 if (infoPtr->hwndEdit)
9120 LPWSTR buffer = NULL;
9122 infoPtr->hwndEdit = 0;
9125 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9129 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9131 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9132 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9136 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9138 if (buffer) COMCTL32_Free(buffer);
9142 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9148 * Subclassed edit control Unicode windproc function
9151 * [I] hwnd : the edit window handle
9152 * [I] uMsg : the message that is to be processed
9153 * [I] wParam : first message parameter
9154 * [I] lParam : second message parameter
9158 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9160 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9165 * Subclassed edit control ANSI windproc function
9168 * [I] hwnd : the edit window handle
9169 * [I] uMsg : the message that is to be processed
9170 * [I] wParam : first message parameter
9171 * [I] lParam : second message parameter
9175 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9177 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9182 * Creates a subclassed edit cotrol
9185 * [I] infoPtr : valid pointer to the listview structure
9186 * [I] text : initial text for the edit
9187 * [I] style : the window style
9188 * [I] isW : TRUE if input is Unicode
9192 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9193 INT x, INT y, INT width, INT height, BOOL isW)
9195 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9200 TEXTMETRICW textMetric;
9201 HINSTANCE hinst = (HINSTANCE)GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9203 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9205 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9206 hdc = GetDC(infoPtr->hwndSelf);
9208 /* Select the font to get appropriate metric dimensions */
9209 if(infoPtr->hFont != 0)
9210 hOldFont = SelectObject(hdc, infoPtr->hFont);
9212 /*Get String Lenght in pixels */
9213 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9215 /*Add Extra spacing for the next character */
9216 GetTextMetricsW(hdc, &textMetric);
9217 sz.cx += (textMetric.tmMaxCharWidth * 2);
9219 if(infoPtr->hFont != 0)
9220 SelectObject(hdc, hOldFont);
9222 ReleaseDC(infoPtr->hwndSelf, hdc);
9224 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9226 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9228 if (!hedit) return 0;
9230 infoPtr->EditWndProc = (WNDPROC)
9231 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9232 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9234 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);