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"
164 #include "commctrl.h"
165 #include "comctl32.h"
167 #include "wine/debug.h"
168 #include "wine/unicode.h"
170 WINE_DEFAULT_DEBUG_CHANNEL(listview);
172 /* make sure you set this to 0 for production use! */
173 #define DEBUG_RANGES 1
175 typedef struct tagCOLUMN_INFO
177 RECT rcHeader; /* tracks the header's rectangle */
178 int fmt; /* same as LVCOLUMN.fmt */
181 typedef struct tagITEMHDR
185 } ITEMHDR, *LPITEMHDR;
187 typedef struct tagSUBITEM_INFO
193 typedef struct tagITEM_INFO
201 typedef struct tagRANGE
207 typedef struct tagRANGES
212 typedef struct tagITERATOR
221 typedef struct tagLISTVIEW_INFO
228 COLORREF clrTextBkDefault;
229 HIMAGELIST himlNormal;
230 HIMAGELIST himlSmall;
231 HIMAGELIST himlState;
234 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
237 RANGES selectionRanges;
241 RECT rcList; /* This rectangle is really the window
242 * client rectangle possibly reduced by the
243 * horizontal scroll bar and/or header - see
244 * LISTVIEW_UpdateSize. This rectangle offset
245 * by the LISTVIEW_GetOrigin value is in
246 * client coordinates */
255 INT ntmHeight; /* Some cached metrics of the font used */
256 INT ntmAveCharWidth; /* by the listview to draw items */
257 BOOL bRedraw; /* Turns on/off repaints & invalidations */
258 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
260 BOOL bDoChangeNotify; /* send change notification messages? */
263 DWORD dwStyle; /* the cached window GWL_STYLE */
264 DWORD dwLvExStyle; /* extended listview style */
265 INT nItemCount; /* the number of items in the list */
266 HDPA hdpaItems; /* array ITEM_INFO pointers */
267 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
268 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
269 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
270 POINT currIconPos; /* this is the position next icon will be placed */
271 PFNLVCOMPARE pfnCompare;
279 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
281 DWORD lastKeyPressTimestamp;
283 INT nSearchParamLength;
284 WCHAR szSearchParam[ MAX_PATH ];
291 /* How many we debug buffer to allocate */
292 #define DEBUG_BUFFERS 20
293 /* The size of a single debug bbuffer */
294 #define DEBUG_BUFFER_SIZE 256
296 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
297 #define SB_INTERNAL -1
299 /* maximum size of a label */
300 #define DISP_TEXT_SIZE 512
302 /* padding for items in list and small icon display modes */
303 #define WIDTH_PADDING 12
305 /* padding for items in list, report and small icon display modes */
306 #define HEIGHT_PADDING 1
308 /* offset of items in report display mode */
309 #define REPORT_MARGINX 2
311 /* padding for icon in large icon display mode
312 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
313 * that HITTEST will see.
314 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
315 * ICON_TOP_PADDING - sum of the two above.
316 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
317 * LABEL_HOR_PADDING - between text and sides of box
318 * LABEL_VERT_PADDING - between bottom of text and end of box
320 * ICON_LR_PADDING - additional width above icon size.
321 * ICON_LR_HALF - half of the above value
323 #define ICON_TOP_PADDING_NOTHITABLE 2
324 #define ICON_TOP_PADDING_HITABLE 2
325 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
326 #define ICON_BOTTOM_PADDING 4
327 #define LABEL_HOR_PADDING 5
328 #define LABEL_VERT_PADDING 7
329 #define ICON_LR_PADDING 16
330 #define ICON_LR_HALF (ICON_LR_PADDING/2)
332 /* default label width for items in list and small icon display modes */
333 #define DEFAULT_LABEL_WIDTH 40
335 /* default column width for items in list display mode */
336 #define DEFAULT_COLUMN_WIDTH 128
338 /* Size of "line" scroll for V & H scrolls */
339 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
341 /* Padding betwen image and label */
342 #define IMAGE_PADDING 2
344 /* Padding behind the label */
345 #define TRAILING_LABEL_PADDING 12
346 #define TRAILING_HEADER_PADDING 11
348 /* Border for the icon caption */
349 #define CAPTION_BORDER 2
351 /* Standard DrawText flags */
352 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
353 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
354 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
356 /* The time in milliseconds to reset the search in the list */
357 #define KEY_DELAY 450
359 /* Dump the LISTVIEW_INFO structure to the debug channel */
360 #define LISTVIEW_DUMP(iP) do { \
361 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
362 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
363 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
364 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
365 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
366 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
367 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
368 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
369 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
370 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
374 * forward declarations
376 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
377 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
378 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
379 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
380 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
381 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
382 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
383 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
384 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
385 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
386 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
387 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
388 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
389 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
390 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
391 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
392 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
393 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
394 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
395 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
396 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
397 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
398 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
399 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
400 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
402 /******** Text handling functions *************************************/
404 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
405 * text string. The string may be ANSI or Unicode, in which case
406 * the boolean isW tells us the type of the string.
408 * The name of the function tell what type of strings it expects:
409 * W: Unicode, T: ANSI/Unicode - function of isW
412 static inline BOOL is_textW(LPCWSTR text)
414 return text != NULL && text != LPSTR_TEXTCALLBACKW;
417 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
419 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
420 return is_textW(text);
423 static inline int textlenT(LPCWSTR text, BOOL isW)
425 return !is_textT(text, isW) ? 0 :
426 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
429 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
432 if (isSrcW) lstrcpynW(dest, src, max);
433 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
435 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
436 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
439 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
441 LPWSTR wstr = (LPWSTR)text;
443 if (!isW && is_textT(text, isW))
445 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
446 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
447 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
449 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
453 static inline void textfreeT(LPWSTR wstr, BOOL isW)
455 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
459 * dest is a pointer to a Unicode string
460 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
462 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
466 if (src == LPSTR_TEXTCALLBACKW)
468 if (is_textW(*dest)) Free(*dest);
469 *dest = LPSTR_TEXTCALLBACKW;
473 LPWSTR pszText = textdupTtoW(src, isW);
474 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
475 bResult = Str_SetPtrW(dest, pszText);
476 textfreeT(pszText, isW);
482 * compares a Unicode to a Unicode/ANSI text string
484 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
486 if (!aw) return bt ? -1 : 0;
487 if (!bt) return aw ? 1 : 0;
488 if (aw == LPSTR_TEXTCALLBACKW)
489 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
490 if (bt != LPSTR_TEXTCALLBACKW)
492 LPWSTR bw = textdupTtoW(bt, isW);
493 int r = bw ? lstrcmpW(aw, bw) : 1;
501 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
505 n = min(min(n, strlenW(s1)), strlenW(s2));
506 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
507 return res ? res - sizeof(WCHAR) : res;
510 /******** Debugging functions *****************************************/
512 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
514 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
515 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
518 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
520 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
521 n = min(textlenT(text, isW), n);
522 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
525 static char* debug_getbuf()
527 static int index = 0;
528 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
529 return buffers[index++ % DEBUG_BUFFERS];
532 static inline const char* debugrange(const RANGE *lprng)
536 char* buf = debug_getbuf();
537 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
539 } else return "(null)";
542 static inline const char* debugpoint(const POINT *lppt)
546 char* buf = debug_getbuf();
547 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
549 } else return "(null)";
552 static inline const char* debugrect(const RECT *rect)
556 char* buf = debug_getbuf();
557 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
558 rect->left, rect->top, rect->right, rect->bottom);
560 } else return "(null)";
563 static const char * debugscrollinfo(const SCROLLINFO *pScrollInfo)
565 char* buf = debug_getbuf(), *text = buf;
566 int len, size = DEBUG_BUFFER_SIZE;
568 if (pScrollInfo == NULL) return "(null)";
569 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
570 if (len == -1) goto end; buf += len; size -= len;
571 if (pScrollInfo->fMask & SIF_RANGE)
572 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
574 if (len == -1) goto end; buf += len; size -= len;
575 if (pScrollInfo->fMask & SIF_PAGE)
576 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
578 if (len == -1) goto end; buf += len; size -= len;
579 if (pScrollInfo->fMask & SIF_POS)
580 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
582 if (len == -1) goto end; buf += len; size -= len;
583 if (pScrollInfo->fMask & SIF_TRACKPOS)
584 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
586 if (len == -1) goto end; buf += len; size -= len;
589 buf = text + strlen(text);
591 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
595 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
599 char* buf = debug_getbuf();
600 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
601 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
602 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
603 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
605 } else return "(null)";
608 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
610 char* buf = debug_getbuf(), *text = buf;
611 int len, size = DEBUG_BUFFER_SIZE;
613 if (lpLVItem == NULL) return "(null)";
614 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
615 if (len == -1) goto end; buf += len; size -= len;
616 if (lpLVItem->mask & LVIF_STATE)
617 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
619 if (len == -1) goto end; buf += len; size -= len;
620 if (lpLVItem->mask & LVIF_TEXT)
621 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
623 if (len == -1) goto end; buf += len; size -= len;
624 if (lpLVItem->mask & LVIF_IMAGE)
625 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
627 if (len == -1) goto end; buf += len; size -= len;
628 if (lpLVItem->mask & LVIF_PARAM)
629 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
631 if (len == -1) goto end; buf += len; size -= len;
632 if (lpLVItem->mask & LVIF_INDENT)
633 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
635 if (len == -1) goto end; buf += len; size -= len;
638 buf = text + strlen(text);
640 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
644 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
646 char* buf = debug_getbuf(), *text = buf;
647 int len, size = DEBUG_BUFFER_SIZE;
649 if (lpColumn == NULL) return "(null)";
650 len = snprintf(buf, size, "{");
651 if (len == -1) goto end; buf += len; size -= len;
652 if (lpColumn->mask & LVCF_SUBITEM)
653 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
655 if (len == -1) goto end; buf += len; size -= len;
656 if (lpColumn->mask & LVCF_FMT)
657 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
659 if (len == -1) goto end; buf += len; size -= len;
660 if (lpColumn->mask & LVCF_WIDTH)
661 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
663 if (len == -1) goto end; buf += len; size -= len;
664 if (lpColumn->mask & LVCF_TEXT)
665 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
667 if (len == -1) goto end; buf += len; size -= len;
668 if (lpColumn->mask & LVCF_IMAGE)
669 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
671 if (len == -1) goto end; buf += len; size -= len;
672 if (lpColumn->mask & LVCF_ORDER)
673 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
675 if (len == -1) goto end; buf += len; size -= len;
678 buf = text + strlen(text);
680 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
684 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
688 char* buf = debug_getbuf();
689 snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
690 debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
692 } else return "(null)";
695 /* Return the corresponding text for a given scroll value */
696 static inline LPCSTR debugscrollcode(int nScrollCode)
700 case SB_LINELEFT: return "SB_LINELEFT";
701 case SB_LINERIGHT: return "SB_LINERIGHT";
702 case SB_PAGELEFT: return "SB_PAGELEFT";
703 case SB_PAGERIGHT: return "SB_PAGERIGHT";
704 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
705 case SB_THUMBTRACK: return "SB_THUMBTRACK";
706 case SB_ENDSCROLL: return "SB_ENDSCROLL";
707 case SB_INTERNAL: return "SB_INTERNAL";
708 default: return "unknown";
713 /******** Notification functions i************************************/
715 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
719 TRACE("(code=%d)\n", code);
721 pnmh->hwndFrom = infoPtr->hwndSelf;
722 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
724 result = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
725 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
727 TRACE(" <= %ld\n", result);
732 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
735 return notify_hdr(infoPtr, code, &nmh);
738 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
749 item.mask = LVIF_PARAM|LVIF_STATE;
750 item.iItem = htInfo->iItem;
752 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
753 nmia.lParam = item.lParam;
754 nmia.uOldState = item.state;
755 nmia.uNewState = item.state | LVIS_ACTIVATING;
756 nmia.uChanged = LVIF_STATE;
759 nmia.iItem = htInfo->iItem;
760 nmia.iSubItem = htInfo->iSubItem;
761 nmia.ptAction = htInfo->pt;
763 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
764 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
765 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
767 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
770 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
772 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
773 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
776 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
781 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
782 ZeroMemory(&nmlv, sizeof(nmlv));
783 nmlv.iItem = lvht->iItem;
784 nmlv.iSubItem = lvht->iSubItem;
785 nmlv.ptAction = lvht->pt;
786 item.mask = LVIF_PARAM;
787 item.iItem = lvht->iItem;
789 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
790 return notify_listview(infoPtr, code, &nmlv);
793 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
798 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
800 item.mask = LVIF_PARAM;
803 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
804 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
807 static int get_ansi_notification(INT unicodeNotificationCode)
809 switch (unicodeNotificationCode)
811 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
812 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
813 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
814 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
815 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
816 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
818 ERR("unknown notification %x\n", unicodeNotificationCode);
824 With testing on Windows 2000 it looks like the notify format
825 has nothing to do with this message. It ALWAYS seems to be
828 infoPtr : listview struct
829 notificationCode : *Unicode* notification code
830 pdi : dispinfo structure (can be unicode or ansi)
831 isW : TRUE if dispinfo is Unicode
833 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
835 BOOL bResult = FALSE;
836 BOOL convertToAnsi = FALSE;
837 INT cchTempBufMax = 0, savCchTextMax = 0;
838 LPWSTR pszTempBuf = NULL, savPszText = NULL;
840 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
845 if (notificationCode != LVN_GETDISPINFOW)
847 cchTempBufMax = WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText,
848 -1, NULL, 0, NULL, NULL);
852 cchTempBufMax = pdi->item.cchTextMax;
853 *pdi->item.pszText = 0; /* make sure we don't process garbage */
856 pszTempBuf = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) *
858 if (!pszTempBuf) return FALSE;
860 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR)
861 pszTempBuf, cchTempBufMax, NULL, NULL);
863 savCchTextMax = pdi->item.cchTextMax;
864 savPszText = pdi->item.pszText;
865 pdi->item.pszText = pszTempBuf;
866 pdi->item.cchTextMax = cchTempBufMax;
869 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat !=
872 bResult = notify_hdr(infoPtr, get_ansi_notification(notificationCode),
877 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
878 savPszText, savCchTextMax);
879 pdi->item.pszText = savPszText; /* restores our buffer */
880 pdi->item.cchTextMax = savCchTextMax;
881 HeapFree(GetProcessHeap(), 0, pszTempBuf);
886 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
887 const RECT *rcBounds, const LVITEMW *lplvItem)
889 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
890 lpnmlvcd->nmcd.hdc = hdc;
891 lpnmlvcd->nmcd.rc = *rcBounds;
892 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
893 lpnmlvcd->clrText = infoPtr->clrText;
894 if (!lplvItem) return;
895 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
896 lpnmlvcd->iSubItem = lplvItem->iSubItem;
897 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
898 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
899 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
900 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
903 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
905 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
908 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
909 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
910 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
911 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
912 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
913 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
917 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
919 /* apprently, for selected items, we have to override the returned values */
920 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
924 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
925 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
927 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
929 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
930 lpnmlvcd->clrText = comctl32_color.clrBtnText;
934 /* Set the text attributes */
935 if (lpnmlvcd->clrTextBk != CLR_NONE)
937 SetBkMode(hdc, OPAQUE);
938 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
939 SetBkColor(hdc, infoPtr->clrTextBkDefault);
941 SetBkColor(hdc,lpnmlvcd->clrTextBk);
944 SetBkMode(hdc, TRANSPARENT);
945 SetTextColor(hdc, lpnmlvcd->clrText);
948 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
950 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
953 /******** Item iterator functions **********************************/
955 static RANGES ranges_create(int count);
956 static void ranges_destroy(RANGES ranges);
957 static BOOL ranges_add(RANGES ranges, RANGE range);
958 static BOOL ranges_del(RANGES ranges, RANGE range);
959 static void ranges_dump(RANGES ranges);
961 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
963 RANGE range = { nItem, nItem + 1 };
965 return ranges_add(ranges, range);
968 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
970 RANGE range = { nItem, nItem + 1 };
972 return ranges_del(ranges, range);
976 * ITERATOR DOCUMENTATION
978 * The iterator functions allow for easy, and convenient iteration
979 * over items of iterest in the list. Typically, you create a
980 * iterator, use it, and destroy it, as such:
983 * iterator_xxxitems(&i, ...);
984 * while (iterator_{prev,next}(&i)
986 * //code which uses i.nItem
988 * iterator_destroy(&i);
990 * where xxx is either: framed, or visible.
991 * Note that it is important that the code destroys the iterator
992 * after it's done with it, as the creation of the iterator may
993 * allocate memory, which thus needs to be freed.
995 * You can iterate both forwards, and backwards through the list,
996 * by using iterator_next or iterator_prev respectively.
998 * Lower numbered items are draw on top of higher number items in
999 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1000 * items may overlap). So, to test items, you should use
1002 * which lists the items top to bottom (in Z-order).
1003 * For drawing items, you should use
1005 * which lists the items bottom to top (in Z-order).
1006 * If you keep iterating over the items after the end-of-items
1007 * marker (-1) is returned, the iterator will start from the
1008 * beginning. Typically, you don't need to test for -1,
1009 * because iterator_{next,prev} will return TRUE if more items
1010 * are to be iterated over, or FALSE otherwise.
1012 * Note: the iterator is defined to be bidirectional. That is,
1013 * any number of prev followed by any number of next, or
1014 * five versa, should leave the iterator at the same item:
1015 * prev * n, next * n = next * n, prev * n
1017 * The iterator has a notion of a out-of-order, special item,
1018 * which sits at the start of the list. This is used in
1019 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1020 * which needs to be first, as it may overlap other items.
1022 * The code is a bit messy because we have:
1023 * - a special item to deal with
1024 * - simple range, or composite range
1026 * If you find bugs, or want to add features, please make sure you
1027 * always check/modify *both* iterator_prev, and iterator_next.
1031 * This function iterates through the items in increasing order,
1032 * but prefixed by the special item, then -1. That is:
1033 * special, 1, 2, 3, ..., n, -1.
1034 * Each item is listed only once.
1036 static inline BOOL iterator_next(ITERATOR* i)
1040 i->nItem = i->nSpecial;
1041 if (i->nItem != -1) return TRUE;
1043 if (i->nItem == i->nSpecial)
1045 if (i->ranges) i->index = 0;
1051 if (i->nItem == i->nSpecial) i->nItem++;
1052 if (i->nItem < i->range.upper) return TRUE;
1057 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1058 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1061 else if (i->nItem >= i->range.upper) goto end;
1063 i->nItem = i->range.lower;
1064 if (i->nItem >= 0) goto testitem;
1071 * This function iterates through the items in decreasing order,
1072 * followed by the special item, then -1. That is:
1073 * n, n-1, ..., 3, 2, 1, special, -1.
1074 * Each item is listed only once.
1076 static inline BOOL iterator_prev(ITERATOR* i)
1083 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1086 if (i->nItem == i->nSpecial)
1094 if (i->nItem == i->nSpecial) i->nItem--;
1095 if (i->nItem >= i->range.lower) return TRUE;
1101 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1104 else if (!start && i->nItem < i->range.lower) goto end;
1106 i->nItem = i->range.upper;
1107 if (i->nItem > 0) goto testitem;
1109 return (i->nItem = i->nSpecial) != -1;
1112 static RANGE iterator_range(ITERATOR* i)
1116 if (!i->ranges) return i->range;
1118 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1119 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1124 * Releases resources associated with this ierator.
1126 static inline void iterator_destroy(ITERATOR* i)
1128 ranges_destroy(i->ranges);
1132 * Create an empty iterator.
1134 static inline BOOL iterator_empty(ITERATOR* i)
1136 ZeroMemory(i, sizeof(*i));
1137 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1142 * Create an iterator over a range.
1144 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1152 * Create an iterator over a bunch of ranges.
1153 * Please note that the iterator will take ownership of the ranges,
1154 * and will free them upon destruction.
1156 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1164 * Creates an iterator over the items which intersect lprc.
1166 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1168 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1169 RECT frame = *lprc, rcItem, rcTemp;
1172 /* in case we fail, we want to return an empty iterator */
1173 if (!iterator_empty(i)) return FALSE;
1175 LISTVIEW_GetOrigin(infoPtr, &Origin);
1177 TRACE("(lprc=%s)\n", debugrect(lprc));
1178 OffsetRect(&frame, -Origin.x, -Origin.y);
1180 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1184 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1186 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1187 if (IntersectRect(&rcTemp, &rcItem, lprc))
1188 i->nSpecial = infoPtr->nFocusedItem;
1190 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1191 /* to do better here, we need to have PosX, and PosY sorted */
1192 TRACE("building icon ranges:\n");
1193 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1195 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1196 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1197 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1198 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1199 if (IntersectRect(&rcTemp, &rcItem, &frame))
1200 ranges_additem(i->ranges, nItem);
1204 else if (uView == LVS_REPORT)
1208 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1209 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1211 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1212 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1213 if (range.upper <= range.lower) return TRUE;
1214 if (!iterator_rangeitems(i, range)) return FALSE;
1215 TRACE(" report=%s\n", debugrange(&i->range));
1219 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1220 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1221 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1222 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1223 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1224 INT lower = nFirstCol * nPerCol + nFirstRow;
1228 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1229 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1231 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1233 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1234 TRACE("building list ranges:\n");
1235 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1237 item_range.lower = nCol * nPerCol + nFirstRow;
1238 if(item_range.lower >= infoPtr->nItemCount) break;
1239 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1240 TRACE(" list=%s\n", debugrange(&item_range));
1241 ranges_add(i->ranges, item_range);
1249 * Creates an iterator over the items which intersect the visible region of hdc.
1251 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1253 POINT Origin, Position;
1254 RECT rcItem, rcClip;
1257 rgntype = GetClipBox(hdc, &rcClip);
1258 if (rgntype == NULLREGION) return iterator_empty(i);
1259 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1260 if (rgntype == SIMPLEREGION) return TRUE;
1262 /* first deal with the special item */
1263 if (i->nSpecial != -1)
1265 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1266 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1269 /* if we can't deal with the region, we'll just go with the simple range */
1270 LISTVIEW_GetOrigin(infoPtr, &Origin);
1271 TRACE("building visible range:\n");
1272 if (!i->ranges && i->range.lower < i->range.upper)
1274 if (!(i->ranges = ranges_create(50))) return TRUE;
1275 if (!ranges_add(i->ranges, i->range))
1277 ranges_destroy(i->ranges);
1283 /* now delete the invisible items from the list */
1284 while(iterator_next(i))
1286 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1287 rcItem.left = Position.x + Origin.x;
1288 rcItem.top = Position.y + Origin.y;
1289 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1290 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1291 if (!RectVisible(hdc, &rcItem))
1292 ranges_delitem(i->ranges, i->nItem);
1294 /* the iterator should restart on the next iterator_next */
1300 /******** Misc helper functions ************************************/
1302 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1303 WPARAM wParam, LPARAM lParam, BOOL isW)
1305 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1306 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1309 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1311 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1313 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1314 (uView == LVS_ICON || uView == LVS_SMALLICON);
1317 /******** Internal API functions ************************************/
1319 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1321 static COLUMN_INFO mainItem;
1323 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1324 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1325 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1328 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1330 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1333 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1335 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1338 /* Listview invalidation functions: use _only_ these functions to invalidate */
1340 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1342 return infoPtr->bRedraw;
1345 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1347 if(!is_redrawing(infoPtr)) return;
1348 TRACE(" invalidating rect=%s\n", debugrect(rect));
1349 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1352 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1356 if(!is_redrawing(infoPtr)) return;
1357 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1358 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1361 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1363 POINT Origin, Position;
1366 if(!is_redrawing(infoPtr)) return;
1367 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1368 LISTVIEW_GetOrigin(infoPtr, &Origin);
1369 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1370 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1372 rcBox.bottom = infoPtr->nItemHeight;
1373 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1374 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1377 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1379 LISTVIEW_InvalidateRect(infoPtr, NULL);
1382 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1386 if(!is_redrawing(infoPtr)) return;
1387 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1388 rcCol.top = infoPtr->rcList.top;
1389 rcCol.bottom = infoPtr->rcList.bottom;
1390 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1395 * Retrieves the number of items that can fit vertically in the client area.
1398 * [I] infoPtr : valid pointer to the listview structure
1401 * Number of items per row.
1403 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1405 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1407 return max(nListWidth/infoPtr->nItemWidth, 1);
1412 * Retrieves the number of items that can fit horizontally in the client
1416 * [I] infoPtr : valid pointer to the listview structure
1419 * Number of items per column.
1421 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1423 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1425 return max(nListHeight / infoPtr->nItemHeight, 1);
1429 /*************************************************************************
1430 * LISTVIEW_ProcessLetterKeys
1432 * Processes keyboard messages generated by pressing the letter keys
1434 * What this does is perform a case insensitive search from the
1435 * current position with the following quirks:
1436 * - If two chars or more are pressed in quick succession we search
1437 * for the corresponding string (e.g. 'abc').
1438 * - If there is a delay we wipe away the current search string and
1439 * restart with just that char.
1440 * - If the user keeps pressing the same character, whether slowly or
1441 * fast, so that the search string is entirely composed of this
1442 * character ('aaaaa' for instance), then we search for first item
1443 * that starting with that character.
1444 * - If the user types the above character in quick succession, then
1445 * we must also search for the corresponding string ('aaaaa'), and
1446 * go to that string if there is a match.
1449 * [I] hwnd : handle to the window
1450 * [I] charCode : the character code, the actual character
1451 * [I] keyData : key data
1459 * - The current implementation has a list of characters it will
1460 * accept and it ignores averything else. In particular it will
1461 * ignore accentuated characters which seems to match what
1462 * Windows does. But I'm not sure it makes sense to follow
1464 * - We don't sound a beep when the search fails.
1468 * TREEVIEW_ProcessLetterKeys
1470 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1475 WCHAR buffer[MAX_PATH];
1476 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1478 /* simple parameter checking */
1479 if (!charCode || !keyData) return 0;
1481 /* only allow the valid WM_CHARs through */
1482 if (!isalnum(charCode) &&
1483 charCode != '.' && charCode != '`' && charCode != '!' &&
1484 charCode != '@' && charCode != '#' && charCode != '$' &&
1485 charCode != '%' && charCode != '^' && charCode != '&' &&
1486 charCode != '*' && charCode != '(' && charCode != ')' &&
1487 charCode != '-' && charCode != '_' && charCode != '+' &&
1488 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1489 charCode != '}' && charCode != '[' && charCode != '{' &&
1490 charCode != '/' && charCode != '?' && charCode != '>' &&
1491 charCode != '<' && charCode != ',' && charCode != '~')
1494 /* if there's one item or less, there is no where to go */
1495 if (infoPtr->nItemCount <= 1) return 0;
1497 /* update the search parameters */
1498 infoPtr->lastKeyPressTimestamp = GetTickCount();
1499 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1500 if (infoPtr->nSearchParamLength < MAX_PATH)
1501 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1502 if (infoPtr->charCode != charCode)
1503 infoPtr->charCode = charCode = 0;
1505 infoPtr->charCode=charCode;
1506 infoPtr->szSearchParam[0]=charCode;
1507 infoPtr->nSearchParamLength=1;
1508 /* Redundant with the 1 char string */
1512 /* and search from the current position */
1514 if (infoPtr->nFocusedItem >= 0) {
1515 endidx=infoPtr->nFocusedItem;
1517 /* if looking for single character match,
1518 * then we must always move forward
1520 if (infoPtr->nSearchParamLength == 1)
1523 endidx=infoPtr->nItemCount;
1527 if (idx == infoPtr->nItemCount) {
1528 if (endidx == infoPtr->nItemCount || endidx == 0)
1534 item.mask = LVIF_TEXT;
1537 item.pszText = buffer;
1538 item.cchTextMax = MAX_PATH;
1539 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1541 /* check for a match */
1542 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1545 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1546 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1547 /* This would work but we must keep looking for a longer match */
1551 } while (idx != endidx);
1554 LISTVIEW_KeySelection(infoPtr, nItem);
1559 /*************************************************************************
1560 * LISTVIEW_UpdateHeaderSize [Internal]
1562 * Function to resize the header control
1565 * [I] hwnd : handle to a window
1566 * [I] nNewScrollPos : scroll pos to set
1571 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1576 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1578 GetWindowRect(infoPtr->hwndHeader, &winRect);
1579 point[0].x = winRect.left;
1580 point[0].y = winRect.top;
1581 point[1].x = winRect.right;
1582 point[1].y = winRect.bottom;
1584 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1585 point[0].x = -nNewScrollPos;
1586 point[1].x += nNewScrollPos;
1588 SetWindowPos(infoPtr->hwndHeader,0,
1589 point[0].x,point[0].y,point[1].x,point[1].y,
1590 SWP_NOZORDER | SWP_NOACTIVATE);
1595 * Update the scrollbars. This functions should be called whenever
1596 * the content, size or view changes.
1599 * [I] infoPtr : valid pointer to the listview structure
1604 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1606 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1607 SCROLLINFO horzInfo, vertInfo;
1609 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1611 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1612 horzInfo.cbSize = sizeof(SCROLLINFO);
1613 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1615 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1616 if (uView == LVS_LIST)
1618 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1619 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1621 /* scroll by at least one column per page */
1622 if(horzInfo.nPage < infoPtr->nItemWidth)
1623 horzInfo.nPage = infoPtr->nItemWidth;
1625 horzInfo.nPage /= infoPtr->nItemWidth;
1627 else if (uView == LVS_REPORT)
1629 horzInfo.nMax = infoPtr->nItemWidth;
1631 else /* LVS_ICON, or LVS_SMALLICON */
1635 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1638 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1639 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1640 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1641 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1643 /* Setting the horizontal scroll can change the listview size
1644 * (and potentially everything else) so we need to recompute
1645 * everything again for the vertical scroll
1648 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1649 vertInfo.cbSize = sizeof(SCROLLINFO);
1650 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1652 if (uView == LVS_REPORT)
1654 vertInfo.nMax = infoPtr->nItemCount;
1656 /* scroll by at least one page */
1657 if(vertInfo.nPage < infoPtr->nItemHeight)
1658 vertInfo.nPage = infoPtr->nItemHeight;
1660 vertInfo.nPage /= infoPtr->nItemHeight;
1662 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1666 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1669 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1670 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1671 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1672 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1674 /* Update the Header Control */
1675 if (uView == LVS_REPORT)
1677 horzInfo.fMask = SIF_POS;
1678 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1679 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1686 * Shows/hides the focus rectangle.
1689 * [I] infoPtr : valid pointer to the listview structure
1690 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1695 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1697 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1700 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1702 if (infoPtr->nFocusedItem < 0) return;
1704 /* we need some gymnastics in ICON mode to handle large items */
1705 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1709 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1710 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1712 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1717 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1719 /* for some reason, owner draw should work only in report mode */
1720 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1725 item.iItem = infoPtr->nFocusedItem;
1727 item.mask = LVIF_PARAM;
1728 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1730 ZeroMemory(&dis, sizeof(dis));
1731 dis.CtlType = ODT_LISTVIEW;
1732 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1733 dis.itemID = item.iItem;
1734 dis.itemAction = ODA_FOCUS;
1735 if (fShow) dis.itemState |= ODS_FOCUS;
1736 dis.hwndItem = infoPtr->hwndSelf;
1738 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1739 dis.itemData = item.lParam;
1741 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1745 DrawFocusRect(hdc, &infoPtr->rcFocus);
1748 ReleaseDC(infoPtr->hwndSelf, hdc);
1752 * Invalidates all visible selected items.
1754 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1758 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1759 while(iterator_next(&i))
1761 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1762 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1764 iterator_destroy(&i);
1769 * DESCRIPTION: [INTERNAL]
1770 * Computes an item's (left,top) corner, relative to rcView.
1771 * That is, the position has NOT been made relative to the Origin.
1772 * This is deliberate, to avoid computing the Origin over, and
1773 * over again, when this function is call in a loop. Instead,
1774 * one ca factor the computation of the Origin before the loop,
1775 * and offset the value retured by this function, on every iteration.
1778 * [I] infoPtr : valid pointer to the listview structure
1779 * [I] nItem : item number
1780 * [O] lpptOrig : item top, left corner
1785 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1787 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1789 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1791 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1793 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1794 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1796 else if (uView == LVS_LIST)
1798 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1799 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1800 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1802 else /* LVS_REPORT */
1804 lpptPosition->x = 0;
1805 lpptPosition->y = nItem * infoPtr->nItemHeight;
1810 * DESCRIPTION: [INTERNAL]
1811 * Compute the rectangles of an item. This is to localize all
1812 * the computations in one place. If you are not interested in some
1813 * of these values, simply pass in a NULL -- the fucntion is smart
1814 * enough to compute only what's necessary. The function computes
1815 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1816 * one, the BOX rectangle. This rectangle is very cheap to compute,
1817 * and is guaranteed to contain all the other rectangles. Computing
1818 * the ICON rect is also cheap, but all the others are potentaily
1819 * expensive. This gives an easy and effective optimization when
1820 * searching (like point inclusion, or rectangle intersection):
1821 * first test against the BOX, and if TRUE, test agains the desired
1823 * If the function does not have all the necessary information
1824 * to computed the requested rectangles, will crash with a
1825 * failed assertion. This is done so we catch all programming
1826 * errors, given that the function is called only from our code.
1828 * We have the following 'special' meanings for a few fields:
1829 * * If LVIS_FOCUSED is set, we assume the item has the focus
1830 * This is important in ICON mode, where it might get a larger
1831 * then usual rectange
1833 * Please note that subitem support works only in REPORT mode.
1836 * [I] infoPtr : valid pointer to the listview structure
1837 * [I] lpLVItem : item to compute the measures for
1838 * [O] lprcBox : ptr to Box rectangle
1839 * The internal LVIR_BOX rectangle
1840 * [0] lprcState : ptr to State icon rectangle
1841 * The internal LVIR_STATE rectangle
1842 * [O] lprcIcon : ptr to Icon rectangle
1843 * Same as LVM_GETITEMRECT with LVIR_ICON
1844 * [O] lprcLabel : ptr to Label rectangle
1845 * Same as LVM_GETITEMRECT with LVIR_LABEL
1850 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1851 LPRECT lprcBox, LPRECT lprcState,
1852 LPRECT lprcIcon, LPRECT lprcLabel)
1854 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1855 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1856 RECT Box, State, Icon, Label;
1857 COLUMN_INFO *lpColumnInfo = NULL;
1859 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1861 /* Be smart and try to figure out the minimum we have to do */
1862 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1863 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1865 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1866 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1868 if (lprcLabel) doLabel = TRUE;
1869 if (doLabel || lprcIcon) doIcon = TRUE;
1870 if (doIcon || lprcState) doState = TRUE;
1872 /************************************************************/
1873 /* compute the box rectangle (it should be cheap to do) */
1874 /************************************************************/
1875 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1876 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1878 if (lpLVItem->iSubItem)
1880 Box = lpColumnInfo->rcHeader;
1885 Box.right = infoPtr->nItemWidth;
1888 Box.bottom = infoPtr->nItemHeight;
1890 /************************************************************/
1891 /* compute STATEICON bounding box */
1892 /************************************************************/
1895 if (uView == LVS_ICON)
1897 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1898 if (infoPtr->himlNormal)
1899 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1900 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1904 /* we need the ident in report mode, if we don't have it, we fail */
1905 State.left = Box.left;
1906 if (uView == LVS_REPORT)
1908 if (lpLVItem->iSubItem == 0)
1910 State.left += REPORT_MARGINX;
1911 assert(lpLVItem->mask & LVIF_INDENT);
1912 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1915 State.top = Box.top;
1917 State.right = State.left;
1918 State.bottom = State.top;
1919 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1921 State.right += infoPtr->iconStateSize.cx;
1922 State.bottom += infoPtr->iconStateSize.cy;
1924 if (lprcState) *lprcState = State;
1925 TRACE(" - state=%s\n", debugrect(&State));
1928 /************************************************************/
1929 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1930 /************************************************************/
1933 if (uView == LVS_ICON)
1935 Icon.left = Box.left;
1936 if (infoPtr->himlNormal)
1937 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1938 Icon.top = Box.top + ICON_TOP_PADDING;
1939 Icon.right = Icon.left;
1940 Icon.bottom = Icon.top;
1941 if (infoPtr->himlNormal)
1943 Icon.right += infoPtr->iconSize.cx;
1944 Icon.bottom += infoPtr->iconSize.cy;
1947 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1949 Icon.left = State.right;
1951 Icon.right = Icon.left;
1952 if (infoPtr->himlSmall && (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE)))
1953 Icon.right += infoPtr->iconSize.cx;
1954 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1956 if(lprcIcon) *lprcIcon = Icon;
1957 TRACE(" - icon=%s\n", debugrect(&Icon));
1960 /************************************************************/
1961 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1962 /************************************************************/
1965 SIZE labelSize = { 0, 0 };
1967 /* calculate how far to the right can the label strech */
1968 Label.right = Box.right;
1969 if (uView == LVS_REPORT)
1971 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1974 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1976 labelSize.cx = infoPtr->nItemWidth;
1977 labelSize.cy = infoPtr->nItemHeight;
1981 /* we need the text in non owner draw mode */
1982 assert(lpLVItem->mask & LVIF_TEXT);
1983 if (is_textT(lpLVItem->pszText, TRUE))
1985 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1986 HDC hdc = GetDC(infoPtr->hwndSelf);
1987 HFONT hOldFont = SelectObject(hdc, hFont);
1991 /* compute rough rectangle where the label will go */
1992 SetRectEmpty(&rcText);
1993 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
1994 rcText.bottom = infoPtr->nItemHeight;
1995 if (uView == LVS_ICON)
1996 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1998 /* now figure out the flags */
1999 if (uView == LVS_ICON)
2000 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2002 uFormat = LV_SL_DT_FLAGS;
2004 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2006 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2007 labelSize.cy = rcText.bottom - rcText.top;
2009 SelectObject(hdc, hOldFont);
2010 ReleaseDC(infoPtr->hwndSelf, hdc);
2014 if (uView == LVS_ICON)
2016 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2017 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2018 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2019 Label.right = Label.left + labelSize.cx;
2020 Label.bottom = Label.top + infoPtr->nItemHeight;
2021 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2023 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2024 labelSize.cy /= infoPtr->ntmHeight;
2025 labelSize.cy = max(labelSize.cy, 1);
2026 labelSize.cy *= infoPtr->ntmHeight;
2028 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2030 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2032 Label.left = Icon.right;
2033 Label.top = Box.top;
2034 Label.right = min(Label.left + labelSize.cx, Label.right);
2035 Label.bottom = Label.top + infoPtr->nItemHeight;
2038 if (lprcLabel) *lprcLabel = Label;
2039 TRACE(" - label=%s\n", debugrect(&Label));
2042 /* Fix the Box if necessary */
2045 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2046 else *lprcBox = Box;
2048 TRACE(" - box=%s\n", debugrect(&Box));
2052 * DESCRIPTION: [INTERNAL]
2055 * [I] infoPtr : valid pointer to the listview structure
2056 * [I] nItem : item number
2057 * [O] lprcBox : ptr to Box rectangle
2062 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2064 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2065 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2066 POINT Position, Origin;
2069 LISTVIEW_GetOrigin(infoPtr, &Origin);
2070 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2072 /* Be smart and try to figure out the minimum we have to do */
2074 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2075 lvItem.mask |= LVIF_TEXT;
2076 lvItem.iItem = nItem;
2077 lvItem.iSubItem = 0;
2078 lvItem.pszText = szDispText;
2079 lvItem.cchTextMax = DISP_TEXT_SIZE;
2080 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2081 if (uView == LVS_ICON)
2083 lvItem.mask |= LVIF_STATE;
2084 lvItem.stateMask = LVIS_FOCUSED;
2085 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2087 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2089 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2095 * Returns the current icon position, and advances it along the top.
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_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2107 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2109 *lpPos = infoPtr->currIconPos;
2111 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2112 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2114 infoPtr->currIconPos.x = 0;
2115 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2121 * Returns the current icon position, and advances it down the left edge.
2122 * The returned position is not offset by Origin.
2125 * [I] infoPtr : valid pointer to the listview structure
2126 * [O] lpPos : will get the current icon position
2131 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2133 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2135 *lpPos = infoPtr->currIconPos;
2137 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2138 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2140 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2141 infoPtr->currIconPos.y = 0;
2147 * Moves an icon to the specified position.
2148 * It takes care of invalidating the item, etc.
2151 * [I] infoPtr : valid pointer to the listview structure
2152 * [I] nItem : the item to move
2153 * [I] lpPos : the new icon position
2154 * [I] isNew : flags the item as being new
2160 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2166 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2167 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2169 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2170 LISTVIEW_InvalidateItem(infoPtr, nItem);
2173 /* Allocating a POINTER for every item is too resource intensive,
2174 * so we'll keep the (x,y) in different arrays */
2175 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2176 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2178 LISTVIEW_InvalidateItem(infoPtr, nItem);
2185 * Arranges listview items in icon display mode.
2188 * [I] infoPtr : valid pointer to the listview structure
2189 * [I] nAlignCode : alignment code
2195 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2197 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2198 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2202 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2204 TRACE("nAlignCode=%d\n", nAlignCode);
2206 if (nAlignCode == LVA_DEFAULT)
2208 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2209 else nAlignCode = LVA_ALIGNTOP;
2214 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2215 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2216 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2217 default: return FALSE;
2220 infoPtr->bAutoarrange = TRUE;
2221 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2222 for (i = 0; i < infoPtr->nItemCount; i++)
2224 next_pos(infoPtr, &pos);
2225 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2233 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2236 * [I] infoPtr : valid pointer to the listview structure
2237 * [O] lprcView : bounding rectangle
2243 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2247 SetRectEmpty(lprcView);
2249 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2253 for (i = 0; i < infoPtr->nItemCount; i++)
2255 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2256 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2257 lprcView->right = max(lprcView->right, x);
2258 lprcView->bottom = max(lprcView->bottom, y);
2260 if (infoPtr->nItemCount > 0)
2262 lprcView->right += infoPtr->nItemWidth;
2263 lprcView->bottom += infoPtr->nItemHeight;
2268 y = LISTVIEW_GetCountPerColumn(infoPtr);
2269 x = infoPtr->nItemCount / y;
2270 if (infoPtr->nItemCount % y) x++;
2271 lprcView->right = x * infoPtr->nItemWidth;
2272 lprcView->bottom = y * infoPtr->nItemHeight;
2276 lprcView->right = infoPtr->nItemWidth;
2277 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2284 * Retrieves the bounding rectangle of all the items.
2287 * [I] infoPtr : valid pointer to the listview structure
2288 * [O] lprcView : bounding rectangle
2294 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2298 TRACE("(lprcView=%p)\n", lprcView);
2300 if (!lprcView) return FALSE;
2302 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2303 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2304 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2306 TRACE("lprcView=%s\n", debugrect(lprcView));
2313 * Retrieves the subitem pointer associated with the subitem index.
2316 * [I] hdpaSubItems : DPA handle for a specific item
2317 * [I] nSubItem : index of subitem
2320 * SUCCESS : subitem pointer
2323 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2325 SUBITEM_INFO *lpSubItem;
2328 /* we should binary search here if need be */
2329 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2331 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2332 if (lpSubItem->iSubItem == nSubItem)
2342 * Caclulates the desired item width.
2345 * [I] infoPtr : valid pointer to the listview structure
2348 * The desired item width.
2350 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2352 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2355 TRACE("uView=%d\n", uView);
2357 if (uView == LVS_ICON)
2358 nItemWidth = infoPtr->iconSpacing.cx;
2359 else if (uView == LVS_REPORT)
2363 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2365 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2366 nItemWidth = rcHeader.right;
2369 else /* LVS_SMALLICON, or LVS_LIST */
2373 for (i = 0; i < infoPtr->nItemCount; i++)
2374 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2376 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2377 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2379 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2382 return max(nItemWidth, 1);
2387 * Caclulates the desired item height.
2390 * [I] infoPtr : valid pointer to the listview structure
2393 * The desired item height.
2395 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2397 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2400 TRACE("uView=%d\n", uView);
2402 if (uView == LVS_ICON)
2403 nItemHeight = infoPtr->iconSpacing.cy;
2406 nItemHeight = infoPtr->ntmHeight;
2407 if (infoPtr->himlState)
2408 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2409 if (infoPtr->himlSmall)
2410 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2411 if (infoPtr->himlState || infoPtr->himlSmall)
2412 nItemHeight += HEIGHT_PADDING;
2415 return max(nItemHeight, 1);
2420 * Updates the width, and height of an item.
2423 * [I] infoPtr : valid pointer to the listview structure
2428 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2430 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2431 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2437 * Retrieves and saves important text metrics info for the current
2441 * [I] infoPtr : valid pointer to the listview structure
2444 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2446 HDC hdc = GetDC(infoPtr->hwndSelf);
2447 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2448 HFONT hOldFont = SelectObject(hdc, hFont);
2451 if (GetTextMetricsW(hdc, &tm))
2453 infoPtr->ntmHeight = tm.tmHeight;
2454 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2456 SelectObject(hdc, hOldFont);
2457 ReleaseDC(infoPtr->hwndSelf, hdc);
2459 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2464 * A compare function for ranges
2467 * [I] range1 : pointer to range 1;
2468 * [I] range2 : pointer to range 2;
2472 * > 0 : if range 1 > range 2
2473 * < 0 : if range 2 > range 1
2474 * = 0 : if range intersects range 2
2476 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2480 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2482 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2487 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2493 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2495 #define ranges_check(ranges, desc) do { } while(0)
2498 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2503 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2505 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2506 ranges_dump(ranges);
2507 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2508 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2509 assert (prev->lower >= 0 && prev->lower < prev->upper);
2510 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2512 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2513 assert (prev->upper <= curr->lower);
2514 assert (curr->lower < curr->upper);
2517 TRACE("--- Done checking---\n");
2520 static RANGES ranges_create(int count)
2522 RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2523 if (!ranges) return NULL;
2524 ranges->hdpa = DPA_Create(count);
2525 if (ranges->hdpa) return ranges;
2530 static void ranges_clear(RANGES ranges)
2534 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2535 Free(DPA_GetPtr(ranges->hdpa, i));
2536 DPA_DeleteAllPtrs(ranges->hdpa);
2540 static void ranges_destroy(RANGES ranges)
2542 if (!ranges) return;
2543 ranges_clear(ranges);
2544 DPA_Destroy(ranges->hdpa);
2548 static RANGES ranges_clone(RANGES ranges)
2553 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2555 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2557 RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2558 if (!newrng) goto fail;
2559 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2560 DPA_SetPtr(clone->hdpa, i, newrng);
2565 TRACE ("clone failed\n");
2566 ranges_destroy(clone);
2570 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2574 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2575 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2580 static void ranges_dump(RANGES ranges)
2584 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2585 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2588 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2590 RANGE srchrng = { nItem, nItem + 1 };
2592 TRACE("(nItem=%d)\n", nItem);
2593 ranges_check(ranges, "before contain");
2594 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2597 static INT ranges_itemcount(RANGES ranges)
2601 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2603 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2604 count += sel->upper - sel->lower;
2610 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2612 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2615 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2616 if (index == -1) return TRUE;
2618 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2620 chkrng = DPA_GetPtr(ranges->hdpa, index);
2621 if (chkrng->lower >= nItem)
2622 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2623 if (chkrng->upper > nItem)
2624 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2629 static BOOL ranges_add(RANGES ranges, RANGE range)
2634 TRACE("(%s)\n", debugrange(&range));
2635 ranges_check(ranges, "before add");
2637 /* try find overlapping regions first */
2638 srchrgn.lower = range.lower - 1;
2639 srchrgn.upper = range.upper + 1;
2640 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2646 TRACE("Adding new range\n");
2648 /* create the brand new range to insert */
2649 newrgn = (RANGE *)Alloc(sizeof(RANGE));
2650 if(!newrgn) goto fail;
2653 /* figure out where to insert it */
2654 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2655 TRACE("index=%d\n", index);
2656 if (index == -1) index = 0;
2658 /* and get it over with */
2659 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2667 RANGE *chkrgn, *mrgrgn;
2668 INT fromindex, mergeindex;
2670 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2671 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2673 chkrgn->lower = min(range.lower, chkrgn->lower);
2674 chkrgn->upper = max(range.upper, chkrgn->upper);
2676 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2678 /* merge now common anges */
2680 srchrgn.lower = chkrgn->lower - 1;
2681 srchrgn.upper = chkrgn->upper + 1;
2685 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2686 if (mergeindex == -1) break;
2687 if (mergeindex == index)
2689 fromindex = index + 1;
2693 TRACE("Merge with index %i\n", mergeindex);
2695 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2696 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2697 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2699 DPA_DeletePtr(ranges->hdpa, mergeindex);
2700 if (mergeindex < index) index --;
2704 ranges_check(ranges, "after add");
2708 ranges_check(ranges, "failed add");
2712 static BOOL ranges_del(RANGES ranges, RANGE range)
2717 TRACE("(%s)\n", debugrange(&range));
2718 ranges_check(ranges, "before del");
2720 /* we don't use DPAS_SORTED here, since we need *
2721 * to find the first overlapping range */
2722 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2725 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2727 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2729 /* case 1: Same range */
2730 if ( (chkrgn->upper == range.upper) &&
2731 (chkrgn->lower == range.lower) )
2733 DPA_DeletePtr(ranges->hdpa, index);
2736 /* case 2: engulf */
2737 else if ( (chkrgn->upper <= range.upper) &&
2738 (chkrgn->lower >= range.lower) )
2740 DPA_DeletePtr(ranges->hdpa, index);
2742 /* case 3: overlap upper */
2743 else if ( (chkrgn->upper <= range.upper) &&
2744 (chkrgn->lower < range.lower) )
2746 chkrgn->upper = range.lower;
2748 /* case 4: overlap lower */
2749 else if ( (chkrgn->upper > range.upper) &&
2750 (chkrgn->lower >= range.lower) )
2752 chkrgn->lower = range.upper;
2755 /* case 5: fully internal */
2758 RANGE tmprgn = *chkrgn, *newrgn;
2760 if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2761 newrgn->lower = chkrgn->lower;
2762 newrgn->upper = range.lower;
2763 chkrgn->lower = range.upper;
2764 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2773 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2776 ranges_check(ranges, "after del");
2780 ranges_check(ranges, "failed del");
2786 * Removes all selection ranges
2789 * [I] infoPtr : valid pointer to the listview structure
2790 * [I] toSkip : item range to skip removing the selection
2796 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2805 lvItem.stateMask = LVIS_SELECTED;
2807 /* need to clone the DPA because callbacks can change it */
2808 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2809 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2810 while(iterator_next(&i))
2811 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2812 /* note that the iterator destructor will free the cloned range */
2813 iterator_destroy(&i);
2818 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2822 if (!(toSkip = ranges_create(1))) return FALSE;
2823 if (nItem != -1) ranges_additem(toSkip, nItem);
2824 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2825 ranges_destroy(toSkip);
2829 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2831 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2836 * Retrieves the number of items that are marked as selected.
2839 * [I] infoPtr : valid pointer to the listview structure
2842 * Number of items selected.
2844 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2846 INT nSelectedCount = 0;
2848 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2851 for (i = 0; i < infoPtr->nItemCount; i++)
2853 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2858 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2860 TRACE("nSelectedCount=%d\n", nSelectedCount);
2861 return nSelectedCount;
2866 * Manages the item focus.
2869 * [I] infoPtr : valid pointer to the listview structure
2870 * [I] nItem : item index
2873 * TRUE : focused item changed
2874 * FALSE : focused item has NOT changed
2876 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2878 INT oldFocus = infoPtr->nFocusedItem;
2881 if (nItem == infoPtr->nFocusedItem) return FALSE;
2883 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2884 lvItem.stateMask = LVIS_FOCUSED;
2885 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2887 return oldFocus != infoPtr->nFocusedItem;
2890 /* Helper function for LISTVIEW_ShiftIndices *only* */
2891 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2893 if (nShiftItem < nItem) return nShiftItem;
2895 if (nShiftItem > nItem) return nShiftItem + direction;
2897 if (direction > 0) return nShiftItem + direction;
2899 return min(nShiftItem, infoPtr->nItemCount - 1);
2904 * Updates the various indices after an item has been inserted or deleted.
2907 * [I] infoPtr : valid pointer to the listview structure
2908 * [I] nItem : item index
2909 * [I] direction : Direction of shift, +1 or -1.
2914 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2919 /* temporarily disable change notification while shifting items */
2920 bOldChange = infoPtr->bDoChangeNotify;
2921 infoPtr->bDoChangeNotify = FALSE;
2923 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2925 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2927 assert(abs(direction) == 1);
2929 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2931 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2932 if (nNewFocus != infoPtr->nFocusedItem)
2933 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2935 /* But we are not supposed to modify nHotItem! */
2937 infoPtr->bDoChangeNotify = bOldChange;
2943 * Adds a block of selections.
2946 * [I] infoPtr : valid pointer to the listview structure
2947 * [I] nItem : item index
2952 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2954 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2955 INT nLast = max(infoPtr->nSelectionMark, nItem);
2959 if (nFirst == -1) nFirst = nItem;
2961 item.state = LVIS_SELECTED;
2962 item.stateMask = LVIS_SELECTED;
2964 /* FIXME: this is not correct LVS_OWNERDATA
2965 * setting the item states individually will generate
2966 * a LVN_ITEMCHANGED notification for each one. Instead,
2967 * we have to send a LVN_ODSTATECHANGED notification.
2968 * See MSDN documentation for LVN_ITEMCHANGED.
2970 for (i = nFirst; i <= nLast; i++)
2971 LISTVIEW_SetItemState(infoPtr,i,&item);
2977 * Sets a single group selection.
2980 * [I] infoPtr : valid pointer to the listview structure
2981 * [I] nItem : item index
2986 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2988 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2993 if (!(selection = ranges_create(100))) return;
2995 item.state = LVIS_SELECTED;
2996 item.stateMask = LVIS_SELECTED;
2998 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3000 if (infoPtr->nSelectionMark == -1)
3002 infoPtr->nSelectionMark = nItem;
3003 ranges_additem(selection, nItem);
3009 sel.lower = min(infoPtr->nSelectionMark, nItem);
3010 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3011 ranges_add(selection, sel);
3016 RECT rcItem, rcSel, rcSelMark;
3019 rcItem.left = LVIR_BOUNDS;
3020 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3021 rcSelMark.left = LVIR_BOUNDS;
3022 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3023 UnionRect(&rcSel, &rcItem, &rcSelMark);
3024 iterator_frameditems(&i, infoPtr, &rcSel);
3025 while(iterator_next(&i))
3027 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3028 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3030 iterator_destroy(&i);
3033 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3034 iterator_rangesitems(&i, selection);
3035 while(iterator_next(&i))
3036 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3037 /* this will also destroy the selection */
3038 iterator_destroy(&i);
3040 LISTVIEW_SetItemFocus(infoPtr, nItem);
3045 * Sets a single selection.
3048 * [I] infoPtr : valid pointer to the listview structure
3049 * [I] nItem : item index
3054 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3058 TRACE("nItem=%d\n", nItem);
3060 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3062 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3063 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3064 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3066 infoPtr->nSelectionMark = nItem;
3071 * Set selection(s) with keyboard.
3074 * [I] infoPtr : valid pointer to the listview structure
3075 * [I] nItem : item index
3078 * SUCCESS : TRUE (needs to be repainted)
3079 * FAILURE : FALSE (nothing has changed)
3081 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3083 /* FIXME: pass in the state */
3084 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3085 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3086 BOOL bResult = FALSE;
3088 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3090 if (infoPtr->dwStyle & LVS_SINGLESEL)
3093 LISTVIEW_SetSelection(infoPtr, nItem);
3100 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3104 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3109 LISTVIEW_SetSelection(infoPtr, nItem);
3112 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3115 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3122 * Called when the mouse is being actively tracked and has hovered for a specified
3126 * [I] infoPtr : valid pointer to the listview structure
3127 * [I] fwKeys : key indicator
3128 * [I] pts : mouse position
3131 * 0 if the message was processed, non-zero if there was an error
3134 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3135 * over the item for a certain period of time.
3138 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
3140 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3141 /* FIXME: select the item!!! */
3142 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3149 * Called whenever WM_MOUSEMOVE is received.
3152 * [I] infoPtr : valid pointer to the listview structure
3153 * [I] fwKeys : key indicator
3154 * [I] pts : mouse position
3157 * 0 if the message is processed, non-zero if there was an error
3159 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
3161 TRACKMOUSEEVENT trackinfo;
3163 /* see if we are supposed to be tracking mouse hovering */
3164 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3165 /* fill in the trackinfo struct */
3166 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3167 trackinfo.dwFlags = TME_QUERY;
3168 trackinfo.hwndTrack = infoPtr->hwndSelf;
3169 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3171 /* see if we are already tracking this hwnd */
3172 _TrackMouseEvent(&trackinfo);
3174 if(!(trackinfo.dwFlags & TME_HOVER)) {
3175 trackinfo.dwFlags = TME_HOVER;
3177 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3178 _TrackMouseEvent(&trackinfo);
3187 * Tests wheather the item is assignable to a list with style lStyle
3189 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3191 if ( (lpLVItem->mask & LVIF_TEXT) &&
3192 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3193 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3201 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3204 * [I] infoPtr : valid pointer to the listview structure
3205 * [I] lpLVItem : valid pointer to new item atttributes
3206 * [I] isNew : the item being set is being inserted
3207 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3208 * [O] bChanged : will be set to TRUE if the item really changed
3214 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3216 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3224 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3226 if (lpLVItem->mask == 0) return TRUE;
3228 if (infoPtr->dwStyle & LVS_OWNERDATA)
3230 /* a virtual listview we stores only selection and focus */
3231 if (lpLVItem->mask & ~LVIF_STATE)
3237 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3238 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3242 /* we need to get the lParam and state of the item */
3243 item.iItem = lpLVItem->iItem;
3244 item.iSubItem = lpLVItem->iSubItem;
3245 item.mask = LVIF_STATE | LVIF_PARAM;
3246 item.stateMask = ~0;
3249 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3251 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3252 /* determine what fields will change */
3253 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3254 uChanged |= LVIF_STATE;
3256 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3257 uChanged |= LVIF_IMAGE;
3259 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3260 uChanged |= LVIF_PARAM;
3262 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3263 uChanged |= LVIF_INDENT;
3265 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3266 uChanged |= LVIF_TEXT;
3268 TRACE("uChanged=0x%x\n", uChanged);
3269 if (!uChanged) return TRUE;
3272 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3273 nmlv.iItem = lpLVItem->iItem;
3274 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3275 nmlv.uOldState = item.state;
3276 nmlv.uChanged = uChanged;
3277 nmlv.lParam = item.lParam;
3279 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3280 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3282 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3283 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3286 /* copy information */
3287 if (lpLVItem->mask & LVIF_TEXT)
3288 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3290 if (lpLVItem->mask & LVIF_IMAGE)
3291 lpItem->hdr.iImage = lpLVItem->iImage;
3293 if (lpLVItem->mask & LVIF_PARAM)
3294 lpItem->lParam = lpLVItem->lParam;
3296 if (lpLVItem->mask & LVIF_INDENT)
3297 lpItem->iIndent = lpLVItem->iIndent;
3299 if (uChanged & LVIF_STATE)
3301 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3303 lpItem->state &= ~lpLVItem->stateMask;
3304 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3306 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3308 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3309 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3311 else if (lpLVItem->stateMask & LVIS_SELECTED)
3312 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3314 /* if we are asked to change focus, and we manage it, do it */
3315 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3317 if (lpLVItem->state & LVIS_FOCUSED)
3319 LISTVIEW_SetItemFocus(infoPtr, -1);
3320 infoPtr->nFocusedItem = lpLVItem->iItem;
3321 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3323 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3324 infoPtr->nFocusedItem = -1;
3328 /* if we're inserting the item, we're done */
3329 if (isNew) return TRUE;
3331 /* send LVN_ITEMCHANGED notification */
3332 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3333 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3340 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3343 * [I] infoPtr : valid pointer to the listview structure
3344 * [I] lpLVItem : valid pointer to new subitem atttributes
3345 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3346 * [O] bChanged : will be set to TRUE if the item really changed
3352 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3355 SUBITEM_INFO *lpSubItem;
3357 /* we do not support subitems for virtual listviews */
3358 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3360 /* set subitem only if column is present */
3361 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3363 /* First do some sanity checks */
3364 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3365 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3367 /* get the subitem structure, and create it if not there */
3368 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3369 assert (hdpaSubItems);
3371 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3374 SUBITEM_INFO *tmpSubItem;
3377 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3378 if (!lpSubItem) return FALSE;
3379 /* we could binary search here, if need be...*/
3380 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3382 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3383 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3385 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3390 lpSubItem->iSubItem = lpLVItem->iSubItem;
3394 if (lpLVItem->mask & LVIF_IMAGE)
3395 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3397 lpSubItem->hdr.iImage = lpLVItem->iImage;
3401 if (lpLVItem->mask & LVIF_TEXT)
3402 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3404 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3413 * Sets item attributes.
3416 * [I] infoPtr : valid pointer to the listview structure
3417 * [I] lpLVItem : new item atttributes
3418 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3424 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3426 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3427 LPWSTR pszText = NULL;
3428 BOOL bResult, bChanged = FALSE;
3430 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3432 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3435 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3436 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3438 pszText = lpLVItem->pszText;
3439 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3442 /* actually set the fields */
3443 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3445 if (lpLVItem->iSubItem)
3446 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3448 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3450 /* redraw item, if necessary */
3451 if (bChanged && !infoPtr->bIsDrawing)
3453 /* this little optimization eliminates some nasty flicker */
3454 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3455 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3456 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3458 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3463 textfreeT(lpLVItem->pszText, isW);
3464 ((LVITEMW *)lpLVItem)->pszText = pszText;
3472 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3475 * [I] infoPtr : valid pointer to the listview structure
3480 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3482 LONG lStyle = infoPtr->dwStyle;
3483 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3485 SCROLLINFO scrollInfo;
3487 scrollInfo.cbSize = sizeof(SCROLLINFO);
3488 scrollInfo.fMask = SIF_POS;
3490 if (uView == LVS_LIST)
3492 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3493 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3495 else if (uView == LVS_REPORT)
3497 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3498 nItem = scrollInfo.nPos;
3502 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3503 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3506 TRACE("nItem=%d\n", nItem);
3514 * Erases the background of the given rectangle
3517 * [I] infoPtr : valid pointer to the listview structure
3518 * [I] hdc : device context handle
3519 * [I] lprcBox : clipping rectangle
3525 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3527 if (!infoPtr->hBkBrush) return FALSE;
3529 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3531 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3539 * [I] infoPtr : valid pointer to the listview structure
3540 * [I] hdc : device context handle
3541 * [I] nItem : item index
3542 * [I] nSubItem : subitem index
3543 * [I] pos : item position in client coordinates
3544 * [I] cdmode : custom draw mode
3550 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3552 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3553 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3554 WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3555 DWORD cdsubitemmode = CDRF_DODEFAULT;
3556 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3557 NMLVCUSTOMDRAW nmlvcd;
3561 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3563 /* get information needed for drawing the item */
3564 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3565 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3566 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3567 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3568 lvItem.iItem = nItem;
3569 lvItem.iSubItem = nSubItem;
3572 lvItem.cchTextMax = DISP_TEXT_SIZE;
3573 lvItem.pszText = szDispText;
3574 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3575 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3576 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3577 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3578 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3580 /* now check if we need to update the focus rectangle */
3581 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3583 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3584 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3585 OffsetRect(&rcBox, pos.x, pos.y);
3586 OffsetRect(&rcState, pos.x, pos.y);
3587 OffsetRect(&rcIcon, pos.x, pos.y);
3588 OffsetRect(&rcLabel, pos.x, pos.y);
3589 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3590 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3592 /* fill in the custom draw structure */
3593 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3595 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3596 if (cdmode & CDRF_NOTIFYITEMDRAW)
3597 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3598 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3599 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3600 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3601 prepaint_setup(infoPtr, hdc, &nmlvcd);
3603 /* in full row select, subitems, will just use main item's colors */
3604 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3605 nmlvcd.clrTextBk = CLR_NONE;
3608 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3610 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3613 TRACE("uStateImage=%d\n", uStateImage);
3614 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3619 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3620 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3622 TRACE("iImage=%d\n", lvItem.iImage);
3623 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3624 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3627 /* Don't bother painting item being edited */
3628 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3630 /* draw the selection background, if we're drawing the main item */
3634 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3635 rcSelect.right = rcBox.right;
3637 if (nmlvcd.clrTextBk != CLR_NONE)
3638 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3639 if(lprcFocus) *lprcFocus = rcSelect;
3642 /* figure out the text drawing flags */
3643 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3644 if (uView == LVS_ICON)
3645 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3648 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3650 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3651 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3652 default: uFormat |= DT_LEFT;
3655 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3657 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3658 else rcLabel.left += LABEL_HOR_PADDING;
3660 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3661 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3664 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3665 notify_postpaint(infoPtr, &nmlvcd);
3671 * Draws listview items when in owner draw mode.
3674 * [I] infoPtr : valid pointer to the listview structure
3675 * [I] hdc : device context handle
3680 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3682 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3683 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3684 DWORD cditemmode = CDRF_DODEFAULT;
3685 NMLVCUSTOMDRAW nmlvcd;
3686 POINT Origin, Position;
3692 ZeroMemory(&dis, sizeof(dis));
3694 /* Get scroll info once before loop */
3695 LISTVIEW_GetOrigin(infoPtr, &Origin);
3697 /* iterate through the invalidated rows */
3698 while(iterator_next(i))
3700 item.iItem = i->nItem;
3702 item.mask = LVIF_PARAM | LVIF_STATE;
3703 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3704 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3706 dis.CtlType = ODT_LISTVIEW;
3708 dis.itemID = item.iItem;
3709 dis.itemAction = ODA_DRAWENTIRE;
3711 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3712 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3713 dis.hwndItem = infoPtr->hwndSelf;
3715 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3716 dis.rcItem.left = Position.x + Origin.x;
3717 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3718 dis.rcItem.top = Position.y + Origin.y;
3719 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3720 dis.itemData = item.lParam;
3722 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3724 if (cdmode & CDRF_NOTIFYITEMDRAW)
3726 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3727 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3730 if (!(cditemmode & CDRF_SKIPDEFAULT))
3732 prepaint_setup (infoPtr, hdc, &nmlvcd);
3733 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3736 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3737 notify_postpaint(infoPtr, &nmlvcd);
3743 * Draws listview items when in report display mode.
3746 * [I] infoPtr : valid pointer to the listview structure
3747 * [I] hdc : device context handle
3748 * [I] cdmode : custom draw mode
3753 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3756 RECT rcClip, rcItem;
3757 POINT Origin, Position;
3763 /* figure out what to draw */
3764 rgntype = GetClipBox(hdc, &rcClip);
3765 if (rgntype == NULLREGION) return;
3767 /* Get scroll info once before loop */
3768 LISTVIEW_GetOrigin(infoPtr, &Origin);
3770 /* narrow down the columns we need to paint */
3771 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3773 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3774 if (rcItem.right + Origin.x >= rcClip.left) break;
3776 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3778 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3779 if (rcItem.left + Origin.x < rcClip.right) break;
3781 iterator_rangeitems(&j, colRange);
3783 /* in full row select, we _have_ to draw the main item */
3784 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3787 /* iterate through the invalidated rows */
3788 while(iterator_next(i))
3790 /* iterate through the invalidated columns */
3791 while(iterator_next(&j))
3793 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3794 Position.x += Origin.x;
3795 Position.y += Origin.y;
3797 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3799 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3801 rcItem.bottom = infoPtr->nItemHeight;
3802 OffsetRect(&rcItem, Position.x, Position.y);
3803 if (!RectVisible(hdc, &rcItem)) continue;
3806 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3809 iterator_destroy(&j);
3814 * Draws listview items when in list display mode.
3817 * [I] infoPtr : valid pointer to the listview structure
3818 * [I] hdc : device context handle
3819 * [I] cdmode : custom draw mode
3824 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3826 POINT Origin, Position;
3828 /* Get scroll info once before loop */
3829 LISTVIEW_GetOrigin(infoPtr, &Origin);
3831 while(iterator_prev(i))
3833 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3834 Position.x += Origin.x;
3835 Position.y += Origin.y;
3837 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3844 * Draws listview items.
3847 * [I] infoPtr : valid pointer to the listview structure
3848 * [I] hdc : device context handle
3853 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3855 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3856 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3857 NMLVCUSTOMDRAW nmlvcd;
3864 LISTVIEW_DUMP(infoPtr);
3866 infoPtr->bIsDrawing = TRUE;
3868 /* save dc values we're gonna trash while drawing */
3869 hOldFont = SelectObject(hdc, infoPtr->hFont);
3870 oldBkMode = GetBkMode(hdc);
3871 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3872 oldTextColor = GetTextColor(hdc);
3874 oldClrTextBk = infoPtr->clrTextBk;
3875 oldClrText = infoPtr->clrText;
3877 infoPtr->cditemmode = CDRF_DODEFAULT;
3879 GetClientRect(infoPtr->hwndSelf, &rcClient);
3880 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3881 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3882 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3883 prepaint_setup(infoPtr, hdc, &nmlvcd);
3885 /* Use these colors to draw the items */
3886 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3887 infoPtr->clrText = nmlvcd.clrText;
3889 /* nothing to draw */
3890 if(infoPtr->nItemCount == 0) goto enddraw;
3892 /* figure out what we need to draw */
3893 iterator_visibleitems(&i, infoPtr, hdc);
3895 /* send cache hint notification */
3896 if (infoPtr->dwStyle & LVS_OWNERDATA)
3898 RANGE range = iterator_range(&i);
3901 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3902 nmlv.iFrom = range.lower;
3903 nmlv.iTo = range.upper - 1;
3904 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3907 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3908 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3911 if (uView == LVS_REPORT)
3912 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3913 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3914 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3916 /* if we have a focus rect, draw it */
3917 if (infoPtr->bFocus)
3918 DrawFocusRect(hdc, &infoPtr->rcFocus);
3920 iterator_destroy(&i);
3923 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3924 notify_postpaint(infoPtr, &nmlvcd);
3926 infoPtr->clrTextBk = oldClrTextBk;
3927 infoPtr->clrText = oldClrText;
3929 SelectObject(hdc, hOldFont);
3930 SetBkMode(hdc, oldBkMode);
3931 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3932 SetTextColor(hdc, oldTextColor);
3933 infoPtr->bIsDrawing = FALSE;
3939 * Calculates the approximate width and height of a given number of items.
3942 * [I] infoPtr : valid pointer to the listview structure
3943 * [I] nItemCount : number of items
3944 * [I] wWidth : width
3945 * [I] wHeight : height
3948 * Returns a DWORD. The width in the low word and the height in high word.
3950 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3951 WORD wWidth, WORD wHeight)
3953 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3954 INT nItemCountPerColumn = 1;
3955 INT nColumnCount = 0;
3956 DWORD dwViewRect = 0;
3958 if (nItemCount == -1)
3959 nItemCount = infoPtr->nItemCount;
3961 if (uView == LVS_LIST)
3963 if (wHeight == 0xFFFF)
3965 /* use current height */
3966 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3969 if (wHeight < infoPtr->nItemHeight)
3970 wHeight = infoPtr->nItemHeight;
3974 if (infoPtr->nItemHeight > 0)
3976 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3977 if (nItemCountPerColumn == 0)
3978 nItemCountPerColumn = 1;
3980 if (nItemCount % nItemCountPerColumn != 0)
3981 nColumnCount = nItemCount / nItemCountPerColumn;
3983 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3987 /* Microsoft padding magic */
3988 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3989 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3991 dwViewRect = MAKELONG(wWidth, wHeight);
3993 else if (uView == LVS_REPORT)
3994 FIXME("uView == LVS_REPORT: not implemented\n");
3995 else if (uView == LVS_SMALLICON)
3996 FIXME("uView == LVS_SMALLICON: not implemented\n");
3997 else if (uView == LVS_ICON)
3998 FIXME("uView == LVS_ICON: not implemented\n");
4003 /* << LISTVIEW_CreateDragImage >> */
4008 * Removes all listview items and subitems.
4011 * [I] infoPtr : valid pointer to the listview structure
4017 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4020 HDPA hdpaSubItems = NULL;
4027 /* we do it directly, to avoid notifications */
4028 ranges_clear(infoPtr->selectionRanges);
4029 infoPtr->nSelectionMark = -1;
4030 infoPtr->nFocusedItem = -1;
4031 SetRectEmpty(&infoPtr->rcFocus);
4032 /* But we are supposed to leave nHotItem as is! */
4035 /* send LVN_DELETEALLITEMS notification */
4036 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4038 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4040 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4042 /* send LVN_DELETEITEM notification, if not supressed */
4043 if (!bSuppress) notify_deleteitem(infoPtr, i);
4044 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4046 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4047 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4049 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4050 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4053 DPA_Destroy(hdpaSubItems);
4054 DPA_DeletePtr(infoPtr->hdpaItems, i);
4056 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4057 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4058 infoPtr->nItemCount --;
4061 LISTVIEW_UpdateScroll(infoPtr);
4063 LISTVIEW_InvalidateList(infoPtr);
4070 * Scrolls, and updates the columns, when a column is changing width.
4073 * [I] infoPtr : valid pointer to the listview structure
4074 * [I] nColumn : column to scroll
4075 * [I] dx : amount of scroll, in pixels
4080 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4082 COLUMN_INFO *lpColumnInfo;
4086 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4087 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4088 rcCol = lpColumnInfo->rcHeader;
4089 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4090 rcCol.left = rcCol.right;
4092 /* ajust the other columns */
4093 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4095 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4096 lpColumnInfo->rcHeader.left += dx;
4097 lpColumnInfo->rcHeader.right += dx;
4100 /* do not update screen if not in report mode */
4101 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4103 /* if we have a focus, must first erase the focus rect */
4104 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4106 /* Need to reset the item width when inserting a new column */
4107 infoPtr->nItemWidth += dx;
4109 LISTVIEW_UpdateScroll(infoPtr);
4111 /* scroll to cover the deleted column, and invalidate for redraw */
4112 rcOld = infoPtr->rcList;
4113 rcOld.left = rcCol.left;
4114 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4116 /* we can restore focus now */
4117 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4122 * Removes a column from the listview control.
4125 * [I] infoPtr : valid pointer to the listview structure
4126 * [I] nColumn : column index
4132 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4136 TRACE("nColumn=%d\n", nColumn);
4138 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4139 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4141 /* While the MSDN specifically says that column zero should not be deleted,
4142 it does in fact work on WinNT, and at least one app depends on it. On
4143 WinNT, deleting column zero deletes the last column of items but the
4144 first header. Since no app will ever depend on that bizarre behavior,
4145 we just delete the last column including the header.
4148 nColumn = DPA_GetPtrCount(infoPtr->hdpaColumns) - 1;
4150 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4152 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4155 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4156 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4158 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4160 SUBITEM_INFO *lpSubItem, *lpDelItem;
4162 INT nItem, nSubItem, i;
4165 return LISTVIEW_DeleteAllItems(infoPtr);
4167 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4169 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4172 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4174 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4175 if (lpSubItem->iSubItem == nColumn)
4178 lpDelItem = lpSubItem;
4180 else if (lpSubItem->iSubItem > nColumn)
4182 lpSubItem->iSubItem--;
4186 /* if we found our subitem, zapp it */
4190 if (is_textW(lpDelItem->hdr.pszText))
4191 Free(lpDelItem->hdr.pszText);
4196 /* free dpa memory */
4197 DPA_DeletePtr(hdpaSubItems, nSubItem);
4202 /* update the other column info */
4203 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4210 * Invalidates the listview after an item's insertion or deletion.
4213 * [I] infoPtr : valid pointer to the listview structure
4214 * [I] nItem : item index
4215 * [I] dir : -1 if deleting, 1 if inserting
4220 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4222 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4223 INT nPerCol, nItemCol, nItemRow;
4227 /* if we don't refresh, what's the point of scrolling? */
4228 if (!is_redrawing(infoPtr)) return;
4230 assert (abs(dir) == 1);
4232 /* arrange icons if autoarrange is on */
4233 if (is_autoarrange(infoPtr))
4235 BOOL arrange = TRUE;
4236 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4237 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4238 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4241 /* scrollbars need updating */
4242 LISTVIEW_UpdateScroll(infoPtr);
4244 /* figure out the item's position */
4245 if (uView == LVS_REPORT)
4246 nPerCol = infoPtr->nItemCount + 1;
4247 else if (uView == LVS_LIST)
4248 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4249 else /* LVS_ICON, or LVS_SMALLICON */
4252 nItemCol = nItem / nPerCol;
4253 nItemRow = nItem % nPerCol;
4254 LISTVIEW_GetOrigin(infoPtr, &Origin);
4256 /* move the items below up a slot */
4257 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4258 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4259 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4260 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4261 OffsetRect(&rcScroll, Origin.x, Origin.y);
4262 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4263 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4265 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4266 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4267 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4270 /* report has only that column, so we're done */
4271 if (uView == LVS_REPORT) return;
4273 /* now for LISTs, we have to deal with the columns to the right */
4274 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4276 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4277 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4278 OffsetRect(&rcScroll, Origin.x, Origin.y);
4279 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4280 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4281 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4286 * Removes an item from the listview control.
4289 * [I] infoPtr : valid pointer to the listview structure
4290 * [I] nItem : item index
4296 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4298 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4301 TRACE("(nItem=%d)\n", nItem);
4303 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4305 /* remove selection, and focus */
4307 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4308 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4310 /* send LVN_DELETEITEM notification. */
4311 notify_deleteitem(infoPtr, nItem);
4313 /* we need to do this here, because we'll be deleting stuff */
4314 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4315 LISTVIEW_InvalidateItem(infoPtr, nItem);
4317 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4323 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4324 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4326 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4327 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4330 DPA_Destroy(hdpaSubItems);
4333 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4335 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4336 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4339 infoPtr->nItemCount--;
4340 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4342 /* now is the invalidation fun */
4343 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4350 * Callback implementation for editlabel control
4353 * [I] infoPtr : valid pointer to the listview structure
4354 * [I] pszText : modified text
4355 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4361 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4363 NMLVDISPINFOW dispInfo;
4365 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4367 ZeroMemory(&dispInfo, sizeof(dispInfo));
4368 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4369 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4370 dispInfo.item.iSubItem = 0;
4371 dispInfo.item.stateMask = ~0;
4372 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4373 /* add the text from the edit in */
4374 dispInfo.item.mask |= LVIF_TEXT;
4375 dispInfo.item.pszText = pszText;
4376 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4378 /* Do we need to update the Item Text */
4379 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4380 if (!pszText) return TRUE;
4382 ZeroMemory(&dispInfo, sizeof(dispInfo));
4383 dispInfo.item.mask = LVIF_TEXT;
4384 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4385 dispInfo.item.iSubItem = 0;
4386 dispInfo.item.pszText = pszText;
4387 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4388 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4393 * Begin in place editing of specified list view item
4396 * [I] infoPtr : valid pointer to the listview structure
4397 * [I] nItem : item index
4398 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4404 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4406 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4407 NMLVDISPINFOW dispInfo;
4410 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4412 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4413 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4415 infoPtr->nEditLabelItem = nItem;
4417 /* Is the EditBox still there, if so remove it */
4418 if(infoPtr->hwndEdit != 0)
4420 SetFocus(infoPtr->hwndSelf);
4421 infoPtr->hwndEdit = 0;
4424 LISTVIEW_SetSelection(infoPtr, nItem);
4425 LISTVIEW_SetItemFocus(infoPtr, nItem);
4426 LISTVIEW_InvalidateItem(infoPtr, nItem);
4428 rect.left = LVIR_LABEL;
4429 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4431 ZeroMemory(&dispInfo, sizeof(dispInfo));
4432 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4433 dispInfo.item.iItem = nItem;
4434 dispInfo.item.iSubItem = 0;
4435 dispInfo.item.stateMask = ~0;
4436 dispInfo.item.pszText = szDispText;
4437 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4438 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4440 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4441 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4442 if (!infoPtr->hwndEdit) return 0;
4444 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4446 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4447 infoPtr->hwndEdit = 0;
4451 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4452 SetFocus(infoPtr->hwndEdit);
4453 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4454 return infoPtr->hwndEdit;
4460 * Ensures the specified item is visible, scrolling into view if necessary.
4463 * [I] infoPtr : valid pointer to the listview structure
4464 * [I] nItem : item index
4465 * [I] bPartial : partially or entirely visible
4471 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4473 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4474 INT nScrollPosHeight = 0;
4475 INT nScrollPosWidth = 0;
4476 INT nHorzAdjust = 0;
4477 INT nVertAdjust = 0;
4480 RECT rcItem, rcTemp;
4482 rcItem.left = LVIR_BOUNDS;
4483 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4485 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4487 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4489 /* scroll left/right, but in LVS_REPORT mode */
4490 if (uView == LVS_LIST)
4491 nScrollPosWidth = infoPtr->nItemWidth;
4492 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4493 nScrollPosWidth = 1;
4495 if (rcItem.left < infoPtr->rcList.left)
4498 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4503 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4507 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4509 /* scroll up/down, but not in LVS_LIST mode */
4510 if (uView == LVS_REPORT)
4511 nScrollPosHeight = infoPtr->nItemHeight;
4512 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4513 nScrollPosHeight = 1;
4515 if (rcItem.top < infoPtr->rcList.top)
4518 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4523 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4527 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4529 if (nScrollPosWidth)
4531 INT diff = nHorzDiff / nScrollPosWidth;
4532 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4533 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4536 if (nScrollPosHeight)
4538 INT diff = nVertDiff / nScrollPosHeight;
4539 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4540 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4548 * Searches for an item with specific characteristics.
4551 * [I] hwnd : window handle
4552 * [I] nStart : base item index
4553 * [I] lpFindInfo : item information to look for
4556 * SUCCESS : index of item
4559 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4560 const LVFINDINFOW *lpFindInfo)
4562 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4563 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4564 BOOL bWrap = FALSE, bNearest = FALSE;
4565 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4566 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4567 POINT Position, Destination;
4570 if (!lpFindInfo || nItem < 0) return -1;
4573 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4575 lvItem.mask |= LVIF_TEXT;
4576 lvItem.pszText = szDispText;
4577 lvItem.cchTextMax = DISP_TEXT_SIZE;
4580 if (lpFindInfo->flags & LVFI_WRAP)
4583 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4584 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4589 LISTVIEW_GetOrigin(infoPtr, &Origin);
4590 Destination.x = lpFindInfo->pt.x - Origin.x;
4591 Destination.y = lpFindInfo->pt.y - Origin.y;
4592 switch(lpFindInfo->vkDirection)
4594 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4595 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4596 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4597 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4598 case VK_HOME: Destination.x = Destination.y = 0; break;
4599 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4600 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4602 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4603 Destination.x = rcArea.right;
4604 Destination.y = rcArea.bottom;
4606 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4611 /* if LVFI_PARAM is specified, all other flags are ignored */
4612 if (lpFindInfo->flags & LVFI_PARAM)
4614 lvItem.mask |= LVIF_PARAM;
4616 lvItem.mask &= ~LVIF_TEXT;
4620 for (; nItem < nLast; nItem++)
4622 lvItem.iItem = nItem;
4623 lvItem.iSubItem = 0;
4624 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4626 if (lvItem.mask & LVIF_PARAM)
4628 if (lpFindInfo->lParam == lvItem.lParam)
4634 if (lvItem.mask & LVIF_TEXT)
4636 if (lpFindInfo->flags & LVFI_PARTIAL)
4638 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4642 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4646 if (!bNearest) return nItem;
4648 /* This is very inefficient. To do a good job here,
4649 * we need a sorted array of (x,y) item positions */
4650 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4652 /* compute the distance^2 to the destination */
4653 xdist = Destination.x - Position.x;
4654 ydist = Destination.y - Position.y;
4655 dist = xdist * xdist + ydist * ydist;
4657 /* remember the distance, and item if it's closer */
4661 nNearestItem = nItem;
4668 nLast = min(nStart + 1, infoPtr->nItemCount);
4673 return nNearestItem;
4678 * Searches for an item with specific characteristics.
4681 * [I] hwnd : window handle
4682 * [I] nStart : base item index
4683 * [I] lpFindInfo : item information to look for
4686 * SUCCESS : index of item
4689 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4690 const LVFINDINFOA *lpFindInfo)
4692 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4696 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4697 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4698 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4699 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4705 * Retrieves the background image of the listview control.
4708 * [I] infoPtr : valid pointer to the listview structure
4709 * [O] lpBkImage : background image attributes
4715 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4717 /* FIXME (listview, "empty stub!\n"); */
4723 * Retrieves column attributes.
4726 * [I] infoPtr : valid pointer to the listview structure
4727 * [I] nColumn : column index
4728 * [IO] lpColumn : column information
4729 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4730 * otherwise it is in fact a LPLVCOLUMNA
4736 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4738 COLUMN_INFO *lpColumnInfo;
4741 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4742 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4744 /* initialize memory */
4745 ZeroMemory(&hdi, sizeof(hdi));
4747 if (lpColumn->mask & LVCF_TEXT)
4749 hdi.mask |= HDI_TEXT;
4750 hdi.pszText = lpColumn->pszText;
4751 hdi.cchTextMax = lpColumn->cchTextMax;
4754 if (lpColumn->mask & LVCF_IMAGE)
4755 hdi.mask |= HDI_IMAGE;
4757 if (lpColumn->mask & LVCF_ORDER)
4758 hdi.mask |= HDI_ORDER;
4760 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4762 if (lpColumn->mask & LVCF_FMT)
4763 lpColumn->fmt = lpColumnInfo->fmt;
4765 if (lpColumn->mask & LVCF_WIDTH)
4766 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4768 if (lpColumn->mask & LVCF_IMAGE)
4769 lpColumn->iImage = hdi.iImage;
4771 if (lpColumn->mask & LVCF_ORDER)
4772 lpColumn->iOrder = hdi.iOrder;
4778 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4785 /* FIXME: little hack */
4786 for (i = 0; i < iCount; i++)
4794 * Retrieves the column width.
4797 * [I] infoPtr : valid pointer to the listview structure
4798 * [I] int : column index
4801 * SUCCESS : column width
4804 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4806 INT nColumnWidth = 0;
4809 TRACE("nColumn=%d\n", nColumn);
4811 /* we have a 'column' in LIST and REPORT mode only */
4812 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4815 nColumnWidth = infoPtr->nItemWidth;
4818 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
4819 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4820 nColumnWidth = rcHeader.right - rcHeader.left;
4824 TRACE("nColumnWidth=%d\n", nColumnWidth);
4825 return nColumnWidth;
4830 * In list or report display mode, retrieves the number of items that can fit
4831 * vertically in the visible area. In icon or small icon display mode,
4832 * retrieves the total number of visible items.
4835 * [I] infoPtr : valid pointer to the listview structure
4838 * Number of fully visible items.
4840 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4842 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4846 return infoPtr->nItemCount;
4848 return LISTVIEW_GetCountPerColumn(infoPtr);
4850 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4858 * Retrieves an image list handle.
4861 * [I] infoPtr : valid pointer to the listview structure
4862 * [I] nImageList : image list identifier
4865 * SUCCESS : image list handle
4868 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4872 case LVSIL_NORMAL: return infoPtr->himlNormal;
4873 case LVSIL_SMALL: return infoPtr->himlSmall;
4874 case LVSIL_STATE: return infoPtr->himlState;
4879 /* LISTVIEW_GetISearchString */
4883 * Retrieves item attributes.
4886 * [I] hwnd : window handle
4887 * [IO] lpLVItem : item info
4888 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4889 * if FALSE, the lpLVItem is a LPLVITEMA.
4892 * This is the internal 'GetItem' interface -- it tries to
4893 * be smart, and avoids text copies, if possible, by modifing
4894 * lpLVItem->pszText to point to the text string. Please note
4895 * that this is not always possible (e.g. OWNERDATA), so on
4896 * entry you *must* supply valid values for pszText, and cchTextMax.
4897 * The only difference to the documented interface is that upon
4898 * return, you should use *only* the lpLVItem->pszText, rather than
4899 * the buffer pointer you provided on input. Most code already does
4900 * that, so it's not a problem.
4901 * For the two cases when the text must be copied (that is,
4902 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4908 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4910 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
4911 NMLVDISPINFOW dispInfo;
4916 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4918 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4921 if (lpLVItem->mask == 0) return TRUE;
4923 /* a quick optimization if all we're asked is the focus state
4924 * these queries are worth optimising since they are common,
4925 * and can be answered in constant time, without the heavy accesses */
4926 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
4927 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4929 lpLVItem->state = 0;
4930 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4931 lpLVItem->state |= LVIS_FOCUSED;
4935 ZeroMemory(&dispInfo, sizeof(dispInfo));
4937 /* if the app stores all the data, handle it separately */
4938 if (infoPtr->dwStyle & LVS_OWNERDATA)
4940 dispInfo.item.state = 0;
4942 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
4943 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
4945 /* NOTE: copy only fields which we _know_ are initialized, some apps
4946 * depend on the uninitialized fields being 0 */
4947 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
4948 dispInfo.item.iItem = lpLVItem->iItem;
4949 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4950 if (lpLVItem->mask & LVIF_TEXT)
4952 dispInfo.item.pszText = lpLVItem->pszText;
4953 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4955 if (lpLVItem->mask & LVIF_STATE)
4956 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4957 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4958 dispInfo.item.stateMask = lpLVItem->stateMask;
4959 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
4961 /* full size structure expected - _WIN32IE >= 0x560 */
4962 *lpLVItem = dispInfo.item;
4964 else if (lpLVItem->mask & LVIF_INDENT)
4966 /* indent member expected - _WIN32IE >= 0x300 */
4967 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
4971 /* minimal structure expected */
4972 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
4974 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4977 /* make sure lParam is zeroed out */
4978 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
4980 /* we store only a little state, so if we're not asked, we're done */
4981 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4983 /* if focus is handled by us, report it */
4984 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4986 lpLVItem->state &= ~LVIS_FOCUSED;
4987 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4988 lpLVItem->state |= LVIS_FOCUSED;
4991 /* and do the same for selection, if we handle it */
4992 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4994 lpLVItem->state &= ~LVIS_SELECTED;
4995 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4996 lpLVItem->state |= LVIS_SELECTED;
5002 /* find the item and subitem structures before we proceed */
5003 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5004 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5007 if (lpLVItem->iSubItem)
5009 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5010 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5013 pItemHdr = &lpItem->hdr;
5015 /* Do we need to query the state from the app? */
5016 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
5018 dispInfo.item.mask |= LVIF_STATE;
5019 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5022 /* Do we need to enquire about the image? */
5023 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK)
5024 dispInfo.item.mask |= LVIF_IMAGE;
5026 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5027 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5029 dispInfo.item.mask |= LVIF_TEXT;
5030 dispInfo.item.pszText = lpLVItem->pszText;
5031 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5032 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5033 *dispInfo.item.pszText = '\0';
5036 /* If we don't have all the requested info, query the application */
5037 if (dispInfo.item.mask != 0)
5039 dispInfo.item.iItem = lpLVItem->iItem;
5040 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5041 dispInfo.item.lParam = lpItem->lParam;
5042 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5043 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5046 /* we should not store values for subitems */
5047 if (lpLVItem->iSubItem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5049 /* Now, handle the iImage field */
5050 if (dispInfo.item.mask & LVIF_IMAGE)
5052 lpLVItem->iImage = dispInfo.item.iImage;
5053 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5054 pItemHdr->iImage = dispInfo.item.iImage;
5056 else if (lpLVItem->mask & LVIF_IMAGE)
5057 lpLVItem->iImage = pItemHdr->iImage;
5059 /* The pszText field */
5060 if (dispInfo.item.mask & LVIF_TEXT)
5062 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5063 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5065 lpLVItem->pszText = dispInfo.item.pszText;
5067 else if (lpLVItem->mask & LVIF_TEXT)
5069 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5070 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5073 /* if this is a subitem, we're done */
5074 if (lpLVItem->iSubItem) return TRUE;
5076 /* Next is the lParam field */
5077 if (dispInfo.item.mask & LVIF_PARAM)
5079 lpLVItem->lParam = dispInfo.item.lParam;
5080 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5081 lpItem->lParam = dispInfo.item.lParam;
5083 else if (lpLVItem->mask & LVIF_PARAM)
5084 lpLVItem->lParam = lpItem->lParam;
5086 /* ... the state field (this one is different due to uCallbackmask) */
5087 if (lpLVItem->mask & LVIF_STATE)
5089 lpLVItem->state = lpItem->state;
5090 if (dispInfo.item.mask & LVIF_STATE)
5092 lpLVItem->state &= ~dispInfo.item.stateMask;
5093 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5095 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5097 lpLVItem->state &= ~LVIS_FOCUSED;
5098 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5099 lpLVItem->state |= LVIS_FOCUSED;
5101 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5103 lpLVItem->state &= ~LVIS_SELECTED;
5104 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5105 lpLVItem->state |= LVIS_SELECTED;
5109 /* and last, but not least, the indent field */
5110 if (lpLVItem->mask & LVIF_INDENT)
5111 lpLVItem->iIndent = lpItem->iIndent;
5118 * Retrieves item attributes.
5121 * [I] hwnd : window handle
5122 * [IO] lpLVItem : item info
5123 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5124 * if FALSE, the lpLVItem is a LPLVITEMA.
5127 * This is the external 'GetItem' interface -- it properly copies
5128 * the text in the provided buffer.
5134 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5139 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5142 pszText = lpLVItem->pszText;
5143 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5144 if (bResult && lpLVItem->pszText != pszText)
5145 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5146 lpLVItem->pszText = pszText;
5154 * Retrieves the position (upper-left) of the listview control item.
5155 * Note that for LVS_ICON style, the upper-left is that of the icon
5156 * and not the bounding box.
5159 * [I] infoPtr : valid pointer to the listview structure
5160 * [I] nItem : item index
5161 * [O] lpptPosition : coordinate information
5167 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5169 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5172 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5174 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5176 LISTVIEW_GetOrigin(infoPtr, &Origin);
5177 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5179 if (uView == LVS_ICON)
5181 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5182 lpptPosition->y += ICON_TOP_PADDING;
5184 lpptPosition->x += Origin.x;
5185 lpptPosition->y += Origin.y;
5187 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5194 * Retrieves the bounding rectangle for a listview control item.
5197 * [I] infoPtr : valid pointer to the listview structure
5198 * [I] nItem : item index
5199 * [IO] lprc : bounding rectangle coordinates
5200 * lprc->left specifies the portion of the item for which the bounding
5201 * rectangle will be retrieved.
5203 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5204 * including the icon and label.
5207 * * Experiment shows that native control returns:
5208 * * width = min (48, length of text line)
5209 * * .left = position.x - (width - iconsize.cx)/2
5210 * * .right = .left + width
5211 * * height = #lines of text * ntmHeight + icon height + 8
5212 * * .top = position.y - 2
5213 * * .bottom = .top + height
5214 * * separation between items .y = itemSpacing.cy - height
5215 * * .x = itemSpacing.cx - width
5216 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5219 * * Experiment shows that native control returns:
5220 * * width = iconSize.cx + 16
5221 * * .left = position.x - (width - iconsize.cx)/2
5222 * * .right = .left + width
5223 * * height = iconSize.cy + 4
5224 * * .top = position.y - 2
5225 * * .bottom = .top + height
5226 * * separation between items .y = itemSpacing.cy - height
5227 * * .x = itemSpacing.cx - width
5228 * LVIR_LABEL Returns the bounding rectangle of the item text.
5231 * * Experiment shows that native control returns:
5232 * * width = text length
5233 * * .left = position.x - width/2
5234 * * .right = .left + width
5235 * * height = ntmH * linecount + 2
5236 * * .top = position.y + iconSize.cy + 6
5237 * * .bottom = .top + height
5238 * * separation between items .y = itemSpacing.cy - height
5239 * * .x = itemSpacing.cx - width
5240 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5241 * rectangles, but excludes columns in report view.
5248 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5249 * upon whether the window has the focus currently and on whether the item
5250 * is the one with the focus. Ensure that the control's record of which
5251 * item has the focus agrees with the items' records.
5253 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5255 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5256 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5257 BOOL doLabel = TRUE, oversizedBox = FALSE;
5258 POINT Position, Origin;
5262 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5264 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5266 LISTVIEW_GetOrigin(infoPtr, &Origin);
5267 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5269 /* Be smart and try to figure out the minimum we have to do */
5270 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5271 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5272 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5273 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5274 oversizedBox = TRUE;
5276 /* get what we need from the item before hand, so we make
5277 * only one request. This can speed up things, if data
5278 * is stored on the app side */
5280 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5281 if (doLabel) lvItem.mask |= LVIF_TEXT;
5282 lvItem.iItem = nItem;
5283 lvItem.iSubItem = 0;
5284 lvItem.pszText = szDispText;
5285 lvItem.cchTextMax = DISP_TEXT_SIZE;
5286 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5287 /* we got the state already up, simulate it here, to avoid a reget */
5288 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5290 lvItem.mask |= LVIF_STATE;
5291 lvItem.stateMask = LVIS_FOCUSED;
5292 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5295 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5296 lprc->left = LVIR_BOUNDS;
5300 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5304 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5308 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5311 case LVIR_SELECTBOUNDS:
5312 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5313 UnionRect(lprc, lprc, &label_rect);
5317 WARN("Unknown value: %ld\n", lprc->left);
5321 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5323 TRACE(" rect=%s\n", debugrect(lprc));
5330 * Retrieves the spacing between listview control items.
5333 * [I] infoPtr : valid pointer to the listview structure
5334 * [IO] lprc : rectangle to receive the output
5335 * on input, lprc->top = nSubItem
5336 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5338 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5339 * not only those of the first column.
5340 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5346 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5351 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5353 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5354 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5356 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5358 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5360 lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5361 lvItem.iItem = nItem;
5362 lvItem.iSubItem = lprc->top;
5364 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5368 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5373 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5377 ERR("Unknown bounds=%ld\n", lprc->left);
5381 OffsetRect(lprc, Position.x, Position.y);
5388 * Retrieves the width of a label.
5391 * [I] infoPtr : valid pointer to the listview structure
5394 * SUCCESS : string width (in pixels)
5397 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5399 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5402 TRACE("(nItem=%d)\n", nItem);
5404 lvItem.mask = LVIF_TEXT;
5405 lvItem.iItem = nItem;
5406 lvItem.iSubItem = 0;
5407 lvItem.pszText = szDispText;
5408 lvItem.cchTextMax = DISP_TEXT_SIZE;
5409 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5411 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5416 * Retrieves the spacing between listview control items.
5419 * [I] infoPtr : valid pointer to the listview structure
5420 * [I] bSmall : flag for small or large icon
5423 * Horizontal + vertical spacing
5425 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5431 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5435 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5436 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5438 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5445 * Retrieves the state of a listview control item.
5448 * [I] infoPtr : valid pointer to the listview structure
5449 * [I] nItem : item index
5450 * [I] uMask : state mask
5453 * State specified by the mask.
5455 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5459 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5461 lvItem.iItem = nItem;
5462 lvItem.iSubItem = 0;
5463 lvItem.mask = LVIF_STATE;
5464 lvItem.stateMask = uMask;
5465 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5467 return lvItem.state & uMask;
5472 * Retrieves the text of a listview control item or subitem.
5475 * [I] hwnd : window handle
5476 * [I] nItem : item index
5477 * [IO] lpLVItem : item information
5478 * [I] isW : TRUE if lpLVItem is Unicode
5481 * SUCCESS : string length
5484 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5486 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5488 lpLVItem->mask = LVIF_TEXT;
5489 lpLVItem->iItem = nItem;
5490 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5492 return textlenT(lpLVItem->pszText, isW);
5497 * Searches for an item based on properties + relationships.
5500 * [I] infoPtr : valid pointer to the listview structure
5501 * [I] nItem : item index
5502 * [I] uFlags : relationship flag
5505 * SUCCESS : item index
5508 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5510 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5512 LVFINDINFOW lvFindInfo;
5513 INT nCountPerColumn;
5517 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5518 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5520 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5522 if (uFlags & LVNI_CUT)
5525 if (uFlags & LVNI_DROPHILITED)
5526 uMask |= LVIS_DROPHILITED;
5528 if (uFlags & LVNI_FOCUSED)
5529 uMask |= LVIS_FOCUSED;
5531 if (uFlags & LVNI_SELECTED)
5532 uMask |= LVIS_SELECTED;
5534 /* if we're asked for the focused item, that's only one,
5535 * so it's worth optimizing */
5536 if (uFlags & LVNI_FOCUSED)
5538 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5539 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5542 if (uFlags & LVNI_ABOVE)
5544 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5549 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5555 /* Special case for autoarrange - move 'til the top of a list */
5556 if (is_autoarrange(infoPtr))
5558 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5559 while (nItem - nCountPerRow >= 0)
5561 nItem -= nCountPerRow;
5562 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5567 lvFindInfo.flags = LVFI_NEARESTXY;
5568 lvFindInfo.vkDirection = VK_UP;
5569 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5570 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5572 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5577 else if (uFlags & LVNI_BELOW)
5579 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5581 while (nItem < infoPtr->nItemCount)
5584 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5590 /* Special case for autoarrange - move 'til the bottom of a list */
5591 if (is_autoarrange(infoPtr))
5593 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5594 while (nItem + nCountPerRow < infoPtr->nItemCount )
5596 nItem += nCountPerRow;
5597 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5602 lvFindInfo.flags = LVFI_NEARESTXY;
5603 lvFindInfo.vkDirection = VK_DOWN;
5604 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5605 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5607 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5612 else if (uFlags & LVNI_TOLEFT)
5614 if (uView == LVS_LIST)
5616 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5617 while (nItem - nCountPerColumn >= 0)
5619 nItem -= nCountPerColumn;
5620 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5624 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5626 /* Special case for autoarrange - move 'ti the beginning of a row */
5627 if (is_autoarrange(infoPtr))
5629 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5630 while (nItem % nCountPerRow > 0)
5633 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5638 lvFindInfo.flags = LVFI_NEARESTXY;
5639 lvFindInfo.vkDirection = VK_LEFT;
5640 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5641 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5643 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5648 else if (uFlags & LVNI_TORIGHT)
5650 if (uView == LVS_LIST)
5652 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5653 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5655 nItem += nCountPerColumn;
5656 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5660 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5662 /* Special case for autoarrange - move 'til the end of a row */
5663 if (is_autoarrange(infoPtr))
5665 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5666 while (nItem % nCountPerRow < nCountPerRow - 1 )
5669 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5674 lvFindInfo.flags = LVFI_NEARESTXY;
5675 lvFindInfo.vkDirection = VK_RIGHT;
5676 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5677 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5679 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5688 /* search by index */
5689 for (i = nItem; i < infoPtr->nItemCount; i++)
5691 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5699 /* LISTVIEW_GetNumberOfWorkAreas */
5703 * Retrieves the origin coordinates when in icon or small icon display mode.
5706 * [I] infoPtr : valid pointer to the listview structure
5707 * [O] lpptOrigin : coordinate information
5712 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5714 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5715 INT nHorzPos = 0, nVertPos = 0;
5716 SCROLLINFO scrollInfo;
5718 scrollInfo.cbSize = sizeof(SCROLLINFO);
5719 scrollInfo.fMask = SIF_POS;
5721 if ((infoPtr->dwStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5722 nHorzPos = scrollInfo.nPos;
5723 if ((infoPtr->dwStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5724 nVertPos = scrollInfo.nPos;
5726 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5728 lpptOrigin->x = infoPtr->rcList.left;
5729 lpptOrigin->y = infoPtr->rcList.top;
5730 if (uView == LVS_LIST)
5731 nHorzPos *= infoPtr->nItemWidth;
5732 else if (uView == LVS_REPORT)
5733 nVertPos *= infoPtr->nItemHeight;
5735 lpptOrigin->x -= nHorzPos;
5736 lpptOrigin->y -= nVertPos;
5738 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5743 * Retrieves the width of a string.
5746 * [I] hwnd : window handle
5747 * [I] lpszText : text string to process
5748 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5751 * SUCCESS : string width (in pixels)
5754 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5759 if (is_textT(lpszText, isW))
5761 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5762 HDC hdc = GetDC(infoPtr->hwndSelf);
5763 HFONT hOldFont = SelectObject(hdc, hFont);
5766 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5768 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5769 SelectObject(hdc, hOldFont);
5770 ReleaseDC(infoPtr->hwndSelf, hdc);
5772 return stringSize.cx;
5777 * Determines which listview item is located at the specified position.
5780 * [I] infoPtr : valid pointer to the listview structure
5781 * [IO] lpht : hit test information
5782 * [I] subitem : fill out iSubItem.
5783 * [I] select : return the index only if the hit selects the item
5786 * (mm 20001022): We must not allow iSubItem to be touched, for
5787 * an app might pass only a structure with space up to iItem!
5788 * (MS Office 97 does that for instance in the file open dialog)
5791 * SUCCESS : item index
5794 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5796 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5797 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5798 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5799 POINT Origin, Position, opt;
5804 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5808 if (subitem) lpht->iSubItem = 0;
5810 if (infoPtr->rcList.left > lpht->pt.x)
5811 lpht->flags |= LVHT_TOLEFT;
5812 else if (infoPtr->rcList.right < lpht->pt.x)
5813 lpht->flags |= LVHT_TORIGHT;
5815 if (infoPtr->rcList.top > lpht->pt.y)
5816 lpht->flags |= LVHT_ABOVE;
5817 else if (infoPtr->rcList.bottom < lpht->pt.y)
5818 lpht->flags |= LVHT_BELOW;
5820 TRACE("lpht->flags=0x%x\n", lpht->flags);
5821 if (lpht->flags) return -1;
5823 lpht->flags |= LVHT_NOWHERE;
5825 LISTVIEW_GetOrigin(infoPtr, &Origin);
5827 /* first deal with the large items */
5828 rcSearch.left = lpht->pt.x;
5829 rcSearch.top = lpht->pt.y;
5830 rcSearch.right = rcSearch.left + 1;
5831 rcSearch.bottom = rcSearch.top + 1;
5833 iterator_frameditems(&i, infoPtr, &rcSearch);
5834 iterator_next(&i); /* go to first item in the sequence */
5836 iterator_destroy(&i);
5838 TRACE("lpht->iItem=%d\n", iItem);
5839 if (iItem == -1) return -1;
5841 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5842 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5843 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5844 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5845 lvItem.iItem = iItem;
5846 lvItem.iSubItem = 0;
5847 lvItem.pszText = szDispText;
5848 lvItem.cchTextMax = DISP_TEXT_SIZE;
5849 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5850 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5852 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5853 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
5854 opt.x = lpht->pt.x - Position.x - Origin.x;
5855 opt.y = lpht->pt.y - Position.y - Origin.y;
5857 if (uView == LVS_REPORT)
5860 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5861 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5862 if (!PtInRect(&rcBounds, opt)) return -1;
5864 if (PtInRect(&rcIcon, opt))
5865 lpht->flags |= LVHT_ONITEMICON;
5866 else if (PtInRect(&rcLabel, opt))
5867 lpht->flags |= LVHT_ONITEMLABEL;
5868 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5869 lpht->flags |= LVHT_ONITEMSTATEICON;
5870 if (lpht->flags & LVHT_ONITEM)
5871 lpht->flags &= ~LVHT_NOWHERE;
5873 TRACE("lpht->flags=0x%x\n", lpht->flags);
5874 if (uView == LVS_REPORT && subitem)
5878 rcBounds.right = rcBounds.left;
5879 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
5881 rcBounds.left = rcBounds.right;
5882 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5883 if (PtInRect(&rcBounds, opt))
5891 if (select && !(uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)))
5893 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5894 if (!PtInRect(&rcBounds, opt)) iItem = -1;
5896 return lpht->iItem = iItem;
5900 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5901 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5902 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5903 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5904 their own sort proc. when sending LVM_SORTITEMS.
5907 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5909 LVS_SORTXXX must be specified,
5910 LVS_OWNERDRAW is not set,
5911 <item>.pszText is not LPSTR_TEXTCALLBACK.
5913 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5914 are sorted based on item text..."
5916 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5918 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
5919 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
5920 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5922 /* if we're sorting descending, negate the return value */
5923 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5928 * Inserts a new item in the listview control.
5931 * [I] infoPtr : valid pointer to the listview structure
5932 * [I] lpLVItem : item information
5933 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5936 * SUCCESS : new item index
5939 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
5941 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5946 BOOL is_sorted, has_changed;
5949 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5951 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
5953 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5954 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
5956 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
5958 if ( !(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO))) )
5961 /* insert item in listview control data structure */
5962 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
5963 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
5965 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5966 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5968 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
5969 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
5970 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
5971 if (nItem == -1) goto fail;
5972 infoPtr->nItemCount++;
5974 /* shift indices first so they don't get tangled */
5975 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5977 /* set the item attributes */
5978 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5980 /* full size structure expected - _WIN32IE >= 0x560 */
5983 else if (lpLVItem->mask & LVIF_INDENT)
5985 /* indent member expected - _WIN32IE >= 0x300 */
5986 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
5990 /* minimal structure expected */
5991 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
5994 item.state &= ~LVIS_STATEIMAGEMASK;
5995 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
5997 /* if we're sorted, sort the list, and update the index */
6000 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6001 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6002 assert(nItem != -1);
6005 /* make room for the position, if we are in the right mode */
6006 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6008 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6010 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6012 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6017 /* send LVN_INSERTITEM notification */
6018 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6020 nmlv.lParam = lpItem->lParam;
6021 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6023 /* align items (set position of each item) */
6024 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6028 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6029 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6031 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6033 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6036 /* now is the invalidation fun */
6037 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6041 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6042 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6043 infoPtr->nItemCount--;
6045 DPA_DeletePtr(hdpaSubItems, 0);
6046 DPA_Destroy (hdpaSubItems);
6053 * Redraws a range of items.
6056 * [I] infoPtr : valid pointer to the listview structure
6057 * [I] nFirst : first item
6058 * [I] nLast : last item
6064 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6068 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6069 max(nFirst, nLast) >= infoPtr->nItemCount)
6072 for (i = nFirst; i <= nLast; i++)
6073 LISTVIEW_InvalidateItem(infoPtr, i);
6080 * Scroll the content of a listview.
6083 * [I] infoPtr : valid pointer to the listview structure
6084 * [I] dx : horizontal scroll amount in pixels
6085 * [I] dy : vertical scroll amount in pixels
6092 * If the control is in report mode (LVS_REPORT) the control can
6093 * be scrolled only in line increments. "dy" will be rounded to the
6094 * nearest number of pixels that are a whole line. Ex: if line height
6095 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6096 * is passed the the scroll will be 0. (per MSDN 7/2002)
6098 * For: (per experimentaion with native control and CSpy ListView)
6099 * LVS_ICON dy=1 = 1 pixel (vertical only)
6101 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6103 * LVS_LIST dx=1 = 1 column (horizontal only)
6104 * but will only scroll 1 column per message
6105 * no matter what the value.
6106 * dy must be 0 or FALSE returned.
6107 * LVS_REPORT dx=1 = 1 pixel
6111 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6113 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6115 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6116 dy /= infoPtr->nItemHeight;
6119 if (dy != 0) return FALSE;
6126 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6127 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6134 * Sets the background color.
6137 * [I] infoPtr : valid pointer to the listview structure
6138 * [I] clrBk : background color
6144 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6146 TRACE("(clrBk=%lx)\n", clrBk);
6148 if(infoPtr->clrBk != clrBk) {
6149 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6150 infoPtr->clrBk = clrBk;
6151 if (clrBk == CLR_NONE)
6152 infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6154 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6155 LISTVIEW_InvalidateList(infoPtr);
6161 /* LISTVIEW_SetBkImage */
6163 /*** Helper for {Insert,Set}ColumnT *only* */
6164 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6166 if (lpColumn->mask & LVCF_FMT)
6168 /* format member is valid */
6169 lphdi->mask |= HDI_FORMAT;
6171 /* set text alignment (leftmost column must be left-aligned) */
6172 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
6173 lphdi->fmt |= HDF_LEFT;
6174 else if (lpColumn->fmt & LVCFMT_RIGHT)
6175 lphdi->fmt |= HDF_RIGHT;
6176 else if (lpColumn->fmt & LVCFMT_CENTER)
6177 lphdi->fmt |= HDF_CENTER;
6179 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6180 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6182 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6184 lphdi->fmt |= HDF_IMAGE;
6185 lphdi->iImage = I_IMAGECALLBACK;
6189 if (lpColumn->mask & LVCF_WIDTH)
6191 lphdi->mask |= HDI_WIDTH;
6192 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6194 /* make it fill the remainder of the controls width */
6198 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6200 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6201 lphdi->cxy += rcHeader.right - rcHeader.left;
6204 /* retrieve the layout of the header */
6205 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6206 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6208 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6211 lphdi->cxy = lpColumn->cx;
6214 if (lpColumn->mask & LVCF_TEXT)
6216 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6217 lphdi->fmt |= HDF_STRING;
6218 lphdi->pszText = lpColumn->pszText;
6219 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6222 if (lpColumn->mask & LVCF_IMAGE)
6224 lphdi->mask |= HDI_IMAGE;
6225 lphdi->iImage = lpColumn->iImage;
6228 if (lpColumn->mask & LVCF_ORDER)
6230 lphdi->mask |= HDI_ORDER;
6231 lphdi->iOrder = lpColumn->iOrder;
6238 * Inserts a new column.
6241 * [I] infoPtr : valid pointer to the listview structure
6242 * [I] nColumn : column index
6243 * [I] lpColumn : column information
6244 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6247 * SUCCESS : new column index
6250 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6251 const LVCOLUMNW *lpColumn, BOOL isW)
6253 COLUMN_INFO *lpColumnInfo;
6257 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6259 if (!lpColumn || nColumn < 0) return -1;
6260 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6262 ZeroMemory(&hdi, sizeof(HDITEMW));
6263 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6265 /* insert item in header control */
6266 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6267 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6268 (WPARAM)nColumn, (LPARAM)&hdi);
6269 if (nNewColumn == -1) return -1;
6270 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6272 /* create our own column info */
6273 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6274 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6276 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6277 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6279 /* now we have to actually adjust the data */
6280 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6282 SUBITEM_INFO *lpSubItem;
6286 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6288 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6289 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6291 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6292 if (lpSubItem->iSubItem >= nNewColumn)
6293 lpSubItem->iSubItem++;
6298 /* make space for the new column */
6299 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6304 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6307 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6315 * Sets the attributes of a header item.
6318 * [I] infoPtr : valid pointer to the listview structure
6319 * [I] nColumn : column index
6320 * [I] lpColumn : column attributes
6321 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6327 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6328 const LVCOLUMNW *lpColumn, BOOL isW)
6330 HDITEMW hdi, hdiget;
6333 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6335 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6337 ZeroMemory(&hdi, sizeof(HDITEMW));
6338 if (lpColumn->mask & LVCF_FMT)
6340 hdi.mask |= HDI_FORMAT;
6341 hdiget.mask = HDI_FORMAT;
6342 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6343 hdi.fmt = hdiget.fmt & HDF_STRING;
6345 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6347 /* set header item attributes */
6348 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6349 if (!bResult) return FALSE;
6351 if (lpColumn->mask & LVCF_FMT)
6353 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6354 int oldFmt = lpColumnInfo->fmt;
6356 lpColumnInfo->fmt = lpColumn->fmt;
6357 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6359 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6360 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6369 * Sets the column order array
6372 * [I] infoPtr : valid pointer to the listview structure
6373 * [I] iCount : number of elements in column order array
6374 * [I] lpiArray : pointer to column order array
6380 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6382 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6393 * Sets the width of a column
6396 * [I] infoPtr : valid pointer to the listview structure
6397 * [I] nColumn : column index
6398 * [I] cx : column width
6404 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6406 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6407 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6411 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6413 /* set column width only if in report or list mode */
6414 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6416 /* take care of invalid cx values */
6417 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6418 else if (uView == LVS_LIST && cx < 1) return FALSE;
6420 /* resize all columns if in LVS_LIST mode */
6421 if(uView == LVS_LIST)
6423 infoPtr->nItemWidth = cx;
6424 LISTVIEW_InvalidateList(infoPtr);
6428 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6430 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6435 lvItem.mask = LVIF_TEXT;
6437 lvItem.iSubItem = nColumn;
6438 lvItem.pszText = szDispText;
6439 lvItem.cchTextMax = DISP_TEXT_SIZE;
6440 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6442 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6443 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6444 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6446 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6447 max_cx += infoPtr->iconSize.cx;
6448 max_cx += TRAILING_LABEL_PADDING;
6451 /* autosize based on listview items width */
6452 if(cx == LVSCW_AUTOSIZE)
6454 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6456 /* if iCol is the last column make it fill the remainder of the controls width */
6457 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6462 LISTVIEW_GetOrigin(infoPtr, &Origin);
6463 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6465 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6469 /* Despite what the MS docs say, if this is not the last
6470 column, then MS resizes the column to the width of the
6471 largest text string in the column, including headers
6472 and items. This is different from LVSCW_AUTOSIZE in that
6473 LVSCW_AUTOSIZE ignores the header string length. */
6476 /* retrieve header text */
6477 hdi.mask = HDI_TEXT;
6478 hdi.cchTextMax = DISP_TEXT_SIZE;
6479 hdi.pszText = szDispText;
6480 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6482 HDC hdc = GetDC(infoPtr->hwndSelf);
6483 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6486 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6487 cx = size.cx + TRAILING_HEADER_PADDING;
6488 /* FIXME: Take into account the header image, if one is present */
6489 SelectObject(hdc, old_font);
6490 ReleaseDC(infoPtr->hwndSelf, hdc);
6492 cx = max (cx, max_cx);
6496 if (cx < 0) return FALSE;
6498 /* call header to update the column change */
6499 hdi.mask = HDI_WIDTH;
6501 TRACE("hdi.cxy=%d\n", hdi.cxy);
6502 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6507 * Sets the extended listview style.
6510 * [I] infoPtr : valid pointer to the listview structure
6512 * [I] dwStyle : style
6515 * SUCCESS : previous style
6518 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6520 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6524 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6526 infoPtr->dwLvExStyle = dwStyle;
6533 * Sets the new hot cursor used during hot tracking and hover selection.
6536 * [I] infoPtr : valid pointer to the listview structure
6537 * [I} hCurosr : the new hot cursor handle
6540 * Returns the previous hot cursor
6542 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6544 HCURSOR oldCursor = infoPtr->hHotCursor;
6546 infoPtr->hHotCursor = hCursor;
6554 * Sets the hot item index.
6557 * [I] infoPtr : valid pointer to the listview structure
6558 * [I] iIndex : index
6561 * SUCCESS : previous hot item index
6562 * FAILURE : -1 (no hot item)
6564 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6566 INT iOldIndex = infoPtr->nHotItem;
6568 infoPtr->nHotItem = iIndex;
6576 * Sets the amount of time the cursor must hover over an item before it is selected.
6579 * [I] infoPtr : valid pointer to the listview structure
6580 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6583 * Returns the previous hover time
6585 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6587 DWORD oldHoverTime = infoPtr->dwHoverTime;
6589 infoPtr->dwHoverTime = dwHoverTime;
6591 return oldHoverTime;
6596 * Sets spacing for icons of LVS_ICON style.
6599 * [I] infoPtr : valid pointer to the listview structure
6600 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6601 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6604 * MAKELONG(oldcx, oldcy)
6606 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6608 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6609 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6611 TRACE("requested=(%d,%d)\n", cx, cy);
6613 /* this is supported only for LVS_ICON style */
6614 if (uView != LVS_ICON) return oldspacing;
6616 /* set to defaults, if instructed to */
6617 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6618 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6620 /* if 0 then compute width
6621 * FIXME: Should scan each item and determine max width of
6622 * icon or label, then make that the width */
6624 cx = infoPtr->iconSpacing.cx;
6626 /* if 0 then compute height */
6628 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6629 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6632 infoPtr->iconSpacing.cx = cx;
6633 infoPtr->iconSpacing.cy = cy;
6635 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6636 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6637 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6638 infoPtr->ntmHeight);
6640 /* these depend on the iconSpacing */
6641 LISTVIEW_UpdateItemSize(infoPtr);
6646 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6650 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6657 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6658 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6667 * [I] infoPtr : valid pointer to the listview structure
6668 * [I] nType : image list type
6669 * [I] himl : image list handle
6672 * SUCCESS : old image list
6675 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6677 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6678 INT oldHeight = infoPtr->nItemHeight;
6679 HIMAGELIST himlOld = 0;
6681 TRACE("(nType=%d, himl=%p\n", nType, himl);
6686 himlOld = infoPtr->himlNormal;
6687 infoPtr->himlNormal = himl;
6688 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6689 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6693 himlOld = infoPtr->himlSmall;
6694 infoPtr->himlSmall = himl;
6695 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6699 himlOld = infoPtr->himlState;
6700 infoPtr->himlState = himl;
6701 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6702 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6706 ERR("Unknown icon type=%d\n", nType);
6710 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6711 if (infoPtr->nItemHeight != oldHeight)
6712 LISTVIEW_UpdateScroll(infoPtr);
6719 * Preallocates memory (does *not* set the actual count of items !)
6722 * [I] infoPtr : valid pointer to the listview structure
6723 * [I] nItems : item count (projected number of items to allocate)
6724 * [I] dwFlags : update flags
6730 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6732 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6734 if (infoPtr->dwStyle & LVS_OWNERDATA)
6736 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6737 INT nOldCount = infoPtr->nItemCount;
6739 if (nItems < nOldCount)
6741 RANGE range = { nItems, nOldCount };
6742 ranges_del(infoPtr->selectionRanges, range);
6743 if (infoPtr->nFocusedItem >= nItems)
6745 infoPtr->nFocusedItem = -1;
6746 SetRectEmpty(&infoPtr->rcFocus);
6750 infoPtr->nItemCount = nItems;
6751 LISTVIEW_UpdateScroll(infoPtr);
6753 /* the flags are valid only in ownerdata report and list modes */
6754 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6756 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6757 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6759 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6760 LISTVIEW_InvalidateList(infoPtr);
6767 LISTVIEW_GetOrigin(infoPtr, &Origin);
6768 nFrom = min(nOldCount, nItems);
6769 nTo = max(nOldCount, nItems);
6771 if (uView == LVS_REPORT)
6774 rcErase.top = nFrom * infoPtr->nItemHeight;
6775 rcErase.right = infoPtr->nItemWidth;
6776 rcErase.bottom = nTo * infoPtr->nItemHeight;
6777 OffsetRect(&rcErase, Origin.x, Origin.y);
6778 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6779 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6783 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6785 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6786 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6787 rcErase.right = rcErase.left + 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);
6793 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6795 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
6796 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6797 OffsetRect(&rcErase, Origin.x, Origin.y);
6798 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6799 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6805 /* According to MSDN for non-LVS_OWNERDATA this is just
6806 * a performance issue. The control allocates its internal
6807 * data structures for the number of items specified. It
6808 * cuts down on the number of memory allocations. Therefore
6809 * we will just issue a WARN here
6811 WARN("for non-ownerdata performance option not implemented.\n");
6819 * Sets the position of an item.
6822 * [I] infoPtr : valid pointer to the listview structure
6823 * [I] nItem : item index
6824 * [I] pt : coordinate
6830 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6832 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6835 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6837 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6838 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6840 LISTVIEW_GetOrigin(infoPtr, &Origin);
6842 /* This point value seems to be an undocumented feature.
6843 * The best guess is that it means either at the origin,
6844 * or at true beginning of the list. I will assume the origin. */
6845 if ((pt.x == -1) && (pt.y == -1))
6848 if (uView == LVS_ICON)
6850 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6851 pt.y -= ICON_TOP_PADDING;
6856 infoPtr->bAutoarrange = FALSE;
6858 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
6863 * Sets the state of one or many items.
6866 * [I] infoPtr : valid pointer to the listview structure
6867 * [I] nItem : item index
6868 * [I] lpLVItem : item or subitem info
6874 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
6876 BOOL bResult = TRUE;
6879 lvItem.iItem = nItem;
6880 lvItem.iSubItem = 0;
6881 lvItem.mask = LVIF_STATE;
6882 lvItem.state = lpLVItem->state;
6883 lvItem.stateMask = lpLVItem->stateMask;
6884 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6888 /* apply to all items */
6889 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6890 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6893 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6900 * Sets the text of an item or subitem.
6903 * [I] hwnd : window handle
6904 * [I] nItem : item index
6905 * [I] lpLVItem : item or subitem info
6906 * [I] isW : TRUE if input is Unicode
6912 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
6916 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6918 lvItem.iItem = nItem;
6919 lvItem.iSubItem = lpLVItem->iSubItem;
6920 lvItem.mask = LVIF_TEXT;
6921 lvItem.pszText = lpLVItem->pszText;
6922 lvItem.cchTextMax = lpLVItem->cchTextMax;
6924 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6926 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6931 * Set item index that marks the start of a multiple selection.
6934 * [I] infoPtr : valid pointer to the listview structure
6935 * [I] nIndex : index
6938 * Index number or -1 if there is no selection mark.
6940 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6942 INT nOldIndex = infoPtr->nSelectionMark;
6944 TRACE("(nIndex=%d)\n", nIndex);
6946 infoPtr->nSelectionMark = nIndex;
6953 * Sets the text background color.
6956 * [I] infoPtr : valid pointer to the listview structure
6957 * [I] clrTextBk : text background color
6963 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6965 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6967 if (infoPtr->clrTextBk != clrTextBk)
6969 infoPtr->clrTextBk = clrTextBk;
6970 LISTVIEW_InvalidateList(infoPtr);
6978 * Sets the text foreground color.
6981 * [I] infoPtr : valid pointer to the listview structure
6982 * [I] clrText : text color
6988 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6990 TRACE("(clrText=%lx)\n", clrText);
6992 if (infoPtr->clrText != clrText)
6994 infoPtr->clrText = clrText;
6995 LISTVIEW_InvalidateList(infoPtr);
7003 * Determines which listview item is located at the specified position.
7006 * [I] infoPtr : valid pointer to the listview structure
7007 * [I] hwndNewToolTip : handle to new ToolTip
7012 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7014 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7015 infoPtr->hwndToolTip = hwndNewToolTip;
7016 return hwndOldToolTip;
7019 /* LISTVIEW_SetUnicodeFormat */
7020 /* LISTVIEW_SetWorkAreas */
7024 * Callback internally used by LISTVIEW_SortItems()
7027 * [I] first : pointer to first ITEM_INFO to compare
7028 * [I] second : pointer to second ITEM_INFO to compare
7029 * [I] lParam : HWND of control
7032 * if first comes before second : negative
7033 * if first comes after second : positive
7034 * if first and second are equivalent : zero
7036 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7038 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7039 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7040 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7042 /* Forward the call to the client defined callback */
7043 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7048 * Sorts the listview items.
7051 * [I] infoPtr : valid pointer to the listview structure
7052 * [I] pfnCompare : application-defined value
7053 * [I] lParamSort : pointer to comparision callback
7059 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7061 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7064 LPVOID selectionMarkItem;
7068 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7070 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7072 if (!infoPtr->hdpaItems) return FALSE;
7074 /* if there are 0 or 1 items, there is no need to sort */
7075 if (infoPtr->nItemCount < 2) return TRUE;
7077 if (infoPtr->nFocusedItem >= 0)
7079 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7080 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7081 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7083 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7084 /* clear the lpItem->state for non-selected ones */
7085 /* remove the selection ranges */
7087 infoPtr->pfnCompare = pfnCompare;
7088 infoPtr->lParamSort = lParamSort;
7089 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
7091 /* Adjust selections and indices so that they are the way they should
7092 * be after the sort (otherwise, the list items move around, but
7093 * whatever is at the item's previous original position will be
7096 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7097 for (i=0; i < infoPtr->nItemCount; i++)
7099 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7100 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7102 if (lpItem->state & LVIS_SELECTED)
7104 item.state = LVIS_SELECTED;
7105 item.stateMask = LVIS_SELECTED;
7106 LISTVIEW_SetItemState(infoPtr, i, &item);
7108 if (lpItem->state & LVIS_FOCUSED)
7110 infoPtr->nFocusedItem = i;
7111 lpItem->state &= ~LVIS_FOCUSED;
7114 if (selectionMarkItem != NULL)
7115 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7116 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7118 /* refresh the display */
7119 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7120 LISTVIEW_InvalidateList(infoPtr);
7127 * Updates an items or rearranges the listview control.
7130 * [I] infoPtr : valid pointer to the listview structure
7131 * [I] nItem : item index
7137 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7139 TRACE("(nItem=%d)\n", nItem);
7141 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7143 /* rearrange with default alignment style */
7144 if (is_autoarrange(infoPtr))
7145 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7147 LISTVIEW_InvalidateItem(infoPtr, nItem);
7155 * Creates the listview control.
7158 * [I] hwnd : window handle
7159 * [I] lpcs : the create parameters
7165 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7167 LISTVIEW_INFO *infoPtr;
7168 UINT uView = lpcs->style & LVS_TYPEMASK;
7171 TRACE("(lpcs=%p)\n", lpcs);
7173 /* initialize info pointer */
7174 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7175 if (!infoPtr) return -1;
7177 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7179 infoPtr->hwndSelf = hwnd;
7180 infoPtr->dwStyle = lpcs->style;
7181 /* determine the type of structures to use */
7182 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7183 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7185 /* initialize color information */
7186 infoPtr->clrBk = CLR_NONE;
7187 infoPtr->clrText = comctl32_color.clrWindowText;
7188 infoPtr->clrTextBk = CLR_DEFAULT;
7189 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7191 /* set default values */
7192 infoPtr->nFocusedItem = -1;
7193 infoPtr->nSelectionMark = -1;
7194 infoPtr->nHotItem = -1;
7195 infoPtr->bRedraw = TRUE;
7196 infoPtr->bNoItemMetrics = TRUE;
7197 infoPtr->bDoChangeNotify = TRUE;
7198 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7199 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7200 infoPtr->nEditLabelItem = -1;
7201 infoPtr->dwHoverTime = -1; /* default system hover time */
7203 /* get default font (icon title) */
7204 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7205 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7206 infoPtr->hFont = infoPtr->hDefaultFont;
7207 LISTVIEW_SaveTextMetrics(infoPtr);
7210 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7211 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7212 0, 0, 0, 0, hwnd, NULL,
7213 lpcs->hInstance, NULL);
7214 if (!infoPtr->hwndHeader) goto fail;
7216 /* set header unicode format */
7217 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7219 /* set header font */
7220 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7222 /* allocate memory for the data structure */
7223 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7224 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7225 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7226 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7227 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7229 /* initialize the icon sizes */
7230 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7231 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7233 /* init item size to avoid division by 0 */
7234 LISTVIEW_UpdateItemSize (infoPtr);
7236 if (uView == LVS_REPORT)
7238 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7240 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7244 /* set HDS_HIDDEN flag to hide the header bar */
7245 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7246 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7253 DestroyWindow(infoPtr->hwndHeader);
7254 ranges_destroy(infoPtr->selectionRanges);
7255 DPA_Destroy(infoPtr->hdpaItems);
7256 DPA_Destroy(infoPtr->hdpaPosX);
7257 DPA_Destroy(infoPtr->hdpaPosY);
7258 DPA_Destroy(infoPtr->hdpaColumns);
7265 * Erases the background of the listview control.
7268 * [I] infoPtr : valid pointer to the listview structure
7269 * [I] hdc : device context handle
7275 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7279 TRACE("(hdc=%p)\n", hdc);
7281 if (!GetClipBox(hdc, &rc)) return FALSE;
7283 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7289 * Helper function for LISTVIEW_[HV]Scroll *only*.
7290 * Performs vertical/horizontal scrolling by a give amount.
7293 * [I] infoPtr : valid pointer to the listview structure
7294 * [I] dx : amount of horizontal scroll
7295 * [I] dy : amount of vertical scroll
7297 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7299 /* now we can scroll the list */
7300 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7301 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7302 /* if we have focus, adjust rect */
7303 OffsetRect(&infoPtr->rcFocus, dx, dy);
7304 UpdateWindow(infoPtr->hwndSelf);
7309 * Performs vertical scrolling.
7312 * [I] infoPtr : valid pointer to the listview structure
7313 * [I] nScrollCode : scroll code
7314 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7315 * [I] hScrollWnd : scrollbar control window handle
7321 * SB_LINEUP/SB_LINEDOWN:
7322 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7323 * for LVS_REPORT is 1 line
7324 * for LVS_LIST cannot occur
7327 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7328 INT nScrollDiff, HWND hScrollWnd)
7330 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7331 INT nOldScrollPos, nNewScrollPos;
7332 SCROLLINFO scrollInfo;
7335 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7336 debugscrollcode(nScrollCode), nScrollDiff);
7338 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7340 scrollInfo.cbSize = sizeof(SCROLLINFO);
7341 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7343 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7345 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7347 nOldScrollPos = scrollInfo.nPos;
7348 switch (nScrollCode)
7354 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7358 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7362 nScrollDiff = -scrollInfo.nPage;
7366 nScrollDiff = scrollInfo.nPage;
7369 case SB_THUMBPOSITION:
7371 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7378 /* quit right away if pos isn't changing */
7379 if (nScrollDiff == 0) return 0;
7381 /* calculate new position, and handle overflows */
7382 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7383 if (nScrollDiff > 0) {
7384 if (nNewScrollPos < nOldScrollPos ||
7385 nNewScrollPos > scrollInfo.nMax)
7386 nNewScrollPos = scrollInfo.nMax;
7388 if (nNewScrollPos > nOldScrollPos ||
7389 nNewScrollPos < scrollInfo.nMin)
7390 nNewScrollPos = scrollInfo.nMin;
7393 /* set the new position, and reread in case it changed */
7394 scrollInfo.fMask = SIF_POS;
7395 scrollInfo.nPos = nNewScrollPos;
7396 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7398 /* carry on only if it really changed */
7399 if (nNewScrollPos == nOldScrollPos) return 0;
7401 /* now adjust to client coordinates */
7402 nScrollDiff = nOldScrollPos - nNewScrollPos;
7403 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7405 /* and scroll the window */
7406 scroll_list(infoPtr, 0, nScrollDiff);
7413 * Performs horizontal scrolling.
7416 * [I] infoPtr : valid pointer to the listview structure
7417 * [I] nScrollCode : scroll code
7418 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7419 * [I] hScrollWnd : scrollbar control window handle
7425 * SB_LINELEFT/SB_LINERIGHT:
7426 * for LVS_ICON, LVS_SMALLICON 1 pixel
7427 * for LVS_REPORT is 1 pixel
7428 * for LVS_LIST is 1 column --> which is a 1 because the
7429 * scroll is based on columns not pixels
7432 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7433 INT nScrollDiff, HWND hScrollWnd)
7435 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7436 INT nOldScrollPos, nNewScrollPos;
7437 SCROLLINFO scrollInfo;
7439 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7440 debugscrollcode(nScrollCode), nScrollDiff);
7442 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7444 scrollInfo.cbSize = sizeof(SCROLLINFO);
7445 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7447 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7449 nOldScrollPos = scrollInfo.nPos;
7451 switch (nScrollCode)
7465 nScrollDiff = -scrollInfo.nPage;
7469 nScrollDiff = scrollInfo.nPage;
7472 case SB_THUMBPOSITION:
7474 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7481 /* quit right away if pos isn't changing */
7482 if (nScrollDiff == 0) return 0;
7484 /* calculate new position, and handle overflows */
7485 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7486 if (nScrollDiff > 0) {
7487 if (nNewScrollPos < nOldScrollPos ||
7488 nNewScrollPos > scrollInfo.nMax)
7489 nNewScrollPos = scrollInfo.nMax;
7491 if (nNewScrollPos > nOldScrollPos ||
7492 nNewScrollPos < scrollInfo.nMin)
7493 nNewScrollPos = scrollInfo.nMin;
7496 /* set the new position, and reread in case it changed */
7497 scrollInfo.fMask = SIF_POS;
7498 scrollInfo.nPos = nNewScrollPos;
7499 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7501 /* carry on only if it really changed */
7502 if (nNewScrollPos == nOldScrollPos) return 0;
7504 if(uView == LVS_REPORT)
7505 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7507 /* now adjust to client coordinates */
7508 nScrollDiff = nOldScrollPos - nNewScrollPos;
7509 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7511 /* and scroll the window */
7512 scroll_list(infoPtr, nScrollDiff, 0);
7517 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7519 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7520 INT gcWheelDelta = 0;
7521 UINT pulScrollLines = 3;
7522 SCROLLINFO scrollInfo;
7524 TRACE("(wheelDelta=%d)\n", wheelDelta);
7526 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7527 gcWheelDelta -= wheelDelta;
7529 scrollInfo.cbSize = sizeof(SCROLLINFO);
7530 scrollInfo.fMask = SIF_POS;
7537 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7538 * should be fixed in the future.
7540 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7541 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7545 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7547 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7548 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7549 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7554 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7565 * [I] infoPtr : valid pointer to the listview structure
7566 * [I] nVirtualKey : virtual key
7567 * [I] lKeyData : key data
7572 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7574 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7576 NMLVKEYDOWN nmKeyDown;
7578 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7580 /* send LVN_KEYDOWN notification */
7581 nmKeyDown.wVKey = nVirtualKey;
7582 nmKeyDown.flags = 0;
7583 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7585 switch (nVirtualKey)
7588 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7590 notify(infoPtr, NM_RETURN);
7591 notify(infoPtr, LVN_ITEMACTIVATE);
7596 if (infoPtr->nItemCount > 0)
7601 if (infoPtr->nItemCount > 0)
7602 nItem = infoPtr->nItemCount - 1;
7606 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7610 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7614 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7618 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7622 if (uView == LVS_REPORT)
7623 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7625 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7626 * LISTVIEW_GetCountPerRow(infoPtr);
7627 if(nItem < 0) nItem = 0;
7631 if (uView == LVS_REPORT)
7632 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7634 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7635 * LISTVIEW_GetCountPerRow(infoPtr);
7636 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7640 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7641 LISTVIEW_KeySelection(infoPtr, nItem);
7651 * [I] infoPtr : valid pointer to the listview structure
7656 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7660 /* if we did not have the focus, there's nothing to do */
7661 if (!infoPtr->bFocus) return 0;
7663 /* send NM_KILLFOCUS notification */
7664 notify(infoPtr, NM_KILLFOCUS);
7666 /* if we have a focus rectagle, get rid of it */
7667 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7669 /* set window focus flag */
7670 infoPtr->bFocus = FALSE;
7672 /* invalidate the selected items before reseting focus flag */
7673 LISTVIEW_InvalidateSelectedItems(infoPtr);
7680 * Processes double click messages (left mouse button).
7683 * [I] infoPtr : valid pointer to the listview structure
7684 * [I] wKey : key flag
7685 * [I] pts : mouse coordinate
7690 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7692 LVHITTESTINFO htInfo;
7694 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7696 /* send NM_RELEASEDCAPTURE notification */
7697 notify(infoPtr, NM_RELEASEDCAPTURE);
7699 htInfo.pt.x = pts.x;
7700 htInfo.pt.y = pts.y;
7702 /* send NM_DBLCLK notification */
7703 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7704 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7706 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7707 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
7714 * Processes mouse down messages (left mouse button).
7717 * [I] infoPtr : valid pointer to the listview structure
7718 * [I] wKey : key flag
7719 * [I] pts : mouse coordinate
7724 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7726 LVHITTESTINFO lvHitTestInfo;
7727 static BOOL bGroupSelect = TRUE;
7728 POINT pt = { pts.x, pts.y };
7731 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7733 /* send NM_RELEASEDCAPTURE notification */
7734 notify(infoPtr, NM_RELEASEDCAPTURE);
7736 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7738 /* set left button down flag */
7739 infoPtr->bLButtonDown = TRUE;
7741 lvHitTestInfo.pt.x = pts.x;
7742 lvHitTestInfo.pt.y = pts.y;
7744 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7745 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7746 infoPtr->nEditLabelItem = -1;
7747 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7749 if (infoPtr->dwStyle & LVS_SINGLESEL)
7751 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7752 infoPtr->nEditLabelItem = nItem;
7754 LISTVIEW_SetSelection(infoPtr, nItem);
7758 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7762 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7763 LISTVIEW_SetItemFocus(infoPtr, nItem);
7764 infoPtr->nSelectionMark = nItem;
7770 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7771 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7773 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7774 infoPtr->nSelectionMark = nItem;
7777 else if (wKey & MK_CONTROL)
7781 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7783 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7784 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7785 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7786 infoPtr->nSelectionMark = nItem;
7788 else if (wKey & MK_SHIFT)
7790 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7794 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7795 infoPtr->nEditLabelItem = nItem;
7797 /* set selection (clears other pre-existing selections) */
7798 LISTVIEW_SetSelection(infoPtr, nItem);
7804 /* remove all selections */
7805 LISTVIEW_DeselectAll(infoPtr);
7813 * Processes mouse up messages (left mouse button).
7816 * [I] infoPtr : valid pointer to the listview structure
7817 * [I] wKey : key flag
7818 * [I] pts : mouse coordinate
7823 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7825 LVHITTESTINFO lvHitTestInfo;
7827 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7829 if (!infoPtr->bLButtonDown) return 0;
7831 lvHitTestInfo.pt.x = pts.x;
7832 lvHitTestInfo.pt.y = pts.y;
7834 /* send NM_CLICK notification */
7835 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7836 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7838 /* set left button flag */
7839 infoPtr->bLButtonDown = FALSE;
7841 /* if we clicked on a selected item, edit the label */
7842 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7843 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7850 * Destroys the listview control (called after WM_DESTROY).
7853 * [I] infoPtr : valid pointer to the listview structure
7858 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7862 /* delete all items */
7863 LISTVIEW_DeleteAllItems(infoPtr);
7865 /* destroy data structure */
7866 DPA_Destroy(infoPtr->hdpaItems);
7867 DPA_Destroy(infoPtr->hdpaPosX);
7868 DPA_Destroy(infoPtr->hdpaPosY);
7869 DPA_Destroy(infoPtr->hdpaColumns);
7870 ranges_destroy(infoPtr->selectionRanges);
7872 /* destroy image lists */
7873 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
7875 if (infoPtr->himlNormal)
7876 ImageList_Destroy(infoPtr->himlNormal);
7877 if (infoPtr->himlSmall)
7878 ImageList_Destroy(infoPtr->himlSmall);
7879 if (infoPtr->himlState)
7880 ImageList_Destroy(infoPtr->himlState);
7883 /* destroy font, bkgnd brush */
7885 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7886 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7888 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7890 /* free listview info pointer*/
7898 * Handles notifications from header.
7901 * [I] infoPtr : valid pointer to the listview structure
7902 * [I] nCtrlId : control identifier
7903 * [I] lpnmh : notification information
7908 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
7910 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7912 TRACE("(lpnmh=%p)\n", lpnmh);
7914 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
7916 switch (lpnmh->hdr.code)
7920 case HDN_ITEMCHANGEDW:
7921 case HDN_ITEMCHANGEDA:
7923 COLUMN_INFO *lpColumnInfo;
7926 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
7930 hdi.mask = HDI_WIDTH;
7931 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
7935 cxy = lpnmh->pitem->cxy;
7937 /* determine how much we change since the last know position */
7938 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
7939 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7942 RECT rcCol = lpColumnInfo->rcHeader;
7944 lpColumnInfo->rcHeader.right += dx;
7945 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
7946 if (uView == LVS_REPORT && is_redrawing(infoPtr))
7948 /* this trick works for left aligned columns only */
7949 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
7951 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
7952 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
7954 rcCol.top = infoPtr->rcList.top;
7955 rcCol.bottom = infoPtr->rcList.bottom;
7956 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
7962 case HDN_ITEMCLICKW:
7963 case HDN_ITEMCLICKA:
7965 /* Handle sorting by Header Column */
7968 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7970 nmlv.iSubItem = lpnmh->iItem;
7971 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7981 * Determines the type of structure to use.
7984 * [I] infoPtr : valid pointer to the listview structureof the sender
7985 * [I] hwndFrom : listview window handle
7986 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
7991 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7993 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
7995 if (nCommand != NF_REQUERY) return 0;
7997 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8004 * Paints/Repaints the listview control.
8007 * [I] infoPtr : valid pointer to the listview structure
8008 * [I] hdc : device context handle
8013 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8015 TRACE("(hdc=%p)\n", hdc);
8017 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8019 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8021 infoPtr->bNoItemMetrics = FALSE;
8022 LISTVIEW_UpdateItemSize(infoPtr);
8023 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8024 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8025 LISTVIEW_UpdateScroll(infoPtr);
8028 LISTVIEW_Refresh(infoPtr, hdc);
8033 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8035 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8036 LISTVIEW_Refresh(infoPtr, hdc);
8037 EndPaint(infoPtr->hwndSelf, &ps);
8045 * Processes double click messages (right mouse button).
8048 * [I] infoPtr : valid pointer to the listview structure
8049 * [I] wKey : key flag
8050 * [I] pts : mouse coordinate
8055 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8057 LVHITTESTINFO lvHitTestInfo;
8059 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8061 /* send NM_RELEASEDCAPTURE notification */
8062 notify(infoPtr, NM_RELEASEDCAPTURE);
8064 /* send NM_RDBLCLK notification */
8065 lvHitTestInfo.pt.x = pts.x;
8066 lvHitTestInfo.pt.y = pts.y;
8067 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8068 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8075 * Processes mouse down messages (right mouse button).
8078 * [I] infoPtr : valid pointer to the listview structure
8079 * [I] wKey : key flag
8080 * [I] pts : mouse coordinate
8085 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8087 LVHITTESTINFO lvHitTestInfo;
8090 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8092 /* send NM_RELEASEDCAPTURE notification */
8093 notify(infoPtr, NM_RELEASEDCAPTURE);
8095 /* make sure the listview control window has the focus */
8096 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8098 /* set right button down flag */
8099 infoPtr->bRButtonDown = TRUE;
8101 /* determine the index of the selected item */
8102 lvHitTestInfo.pt.x = pts.x;
8103 lvHitTestInfo.pt.y = pts.y;
8104 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8106 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8108 LISTVIEW_SetItemFocus(infoPtr, nItem);
8109 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8110 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8111 LISTVIEW_SetSelection(infoPtr, nItem);
8115 LISTVIEW_DeselectAll(infoPtr);
8123 * Processes mouse up messages (right mouse button).
8126 * [I] infoPtr : valid pointer to the listview structure
8127 * [I] wKey : key flag
8128 * [I] pts : mouse coordinate
8133 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8135 LVHITTESTINFO lvHitTestInfo;
8138 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8140 if (!infoPtr->bRButtonDown) return 0;
8142 /* set button flag */
8143 infoPtr->bRButtonDown = FALSE;
8145 /* Send NM_RClICK notification */
8146 lvHitTestInfo.pt.x = pts.x;
8147 lvHitTestInfo.pt.y = pts.y;
8148 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8149 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8151 /* Change to screen coordinate for WM_CONTEXTMENU */
8152 pt = lvHitTestInfo.pt;
8153 ClientToScreen(infoPtr->hwndSelf, &pt);
8155 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8156 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8157 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8168 * [I] infoPtr : valid pointer to the listview structure
8169 * [I] hwnd : window handle of window containing the cursor
8170 * [I] nHittest : hit-test code
8171 * [I] wMouseMsg : ideintifier of the mouse message
8174 * TRUE if cursor is set
8177 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8179 LVHITTESTINFO lvHitTestInfo;
8181 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8183 if(!infoPtr->hHotCursor) return FALSE;
8185 GetCursorPos(&lvHitTestInfo.pt);
8186 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8188 SetCursor(infoPtr->hHotCursor);
8198 * [I] infoPtr : valid pointer to the listview structure
8199 * [I] hwndLoseFocus : handle of previously focused window
8204 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8206 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8208 /* if we have the focus already, there's nothing to do */
8209 if (infoPtr->bFocus) return 0;
8211 /* send NM_SETFOCUS notification */
8212 notify(infoPtr, NM_SETFOCUS);
8214 /* set window focus flag */
8215 infoPtr->bFocus = TRUE;
8217 /* put the focus rect back on */
8218 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8220 /* redraw all visible selected items */
8221 LISTVIEW_InvalidateSelectedItems(infoPtr);
8231 * [I] infoPtr : valid pointer to the listview structure
8232 * [I] fRedraw : font handle
8233 * [I] fRedraw : redraw flag
8238 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8240 HFONT oldFont = infoPtr->hFont;
8242 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8244 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8245 if (infoPtr->hFont == oldFont) return 0;
8247 LISTVIEW_SaveTextMetrics(infoPtr);
8249 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8250 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8252 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8259 * Message handling for WM_SETREDRAW.
8260 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8263 * [I] infoPtr : valid pointer to the listview structure
8264 * [I] bRedraw: state of redraw flag
8267 * DefWinProc return value
8269 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8271 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8273 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8274 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8276 infoPtr->bRedraw = bRedraw;
8278 if(!bRedraw) return 0;
8280 if (is_autoarrange(infoPtr))
8281 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8282 LISTVIEW_UpdateScroll(infoPtr);
8284 /* despite what the WM_SETREDRAW docs says, apps expect us
8285 * to invalidate the listview here... stupid! */
8286 LISTVIEW_InvalidateList(infoPtr);
8293 * Resizes the listview control. This function processes WM_SIZE
8294 * messages. At this time, the width and height are not used.
8297 * [I] infoPtr : valid pointer to the listview structure
8298 * [I] Width : new width
8299 * [I] Height : new height
8304 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8306 RECT rcOld = infoPtr->rcList;
8308 TRACE("(width=%d, height=%d)\n", Width, Height);
8310 LISTVIEW_UpdateSize(infoPtr);
8311 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8313 /* do not bother with display related stuff if we're not redrawing */
8314 if (!is_redrawing(infoPtr)) return 0;
8316 if (is_autoarrange(infoPtr))
8317 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8319 LISTVIEW_UpdateScroll(infoPtr);
8321 /* refresh all only for lists whose height changed significantly */
8322 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8323 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8324 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8325 LISTVIEW_InvalidateList(infoPtr);
8332 * Sets the size information.
8335 * [I] infoPtr : valid pointer to the listview structure
8340 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8342 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8344 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8346 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8348 if (uView == LVS_LIST)
8350 /* Apparently the "LIST" style is supposed to have the same
8351 * number of items in a column even if there is no scroll bar.
8352 * Since if a scroll bar already exists then the bottom is already
8353 * reduced, only reduce if the scroll bar does not currently exist.
8354 * The "2" is there to mimic the native control. I think it may be
8355 * related to either padding or edges. (GLA 7/2002)
8357 if (!(infoPtr->dwStyle & WS_HSCROLL))
8358 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8359 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8361 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8366 hl.prc = &infoPtr->rcList;
8368 Header_Layout(infoPtr->hwndHeader, &hl);
8370 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8372 infoPtr->rcList.top = max(wp.cy, 0);
8375 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8380 * Processes WM_STYLECHANGED messages.
8383 * [I] infoPtr : valid pointer to the listview structure
8384 * [I] wStyleType : window style type (normal or extended)
8385 * [I] lpss : window style information
8390 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8391 const STYLESTRUCT *lpss)
8393 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8394 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8396 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8397 wStyleType, lpss->styleOld, lpss->styleNew);
8399 if (wStyleType != GWL_STYLE) return 0;
8401 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8402 /* what if LVS_OWNERDATA changed? */
8403 /* or LVS_SINGLESEL */
8404 /* or LVS_SORT{AS,DES}CENDING */
8406 infoPtr->dwStyle = lpss->styleNew;
8408 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8409 ((lpss->styleNew & WS_HSCROLL) == 0))
8410 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8412 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8413 ((lpss->styleNew & WS_VSCROLL) == 0))
8414 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8416 if (uNewView != uOldView)
8418 SIZE oldIconSize = infoPtr->iconSize;
8421 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8422 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8424 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8425 SetRectEmpty(&infoPtr->rcFocus);
8427 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8428 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8430 if (uNewView == LVS_ICON)
8432 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8434 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8435 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8436 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8439 else if (uNewView == LVS_REPORT)
8444 hl.prc = &infoPtr->rcList;
8446 Header_Layout(infoPtr->hwndHeader, &hl);
8447 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8450 LISTVIEW_UpdateItemSize(infoPtr);
8453 if (uNewView == LVS_REPORT)
8454 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8456 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8457 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8458 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8460 /* update the size of the client area */
8461 LISTVIEW_UpdateSize(infoPtr);
8463 /* add scrollbars if needed */
8464 LISTVIEW_UpdateScroll(infoPtr);
8466 /* invalidate client area + erase background */
8467 LISTVIEW_InvalidateList(infoPtr);
8474 * Window procedure of the listview control.
8477 static LRESULT WINAPI
8478 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8480 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8482 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8484 if (!infoPtr && (uMsg != WM_CREATE))
8485 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8489 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8494 case LVM_APPROXIMATEVIEWRECT:
8495 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8496 LOWORD(lParam), HIWORD(lParam));
8498 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8500 /* case LVM_CANCELEDITLABEL: */
8502 /* case LVM_CREATEDRAGIMAGE: */
8504 case LVM_DELETEALLITEMS:
8505 return LISTVIEW_DeleteAllItems(infoPtr);
8507 case LVM_DELETECOLUMN:
8508 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8510 case LVM_DELETEITEM:
8511 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8513 case LVM_EDITLABELW:
8514 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8516 case LVM_EDITLABELA:
8517 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8519 /* case LVM_ENABLEGROUPVIEW: */
8521 case LVM_ENSUREVISIBLE:
8522 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8525 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8528 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8530 case LVM_GETBKCOLOR:
8531 return infoPtr->clrBk;
8533 /* case LVM_GETBKIMAGE: */
8535 case LVM_GETCALLBACKMASK:
8536 return infoPtr->uCallbackMask;
8538 case LVM_GETCOLUMNA:
8539 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8541 case LVM_GETCOLUMNW:
8542 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8544 case LVM_GETCOLUMNORDERARRAY:
8545 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8547 case LVM_GETCOLUMNWIDTH:
8548 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8550 case LVM_GETCOUNTPERPAGE:
8551 return LISTVIEW_GetCountPerPage(infoPtr);
8553 case LVM_GETEDITCONTROL:
8554 return (LRESULT)infoPtr->hwndEdit;
8556 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8557 return infoPtr->dwLvExStyle;
8559 /* case LVM_GETGROUPINFO: */
8561 /* case LVM_GETGROUPMETRICS: */
8564 return (LRESULT)infoPtr->hwndHeader;
8566 case LVM_GETHOTCURSOR:
8567 return (LRESULT)infoPtr->hHotCursor;
8569 case LVM_GETHOTITEM:
8570 return infoPtr->nHotItem;
8572 case LVM_GETHOVERTIME:
8573 return infoPtr->dwHoverTime;
8575 case LVM_GETIMAGELIST:
8576 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8578 /* case LVM_GETINSERTMARK: */
8580 /* case LVM_GETINSERTMARKCOLOR: */
8582 /* case LVM_GETINSERTMARKRECT: */
8584 case LVM_GETISEARCHSTRINGA:
8585 case LVM_GETISEARCHSTRINGW:
8586 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8590 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8593 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8595 case LVM_GETITEMCOUNT:
8596 return infoPtr->nItemCount;
8598 case LVM_GETITEMPOSITION:
8599 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8601 case LVM_GETITEMRECT:
8602 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8604 case LVM_GETITEMSPACING:
8605 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8607 case LVM_GETITEMSTATE:
8608 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8610 case LVM_GETITEMTEXTA:
8611 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8613 case LVM_GETITEMTEXTW:
8614 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8616 case LVM_GETNEXTITEM:
8617 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8619 case LVM_GETNUMBEROFWORKAREAS:
8620 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8624 if (!lParam) return FALSE;
8625 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8628 /* case LVM_GETOUTLINECOLOR: */
8630 /* case LVM_GETSELECTEDCOLUMN: */
8632 case LVM_GETSELECTEDCOUNT:
8633 return LISTVIEW_GetSelectedCount(infoPtr);
8635 case LVM_GETSELECTIONMARK:
8636 return infoPtr->nSelectionMark;
8638 case LVM_GETSTRINGWIDTHA:
8639 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8641 case LVM_GETSTRINGWIDTHW:
8642 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8644 case LVM_GETSUBITEMRECT:
8645 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8647 case LVM_GETTEXTBKCOLOR:
8648 return infoPtr->clrTextBk;
8650 case LVM_GETTEXTCOLOR:
8651 return infoPtr->clrText;
8653 /* case LVM_GETTILEINFO: */
8655 /* case LVM_GETTILEVIEWINFO: */
8657 case LVM_GETTOOLTIPS:
8658 return (LRESULT)infoPtr->hwndToolTip;
8660 case LVM_GETTOPINDEX:
8661 return LISTVIEW_GetTopIndex(infoPtr);
8663 /*case LVM_GETUNICODEFORMAT:
8664 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8667 /* case LVM_GETVIEW: */
8669 case LVM_GETVIEWRECT:
8670 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8672 case LVM_GETWORKAREAS:
8673 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8676 /* case LVM_HASGROUP: */
8679 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8681 case LVM_INSERTCOLUMNA:
8682 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8684 case LVM_INSERTCOLUMNW:
8685 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8687 /* case LVM_INSERTGROUP: */
8689 /* case LVM_INSERTGROUPSORTED: */
8691 case LVM_INSERTITEMA:
8692 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8694 case LVM_INSERTITEMW:
8695 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8697 /* case LVM_INSERTMARKHITTEST: */
8699 /* case LVM_ISGROUPVIEWENABLED: */
8701 /* case LVM_MAPIDTOINDEX: */
8703 /* case LVM_MAPINDEXTOID: */
8705 /* case LVM_MOVEGROUP: */
8707 /* case LVM_MOVEITEMTOGROUP: */
8709 case LVM_REDRAWITEMS:
8710 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8712 /* case LVM_REMOVEALLGROUPS: */
8714 /* case LVM_REMOVEGROUP: */
8717 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8719 case LVM_SETBKCOLOR:
8720 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8722 /* case LVM_SETBKIMAGE: */
8724 case LVM_SETCALLBACKMASK:
8725 infoPtr->uCallbackMask = (UINT)wParam;
8728 case LVM_SETCOLUMNA:
8729 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8731 case LVM_SETCOLUMNW:
8732 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8734 case LVM_SETCOLUMNORDERARRAY:
8735 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8737 case LVM_SETCOLUMNWIDTH:
8738 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
8740 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8741 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8743 /* case LVM_SETGROUPINFO: */
8745 /* case LVM_SETGROUPMETRICS: */
8747 case LVM_SETHOTCURSOR:
8748 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8750 case LVM_SETHOTITEM:
8751 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8753 case LVM_SETHOVERTIME:
8754 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8756 case LVM_SETICONSPACING:
8757 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
8759 case LVM_SETIMAGELIST:
8760 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8762 /* case LVM_SETINFOTIP: */
8764 /* case LVM_SETINSERTMARK: */
8766 /* case LVM_SETINSERTMARKCOLOR: */
8769 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8772 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8774 case LVM_SETITEMCOUNT:
8775 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8777 case LVM_SETITEMPOSITION:
8780 pt.x = (short)LOWORD(lParam);
8781 pt.y = (short)HIWORD(lParam);
8782 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8785 case LVM_SETITEMPOSITION32:
8786 if (lParam == 0) return FALSE;
8787 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8789 case LVM_SETITEMSTATE:
8790 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8792 case LVM_SETITEMTEXTA:
8793 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8795 case LVM_SETITEMTEXTW:
8796 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8798 /* case LVM_SETOUTLINECOLOR: */
8800 /* case LVM_SETSELECTEDCOLUMN: */
8802 case LVM_SETSELECTIONMARK:
8803 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8805 case LVM_SETTEXTBKCOLOR:
8806 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8808 case LVM_SETTEXTCOLOR:
8809 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8811 /* case LVM_SETTILEINFO: */
8813 /* case LVM_SETTILEVIEWINFO: */
8815 /* case LVM_SETTILEWIDTH: */
8817 case LVM_SETTOOLTIPS:
8818 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
8820 /* case LVM_SETUNICODEFORMAT: */
8822 /* case LVM_SETVIEW: */
8824 /* case LVM_SETWORKAREAS: */
8826 /* case LVM_SORTGROUPS: */
8829 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8831 /* LVM_SORTITEMSEX: */
8833 case LVM_SUBITEMHITTEST:
8834 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8837 return LISTVIEW_Update(infoPtr, (INT)wParam);
8840 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8843 return LISTVIEW_Command(infoPtr, wParam, lParam);
8846 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8849 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8852 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8855 return (LRESULT)infoPtr->hFont;
8858 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8861 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8864 return LISTVIEW_KillFocus(infoPtr);
8866 case WM_LBUTTONDBLCLK:
8867 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8869 case WM_LBUTTONDOWN:
8870 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8873 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8876 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8879 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8882 return LISTVIEW_NCDestroy(infoPtr);
8885 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
8886 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
8889 case WM_NOTIFYFORMAT:
8890 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8893 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8895 case WM_RBUTTONDBLCLK:
8896 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8898 case WM_RBUTTONDOWN:
8899 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8902 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8905 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8910 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8913 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8916 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8919 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
8921 case WM_STYLECHANGED:
8922 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8924 case WM_SYSCOLORCHANGE:
8925 COMCTL32_RefreshSysColors();
8928 /* case WM_TIMER: */
8931 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8934 if (wParam & (MK_SHIFT | MK_CONTROL))
8935 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8936 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8938 case WM_WINDOWPOSCHANGED:
8939 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
8941 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8942 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8943 LISTVIEW_UpdateSize(infoPtr);
8944 LISTVIEW_UpdateScroll(infoPtr);
8946 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8948 /* case WM_WININICHANGE: */
8951 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8952 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8955 /* call default window procedure */
8956 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8964 * Registers the window class.
8972 void LISTVIEW_Register(void)
8976 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8977 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8978 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8979 wndClass.cbClsExtra = 0;
8980 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8981 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
8982 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8983 wndClass.lpszClassName = WC_LISTVIEWW;
8984 RegisterClassW(&wndClass);
8989 * Unregisters the window class.
8997 void LISTVIEW_Unregister(void)
8999 UnregisterClassW(WC_LISTVIEWW, NULL);
9004 * Handle any WM_COMMAND messages
9007 * [I] infoPtr : valid pointer to the listview structure
9008 * [I] wParam : the first message parameter
9009 * [I] lParam : the second message parameter
9014 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9016 switch (HIWORD(wParam))
9021 * Adjust the edit window size
9024 HDC hdc = GetDC(infoPtr->hwndEdit);
9025 HFONT hFont, hOldFont = 0;
9030 if (!infoPtr->hwndEdit || !hdc) return 0;
9031 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9032 GetWindowRect(infoPtr->hwndEdit, &rect);
9034 /* Select font to get the right dimension of the string */
9035 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9038 hOldFont = SelectObject(hdc, hFont);
9041 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9043 TEXTMETRICW textMetric;
9045 /* Add Extra spacing for the next character */
9046 GetTextMetricsW(hdc, &textMetric);
9047 sz.cx += (textMetric.tmMaxCharWidth * 2);
9055 rect.bottom - rect.top,
9056 SWP_DRAWFRAME|SWP_NOMOVE);
9059 SelectObject(hdc, hOldFont);
9061 ReleaseDC(infoPtr->hwndSelf, hdc);
9067 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
9076 * Subclassed edit control windproc function
9079 * [I] hwnd : the edit window handle
9080 * [I] uMsg : the message that is to be processed
9081 * [I] wParam : first message parameter
9082 * [I] lParam : second message parameter
9083 * [I] isW : TRUE if input is Unicode
9088 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9090 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9091 BOOL cancel = FALSE;
9093 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9094 hwnd, uMsg, wParam, lParam, isW);
9099 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9106 WNDPROC editProc = infoPtr->EditWndProc;
9107 infoPtr->EditWndProc = 0;
9108 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9109 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9113 if (VK_ESCAPE == (INT)wParam)
9118 else if (VK_RETURN == (INT)wParam)
9122 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9126 if (infoPtr->hwndEdit)
9128 LPWSTR buffer = NULL;
9130 infoPtr->hwndEdit = 0;
9133 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9137 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9139 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9140 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9144 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9146 if (buffer) Free(buffer);
9150 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9156 * Subclassed edit control Unicode windproc function
9159 * [I] hwnd : the edit window handle
9160 * [I] uMsg : the message that is to be processed
9161 * [I] wParam : first message parameter
9162 * [I] lParam : second message parameter
9166 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9168 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9173 * Subclassed edit control ANSI windproc function
9176 * [I] hwnd : the edit window handle
9177 * [I] uMsg : the message that is to be processed
9178 * [I] wParam : first message parameter
9179 * [I] lParam : second message parameter
9183 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9185 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9190 * Creates a subclassed edit cotrol
9193 * [I] infoPtr : valid pointer to the listview structure
9194 * [I] text : initial text for the edit
9195 * [I] style : the window style
9196 * [I] isW : TRUE if input is Unicode
9200 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9201 INT x, INT y, INT width, INT height, BOOL isW)
9203 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9208 TEXTMETRICW textMetric;
9209 HINSTANCE hinst = (HINSTANCE)GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9211 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9213 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9214 hdc = GetDC(infoPtr->hwndSelf);
9216 /* Select the font to get appropriate metric dimensions */
9217 if(infoPtr->hFont != 0)
9218 hOldFont = SelectObject(hdc, infoPtr->hFont);
9220 /*Get String Length in pixels */
9221 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9223 /*Add Extra spacing for the next character */
9224 GetTextMetricsW(hdc, &textMetric);
9225 sz.cx += (textMetric.tmMaxCharWidth * 2);
9227 if(infoPtr->hFont != 0)
9228 SelectObject(hdc, hOldFont);
9230 ReleaseDC(infoPtr->hwndSelf, hdc);
9232 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9234 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9236 if (!hedit) return 0;
9238 infoPtr->EditWndProc = (WNDPROC)
9239 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9240 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9242 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);