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 believe 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, we would 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
83 * -- LVS_EX_HEADERDRAGDROP
86 * -- LVS_EX_MULTIWORKAREAS
87 * -- LVS_EX_ONECLICKACTIVATE
89 * -- LVS_EX_SIMPLESELECT
90 * -- LVS_EX_TRACKSELECT
91 * -- LVS_EX_TWOCLICKACTIVATE
92 * -- LVS_EX_UNDERLINECOLD
93 * -- LVS_EX_UNDERLINEHOT
96 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
101 * -- LVN_ODSTATECHANGED
106 * -- LVM_CANCELEDITLABEL
107 * -- LVM_ENABLEGROUPVIEW
108 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
109 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
110 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
111 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
112 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
113 * -- LVM_GETINSERTMARKRECT
114 * -- LVM_GETNUMBEROFWORKAREAS
115 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
116 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
117 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
118 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
119 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
120 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
121 * -- LVM_GETVIEW, LVM_SETVIEW
122 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
123 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
124 * -- LVM_INSERTGROUPSORTED
125 * -- LVM_INSERTMARKHITTEST
126 * -- LVM_ISGROUPVIEWENABLED
127 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
129 * -- LVM_MOVEITEMTOGROUP
131 * -- LVM_SETTILEWIDTH
135 * Known differences in message stream from native control (not known if
136 * these differences cause problems):
137 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
138 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
139 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
140 * processing for "USEDOUBLECLICKTIME".
144 #include "wine/port.h"
159 #include "commctrl.h"
160 #include "comctl32.h"
162 #include "wine/debug.h"
163 #include "wine/unicode.h"
165 WINE_DEFAULT_DEBUG_CHANNEL(listview);
167 /* make sure you set this to 0 for production use! */
168 #define DEBUG_RANGES 1
170 typedef struct tagCOLUMN_INFO
172 RECT rcHeader; /* tracks the header's rectangle */
173 int fmt; /* same as LVCOLUMN.fmt */
176 typedef struct tagITEMHDR
180 } ITEMHDR, *LPITEMHDR;
182 typedef struct tagSUBITEM_INFO
188 typedef struct tagITEM_INFO
196 typedef struct tagRANGE
202 typedef struct tagRANGES
207 typedef struct tagITERATOR
216 typedef struct tagLISTVIEW_INFO
223 COLORREF clrTextBkDefault;
224 HIMAGELIST himlNormal;
225 HIMAGELIST himlSmall;
226 HIMAGELIST himlState;
229 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
232 RANGES selectionRanges;
237 RECT rcList; /* This rectangle is really the window
238 * client rectangle possibly reduced by the
239 * horizontal scroll bar and/or header - see
240 * LISTVIEW_UpdateSize. This rectangle offset
241 * by the LISTVIEW_GetOrigin value is in
242 * client coordinates */
251 INT ntmHeight; /* Some cached metrics of the font used */
252 INT ntmAveCharWidth; /* by the listview to draw items */
253 BOOL bRedraw; /* Turns on/off repaints & invalidations */
254 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
256 BOOL bDoChangeNotify; /* send change notification messages? */
259 DWORD dwStyle; /* the cached window GWL_STYLE */
260 DWORD dwLvExStyle; /* extended listview style */
261 INT nItemCount; /* the number of items in the list */
262 HDPA hdpaItems; /* array ITEM_INFO pointers */
263 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
264 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
265 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
266 POINT currIconPos; /* this is the position next icon will be placed */
267 PFNLVCOMPARE pfnCompare;
275 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
277 DWORD lastKeyPressTimestamp;
279 INT nSearchParamLength;
280 WCHAR szSearchParam[ MAX_PATH ];
287 /* How many we debug buffer to allocate */
288 #define DEBUG_BUFFERS 20
289 /* The size of a single debug bbuffer */
290 #define DEBUG_BUFFER_SIZE 256
292 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
293 #define SB_INTERNAL -1
295 /* maximum size of a label */
296 #define DISP_TEXT_SIZE 512
298 /* padding for items in list and small icon display modes */
299 #define WIDTH_PADDING 12
301 /* padding for items in list, report and small icon display modes */
302 #define HEIGHT_PADDING 1
304 /* offset of items in report display mode */
305 #define REPORT_MARGINX 2
307 /* padding for icon in large icon display mode
308 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
309 * that HITTEST will see.
310 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
311 * ICON_TOP_PADDING - sum of the two above.
312 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
313 * LABEL_HOR_PADDING - between text and sides of box
314 * LABEL_VERT_PADDING - between bottom of text and end of box
316 * ICON_LR_PADDING - additional width above icon size.
317 * ICON_LR_HALF - half of the above value
319 #define ICON_TOP_PADDING_NOTHITABLE 2
320 #define ICON_TOP_PADDING_HITABLE 2
321 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
322 #define ICON_BOTTOM_PADDING 4
323 #define LABEL_HOR_PADDING 5
324 #define LABEL_VERT_PADDING 7
325 #define ICON_LR_PADDING 16
326 #define ICON_LR_HALF (ICON_LR_PADDING/2)
328 /* default label width for items in list and small icon display modes */
329 #define DEFAULT_LABEL_WIDTH 40
331 /* default column width for items in list display mode */
332 #define DEFAULT_COLUMN_WIDTH 128
334 /* Size of "line" scroll for V & H scrolls */
335 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
337 /* Padding betwen image and label */
338 #define IMAGE_PADDING 2
340 /* Padding behind the label */
341 #define TRAILING_LABEL_PADDING 12
342 #define TRAILING_HEADER_PADDING 11
344 /* Border for the icon caption */
345 #define CAPTION_BORDER 2
347 /* Standard DrawText flags */
348 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
349 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
350 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
352 /* The time in milliseconds to reset the search in the list */
353 #define KEY_DELAY 450
355 /* Dump the LISTVIEW_INFO structure to the debug channel */
356 #define LISTVIEW_DUMP(iP) do { \
357 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
358 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
359 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
360 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
361 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
362 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
363 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
364 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
365 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
366 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
370 * forward declarations
372 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
373 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
374 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
375 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
376 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
377 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
378 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
379 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
380 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
381 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
382 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
383 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
384 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
385 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
386 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
387 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
388 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
389 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
390 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
391 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
392 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
393 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
394 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
395 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
396 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
397 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
399 /******** Text handling functions *************************************/
401 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
402 * text string. The string may be ANSI or Unicode, in which case
403 * the boolean isW tells us the type of the string.
405 * The name of the function tell what type of strings it expects:
406 * W: Unicode, T: ANSI/Unicode - function of isW
409 static inline BOOL is_textW(LPCWSTR text)
411 return text != NULL && text != LPSTR_TEXTCALLBACKW;
414 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
416 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
417 return is_textW(text);
420 static inline int textlenT(LPCWSTR text, BOOL isW)
422 return !is_textT(text, isW) ? 0 :
423 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
426 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
429 if (isSrcW) lstrcpynW(dest, src, max);
430 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
432 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
433 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
436 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
438 LPWSTR wstr = (LPWSTR)text;
440 if (!isW && is_textT(text, isW))
442 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
443 wstr = Alloc(len * sizeof(WCHAR));
444 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
446 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
450 static inline void textfreeT(LPWSTR wstr, BOOL isW)
452 if (!isW && is_textT(wstr, isW)) Free (wstr);
456 * dest is a pointer to a Unicode string
457 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
459 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
463 if (src == LPSTR_TEXTCALLBACKW)
465 if (is_textW(*dest)) Free(*dest);
466 *dest = LPSTR_TEXTCALLBACKW;
470 LPWSTR pszText = textdupTtoW(src, isW);
471 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
472 bResult = Str_SetPtrW(dest, pszText);
473 textfreeT(pszText, isW);
479 * compares a Unicode to a Unicode/ANSI text string
481 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
483 if (!aw) return bt ? -1 : 0;
484 if (!bt) return aw ? 1 : 0;
485 if (aw == LPSTR_TEXTCALLBACKW)
486 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
487 if (bt != LPSTR_TEXTCALLBACKW)
489 LPWSTR bw = textdupTtoW(bt, isW);
490 int r = bw ? lstrcmpW(aw, bw) : 1;
498 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
502 n = min(min(n, strlenW(s1)), strlenW(s2));
503 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
504 return res ? res - sizeof(WCHAR) : res;
507 /******** Debugging functions *****************************************/
509 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
511 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
512 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
515 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
517 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
518 n = min(textlenT(text, isW), n);
519 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
522 static char* debug_getbuf()
524 static int index = 0;
525 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
526 return buffers[index++ % DEBUG_BUFFERS];
529 static inline const char* debugrange(const RANGE *lprng)
533 char* buf = debug_getbuf();
534 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
536 } else return "(null)";
539 static inline const char* debugpoint(const POINT *lppt)
543 char* buf = debug_getbuf();
544 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
546 } else return "(null)";
549 static inline const char* debugrect(const RECT *rect)
553 char* buf = debug_getbuf();
554 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
555 rect->left, rect->top, rect->right, rect->bottom);
557 } else return "(null)";
560 static const char * debugscrollinfo(const SCROLLINFO *pScrollInfo)
562 char* buf = debug_getbuf(), *text = buf;
563 int len, size = DEBUG_BUFFER_SIZE;
565 if (pScrollInfo == NULL) return "(null)";
566 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
567 if (len == -1) goto end; buf += len; size -= len;
568 if (pScrollInfo->fMask & SIF_RANGE)
569 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
571 if (len == -1) goto end; buf += len; size -= len;
572 if (pScrollInfo->fMask & SIF_PAGE)
573 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
575 if (len == -1) goto end; buf += len; size -= len;
576 if (pScrollInfo->fMask & SIF_POS)
577 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
579 if (len == -1) goto end; buf += len; size -= len;
580 if (pScrollInfo->fMask & SIF_TRACKPOS)
581 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
583 if (len == -1) goto end; buf += len; size -= len;
586 buf = text + strlen(text);
588 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
592 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
596 char* buf = debug_getbuf();
597 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
598 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
599 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
600 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
602 } else return "(null)";
605 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
607 char* buf = debug_getbuf(), *text = buf;
608 int len, size = DEBUG_BUFFER_SIZE;
610 if (lpLVItem == NULL) return "(null)";
611 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
612 if (len == -1) goto end; buf += len; size -= len;
613 if (lpLVItem->mask & LVIF_STATE)
614 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
616 if (len == -1) goto end; buf += len; size -= len;
617 if (lpLVItem->mask & LVIF_TEXT)
618 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
620 if (len == -1) goto end; buf += len; size -= len;
621 if (lpLVItem->mask & LVIF_IMAGE)
622 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
624 if (len == -1) goto end; buf += len; size -= len;
625 if (lpLVItem->mask & LVIF_PARAM)
626 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
628 if (len == -1) goto end; buf += len; size -= len;
629 if (lpLVItem->mask & LVIF_INDENT)
630 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
632 if (len == -1) goto end; buf += len; size -= len;
635 buf = text + strlen(text);
637 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
641 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
643 char* buf = debug_getbuf(), *text = buf;
644 int len, size = DEBUG_BUFFER_SIZE;
646 if (lpColumn == NULL) return "(null)";
647 len = snprintf(buf, size, "{");
648 if (len == -1) goto end; buf += len; size -= len;
649 if (lpColumn->mask & LVCF_SUBITEM)
650 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
652 if (len == -1) goto end; buf += len; size -= len;
653 if (lpColumn->mask & LVCF_FMT)
654 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
656 if (len == -1) goto end; buf += len; size -= len;
657 if (lpColumn->mask & LVCF_WIDTH)
658 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
660 if (len == -1) goto end; buf += len; size -= len;
661 if (lpColumn->mask & LVCF_TEXT)
662 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
664 if (len == -1) goto end; buf += len; size -= len;
665 if (lpColumn->mask & LVCF_IMAGE)
666 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
668 if (len == -1) goto end; buf += len; size -= len;
669 if (lpColumn->mask & LVCF_ORDER)
670 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
672 if (len == -1) goto end; buf += len; size -= len;
675 buf = text + strlen(text);
677 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
681 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
685 char* buf = debug_getbuf();
686 snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
687 debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
689 } else return "(null)";
692 /* Return the corresponding text for a given scroll value */
693 static inline LPCSTR debugscrollcode(int nScrollCode)
697 case SB_LINELEFT: return "SB_LINELEFT";
698 case SB_LINERIGHT: return "SB_LINERIGHT";
699 case SB_PAGELEFT: return "SB_PAGELEFT";
700 case SB_PAGERIGHT: return "SB_PAGERIGHT";
701 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
702 case SB_THUMBTRACK: return "SB_THUMBTRACK";
703 case SB_ENDSCROLL: return "SB_ENDSCROLL";
704 case SB_INTERNAL: return "SB_INTERNAL";
705 default: return "unknown";
710 /******** Notification functions i************************************/
712 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
716 TRACE("(code=%d)\n", code);
718 pnmh->hwndFrom = infoPtr->hwndSelf;
719 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
721 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
722 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
724 TRACE(" <= %ld\n", result);
729 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
732 return notify_hdr(infoPtr, code, &nmh);
735 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
746 item.mask = LVIF_PARAM|LVIF_STATE;
747 item.iItem = htInfo->iItem;
749 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
750 nmia.lParam = item.lParam;
751 nmia.uOldState = item.state;
752 nmia.uNewState = item.state | LVIS_ACTIVATING;
753 nmia.uChanged = LVIF_STATE;
756 nmia.iItem = htInfo->iItem;
757 nmia.iSubItem = htInfo->iSubItem;
758 nmia.ptAction = htInfo->pt;
760 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
761 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
762 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
764 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
767 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
769 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
770 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
773 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
778 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
779 ZeroMemory(&nmlv, sizeof(nmlv));
780 nmlv.iItem = lvht->iItem;
781 nmlv.iSubItem = lvht->iSubItem;
782 nmlv.ptAction = lvht->pt;
783 item.mask = LVIF_PARAM;
784 item.iItem = lvht->iItem;
786 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
787 return notify_listview(infoPtr, code, &nmlv);
790 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
795 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
797 item.mask = LVIF_PARAM;
800 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
801 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
804 static int get_ansi_notification(INT unicodeNotificationCode)
806 switch (unicodeNotificationCode)
808 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
809 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
810 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
811 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
812 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
813 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
815 ERR("unknown notification %x\n", unicodeNotificationCode);
821 Send notification. depends on dispinfoW having same
822 structure as dispinfoA.
823 infoPtr : listview struct
824 notificationCode : *Unicode* notification code
825 pdi : dispinfo structure (can be unicode or ansi)
826 isW : TRUE if dispinfo is Unicode
828 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
830 BOOL bResult = FALSE;
831 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
832 INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
833 LPWSTR pszTempBuf = NULL, savPszText = NULL;
835 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
837 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
838 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
841 if (convertToAnsi || convertToUnicode)
843 if (notificationCode != LVN_GETDISPINFOW)
845 cchTempBufMax = convertToUnicode ?
846 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
847 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
851 cchTempBufMax = pdi->item.cchTextMax;
852 *pdi->item.pszText = 0; /* make sure we don't process garbage */
855 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
856 if (!pszTempBuf) return FALSE;
858 if (convertToUnicode)
859 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
860 pszTempBuf, cchTempBufMax);
862 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
863 cchTempBufMax, NULL, NULL);
865 savCchTextMax = pdi->item.cchTextMax;
866 savPszText = pdi->item.pszText;
867 pdi->item.pszText = pszTempBuf;
868 pdi->item.cchTextMax = cchTempBufMax;
871 if (infoPtr->notifyFormat == NFR_ANSI)
872 realNotifCode = get_ansi_notification(notificationCode);
874 realNotifCode = notificationCode;
875 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
876 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
878 if (convertToUnicode || convertToAnsi)
880 if (convertToUnicode) /* note : pointer can be changed by app ! */
881 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
882 savCchTextMax, NULL, NULL);
884 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
885 savPszText, savCchTextMax);
886 pdi->item.pszText = savPszText; /* restores our buffer */
887 pdi->item.cchTextMax = savCchTextMax;
893 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
894 const RECT *rcBounds, const LVITEMW *lplvItem)
896 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
897 lpnmlvcd->nmcd.hdc = hdc;
898 lpnmlvcd->nmcd.rc = *rcBounds;
899 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
900 lpnmlvcd->clrText = infoPtr->clrText;
901 if (!lplvItem) return;
902 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
903 lpnmlvcd->iSubItem = lplvItem->iSubItem;
904 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
905 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
906 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
907 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
910 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
912 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
915 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
916 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
917 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
918 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
919 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
920 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
924 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
926 /* apprently, for selected items, we have to override the returned values */
927 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
931 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
932 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
934 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
936 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
937 lpnmlvcd->clrText = comctl32_color.clrBtnText;
941 /* Set the text attributes */
942 if (lpnmlvcd->clrTextBk != CLR_NONE)
944 SetBkMode(hdc, OPAQUE);
945 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
946 SetBkColor(hdc, infoPtr->clrTextBkDefault);
948 SetBkColor(hdc,lpnmlvcd->clrTextBk);
951 SetBkMode(hdc, TRANSPARENT);
952 SetTextColor(hdc, lpnmlvcd->clrText);
955 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
957 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
960 /******** Item iterator functions **********************************/
962 static RANGES ranges_create(int count);
963 static void ranges_destroy(RANGES ranges);
964 static BOOL ranges_add(RANGES ranges, RANGE range);
965 static BOOL ranges_del(RANGES ranges, RANGE range);
966 static void ranges_dump(RANGES ranges);
968 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
970 RANGE range = { nItem, nItem + 1 };
972 return ranges_add(ranges, range);
975 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
977 RANGE range = { nItem, nItem + 1 };
979 return ranges_del(ranges, range);
983 * ITERATOR DOCUMENTATION
985 * The iterator functions allow for easy, and convenient iteration
986 * over items of iterest in the list. Typically, you create a
987 * iterator, use it, and destroy it, as such:
990 * iterator_xxxitems(&i, ...);
991 * while (iterator_{prev,next}(&i)
993 * //code which uses i.nItem
995 * iterator_destroy(&i);
997 * where xxx is either: framed, or visible.
998 * Note that it is important that the code destroys the iterator
999 * after it's done with it, as the creation of the iterator may
1000 * allocate memory, which thus needs to be freed.
1002 * You can iterate both forwards, and backwards through the list,
1003 * by using iterator_next or iterator_prev respectively.
1005 * Lower numbered items are draw on top of higher number items in
1006 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1007 * items may overlap). So, to test items, you should use
1009 * which lists the items top to bottom (in Z-order).
1010 * For drawing items, you should use
1012 * which lists the items bottom to top (in Z-order).
1013 * If you keep iterating over the items after the end-of-items
1014 * marker (-1) is returned, the iterator will start from the
1015 * beginning. Typically, you don't need to test for -1,
1016 * because iterator_{next,prev} will return TRUE if more items
1017 * are to be iterated over, or FALSE otherwise.
1019 * Note: the iterator is defined to be bidirectional. That is,
1020 * any number of prev followed by any number of next, or
1021 * five versa, should leave the iterator at the same item:
1022 * prev * n, next * n = next * n, prev * n
1024 * The iterator has a notion of an out-of-order, special item,
1025 * which sits at the start of the list. This is used in
1026 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1027 * which needs to be first, as it may overlap other items.
1029 * The code is a bit messy because we have:
1030 * - a special item to deal with
1031 * - simple range, or composite range
1033 * If you find bugs, or want to add features, please make sure you
1034 * always check/modify *both* iterator_prev, and iterator_next.
1038 * This function iterates through the items in increasing order,
1039 * but prefixed by the special item, then -1. That is:
1040 * special, 1, 2, 3, ..., n, -1.
1041 * Each item is listed only once.
1043 static inline BOOL iterator_next(ITERATOR* i)
1047 i->nItem = i->nSpecial;
1048 if (i->nItem != -1) return TRUE;
1050 if (i->nItem == i->nSpecial)
1052 if (i->ranges) i->index = 0;
1058 if (i->nItem == i->nSpecial) i->nItem++;
1059 if (i->nItem < i->range.upper) return TRUE;
1064 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1065 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1068 else if (i->nItem >= i->range.upper) goto end;
1070 i->nItem = i->range.lower;
1071 if (i->nItem >= 0) goto testitem;
1078 * This function iterates through the items in decreasing order,
1079 * followed by the special item, then -1. That is:
1080 * n, n-1, ..., 3, 2, 1, special, -1.
1081 * Each item is listed only once.
1083 static inline BOOL iterator_prev(ITERATOR* i)
1090 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1093 if (i->nItem == i->nSpecial)
1101 if (i->nItem == i->nSpecial) i->nItem--;
1102 if (i->nItem >= i->range.lower) return TRUE;
1108 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1111 else if (!start && i->nItem < i->range.lower) goto end;
1113 i->nItem = i->range.upper;
1114 if (i->nItem > 0) goto testitem;
1116 return (i->nItem = i->nSpecial) != -1;
1119 static RANGE iterator_range(ITERATOR* i)
1123 if (!i->ranges) return i->range;
1125 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1127 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1128 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1130 else range.lower = range.upper = 0;
1136 * Releases resources associated with this ierator.
1138 static inline void iterator_destroy(ITERATOR* i)
1140 ranges_destroy(i->ranges);
1144 * Create an empty iterator.
1146 static inline BOOL iterator_empty(ITERATOR* i)
1148 ZeroMemory(i, sizeof(*i));
1149 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1154 * Create an iterator over a range.
1156 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1164 * Create an iterator over a bunch of ranges.
1165 * Please note that the iterator will take ownership of the ranges,
1166 * and will free them upon destruction.
1168 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1176 * Creates an iterator over the items which intersect lprc.
1178 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1180 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1181 RECT frame = *lprc, rcItem, rcTemp;
1184 /* in case we fail, we want to return an empty iterator */
1185 if (!iterator_empty(i)) return FALSE;
1187 LISTVIEW_GetOrigin(infoPtr, &Origin);
1189 TRACE("(lprc=%s)\n", debugrect(lprc));
1190 OffsetRect(&frame, -Origin.x, -Origin.y);
1192 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1196 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1198 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1199 if (IntersectRect(&rcTemp, &rcItem, lprc))
1200 i->nSpecial = infoPtr->nFocusedItem;
1202 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1203 /* to do better here, we need to have PosX, and PosY sorted */
1204 TRACE("building icon ranges:\n");
1205 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1207 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1208 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1209 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1210 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1211 if (IntersectRect(&rcTemp, &rcItem, &frame))
1212 ranges_additem(i->ranges, nItem);
1216 else if (uView == LVS_REPORT)
1220 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1221 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1223 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1224 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1225 if (range.upper <= range.lower) return TRUE;
1226 if (!iterator_rangeitems(i, range)) return FALSE;
1227 TRACE(" report=%s\n", debugrange(&i->range));
1231 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1232 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1233 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1234 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1235 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1236 INT lower = nFirstCol * nPerCol + nFirstRow;
1240 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1241 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1243 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1245 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1246 TRACE("building list ranges:\n");
1247 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1249 item_range.lower = nCol * nPerCol + nFirstRow;
1250 if(item_range.lower >= infoPtr->nItemCount) break;
1251 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1252 TRACE(" list=%s\n", debugrange(&item_range));
1253 ranges_add(i->ranges, item_range);
1261 * Creates an iterator over the items which intersect the visible region of hdc.
1263 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1265 POINT Origin, Position;
1266 RECT rcItem, rcClip;
1269 rgntype = GetClipBox(hdc, &rcClip);
1270 if (rgntype == NULLREGION) return iterator_empty(i);
1271 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1272 if (rgntype == SIMPLEREGION) return TRUE;
1274 /* first deal with the special item */
1275 if (i->nSpecial != -1)
1277 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1278 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1281 /* if we can't deal with the region, we'll just go with the simple range */
1282 LISTVIEW_GetOrigin(infoPtr, &Origin);
1283 TRACE("building visible range:\n");
1284 if (!i->ranges && i->range.lower < i->range.upper)
1286 if (!(i->ranges = ranges_create(50))) return TRUE;
1287 if (!ranges_add(i->ranges, i->range))
1289 ranges_destroy(i->ranges);
1295 /* now delete the invisible items from the list */
1296 while(iterator_next(i))
1298 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1299 rcItem.left = Position.x + Origin.x;
1300 rcItem.top = Position.y + Origin.y;
1301 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1302 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1303 if (!RectVisible(hdc, &rcItem))
1304 ranges_delitem(i->ranges, i->nItem);
1306 /* the iterator should restart on the next iterator_next */
1312 /******** Misc helper functions ************************************/
1314 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1315 WPARAM wParam, LPARAM lParam, BOOL isW)
1317 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1318 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1321 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1323 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1325 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1326 (uView == LVS_ICON || uView == LVS_SMALLICON);
1329 /******** Internal API functions ************************************/
1331 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1333 static COLUMN_INFO mainItem;
1335 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1336 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1337 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1340 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1342 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1345 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1347 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1350 /* Listview invalidation functions: use _only_ these functions to invalidate */
1352 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1354 return infoPtr->bRedraw;
1357 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1359 if(!is_redrawing(infoPtr)) return;
1360 TRACE(" invalidating rect=%s\n", debugrect(rect));
1361 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1364 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1368 if(!is_redrawing(infoPtr)) return;
1369 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1370 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1373 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1375 POINT Origin, Position;
1378 if(!is_redrawing(infoPtr)) return;
1379 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1380 LISTVIEW_GetOrigin(infoPtr, &Origin);
1381 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1382 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1384 rcBox.bottom = infoPtr->nItemHeight;
1385 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1386 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1389 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1391 LISTVIEW_InvalidateRect(infoPtr, NULL);
1394 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1398 if(!is_redrawing(infoPtr)) return;
1399 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1400 rcCol.top = infoPtr->rcList.top;
1401 rcCol.bottom = infoPtr->rcList.bottom;
1402 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1407 * Retrieves the number of items that can fit vertically in the client area.
1410 * [I] infoPtr : valid pointer to the listview structure
1413 * Number of items per row.
1415 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1417 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1419 return max(nListWidth/infoPtr->nItemWidth, 1);
1424 * Retrieves the number of items that can fit horizontally in the client
1428 * [I] infoPtr : valid pointer to the listview structure
1431 * Number of items per column.
1433 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1435 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1437 return max(nListHeight / infoPtr->nItemHeight, 1);
1441 /*************************************************************************
1442 * LISTVIEW_ProcessLetterKeys
1444 * Processes keyboard messages generated by pressing the letter keys
1446 * What this does is perform a case insensitive search from the
1447 * current position with the following quirks:
1448 * - If two chars or more are pressed in quick succession we search
1449 * for the corresponding string (e.g. 'abc').
1450 * - If there is a delay we wipe away the current search string and
1451 * restart with just that char.
1452 * - If the user keeps pressing the same character, whether slowly or
1453 * fast, so that the search string is entirely composed of this
1454 * character ('aaaaa' for instance), then we search for first item
1455 * that starting with that character.
1456 * - If the user types the above character in quick succession, then
1457 * we must also search for the corresponding string ('aaaaa'), and
1458 * go to that string if there is a match.
1461 * [I] hwnd : handle to the window
1462 * [I] charCode : the character code, the actual character
1463 * [I] keyData : key data
1471 * - The current implementation has a list of characters it will
1472 * accept and it ignores averything else. In particular it will
1473 * ignore accentuated characters which seems to match what
1474 * Windows does. But I'm not sure it makes sense to follow
1476 * - We don't sound a beep when the search fails.
1480 * TREEVIEW_ProcessLetterKeys
1482 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1487 WCHAR buffer[MAX_PATH];
1488 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1490 /* simple parameter checking */
1491 if (!charCode || !keyData) return 0;
1493 /* only allow the valid WM_CHARs through */
1494 if (!isalnum(charCode) &&
1495 charCode != '.' && charCode != '`' && charCode != '!' &&
1496 charCode != '@' && charCode != '#' && charCode != '$' &&
1497 charCode != '%' && charCode != '^' && charCode != '&' &&
1498 charCode != '*' && charCode != '(' && charCode != ')' &&
1499 charCode != '-' && charCode != '_' && charCode != '+' &&
1500 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1501 charCode != '}' && charCode != '[' && charCode != '{' &&
1502 charCode != '/' && charCode != '?' && charCode != '>' &&
1503 charCode != '<' && charCode != ',' && charCode != '~')
1506 /* if there's one item or less, there is no where to go */
1507 if (infoPtr->nItemCount <= 1) return 0;
1509 /* update the search parameters */
1510 infoPtr->lastKeyPressTimestamp = GetTickCount();
1511 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1512 if (infoPtr->nSearchParamLength < MAX_PATH)
1513 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1514 if (infoPtr->charCode != charCode)
1515 infoPtr->charCode = charCode = 0;
1517 infoPtr->charCode=charCode;
1518 infoPtr->szSearchParam[0]=charCode;
1519 infoPtr->nSearchParamLength=1;
1520 /* Redundant with the 1 char string */
1524 /* and search from the current position */
1526 if (infoPtr->nFocusedItem >= 0) {
1527 endidx=infoPtr->nFocusedItem;
1529 /* if looking for single character match,
1530 * then we must always move forward
1532 if (infoPtr->nSearchParamLength == 1)
1535 endidx=infoPtr->nItemCount;
1539 if (idx == infoPtr->nItemCount) {
1540 if (endidx == infoPtr->nItemCount || endidx == 0)
1546 item.mask = LVIF_TEXT;
1549 item.pszText = buffer;
1550 item.cchTextMax = MAX_PATH;
1551 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1553 /* check for a match */
1554 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1557 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1558 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1559 /* This would work but we must keep looking for a longer match */
1563 } while (idx != endidx);
1566 LISTVIEW_KeySelection(infoPtr, nItem);
1571 /*************************************************************************
1572 * LISTVIEW_UpdateHeaderSize [Internal]
1574 * Function to resize the header control
1577 * [I] hwnd : handle to a window
1578 * [I] nNewScrollPos : scroll pos to set
1583 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1588 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1590 GetWindowRect(infoPtr->hwndHeader, &winRect);
1591 point[0].x = winRect.left;
1592 point[0].y = winRect.top;
1593 point[1].x = winRect.right;
1594 point[1].y = winRect.bottom;
1596 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1597 point[0].x = -nNewScrollPos;
1598 point[1].x += nNewScrollPos;
1600 SetWindowPos(infoPtr->hwndHeader,0,
1601 point[0].x,point[0].y,point[1].x,point[1].y,
1602 SWP_NOZORDER | SWP_NOACTIVATE);
1607 * Update the scrollbars. This functions should be called whenever
1608 * the content, size or view changes.
1611 * [I] infoPtr : valid pointer to the listview structure
1616 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1618 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1619 SCROLLINFO horzInfo, vertInfo;
1621 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1623 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1624 horzInfo.cbSize = sizeof(SCROLLINFO);
1625 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1627 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1628 if (uView == LVS_LIST)
1630 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1631 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1633 /* scroll by at least one column per page */
1634 if(horzInfo.nPage < infoPtr->nItemWidth)
1635 horzInfo.nPage = infoPtr->nItemWidth;
1637 horzInfo.nPage /= infoPtr->nItemWidth;
1639 else if (uView == LVS_REPORT)
1641 horzInfo.nMax = infoPtr->nItemWidth;
1643 else /* LVS_ICON, or LVS_SMALLICON */
1647 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1650 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1651 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1652 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1653 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1655 /* Setting the horizontal scroll can change the listview size
1656 * (and potentially everything else) so we need to recompute
1657 * everything again for the vertical scroll
1660 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1661 vertInfo.cbSize = sizeof(SCROLLINFO);
1662 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1664 if (uView == LVS_REPORT)
1666 vertInfo.nMax = infoPtr->nItemCount;
1668 /* scroll by at least one page */
1669 if(vertInfo.nPage < infoPtr->nItemHeight)
1670 vertInfo.nPage = infoPtr->nItemHeight;
1672 vertInfo.nPage /= infoPtr->nItemHeight;
1674 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1678 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1681 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1682 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1683 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1684 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1686 /* Update the Header Control */
1687 if (uView == LVS_REPORT)
1689 horzInfo.fMask = SIF_POS;
1690 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1691 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1698 * Shows/hides the focus rectangle.
1701 * [I] infoPtr : valid pointer to the listview structure
1702 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1707 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1709 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1712 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1714 if (infoPtr->nFocusedItem < 0) return;
1716 /* we need some gymnastics in ICON mode to handle large items */
1717 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1721 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1722 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1724 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1729 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1731 /* for some reason, owner draw should work only in report mode */
1732 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1737 item.iItem = infoPtr->nFocusedItem;
1739 item.mask = LVIF_PARAM;
1740 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1742 ZeroMemory(&dis, sizeof(dis));
1743 dis.CtlType = ODT_LISTVIEW;
1744 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1745 dis.itemID = item.iItem;
1746 dis.itemAction = ODA_FOCUS;
1747 if (fShow) dis.itemState |= ODS_FOCUS;
1748 dis.hwndItem = infoPtr->hwndSelf;
1750 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1751 dis.itemData = item.lParam;
1753 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1757 DrawFocusRect(hdc, &infoPtr->rcFocus);
1760 ReleaseDC(infoPtr->hwndSelf, hdc);
1764 * Invalidates all visible selected items.
1766 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1770 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1771 while(iterator_next(&i))
1773 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1774 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1776 iterator_destroy(&i);
1781 * DESCRIPTION: [INTERNAL]
1782 * Computes an item's (left,top) corner, relative to rcView.
1783 * That is, the position has NOT been made relative to the Origin.
1784 * This is deliberate, to avoid computing the Origin over, and
1785 * over again, when this function is call in a loop. Instead,
1786 * one ca factor the computation of the Origin before the loop,
1787 * and offset the value retured by this function, on every iteration.
1790 * [I] infoPtr : valid pointer to the listview structure
1791 * [I] nItem : item number
1792 * [O] lpptOrig : item top, left corner
1797 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1799 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1801 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1803 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1805 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1806 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1808 else if (uView == LVS_LIST)
1810 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1811 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1812 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1814 else /* LVS_REPORT */
1816 lpptPosition->x = 0;
1817 lpptPosition->y = nItem * infoPtr->nItemHeight;
1822 * DESCRIPTION: [INTERNAL]
1823 * Compute the rectangles of an item. This is to localize all
1824 * the computations in one place. If you are not interested in some
1825 * of these values, simply pass in a NULL -- the fucntion is smart
1826 * enough to compute only what's necessary. The function computes
1827 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1828 * one, the BOX rectangle. This rectangle is very cheap to compute,
1829 * and is guaranteed to contain all the other rectangles. Computing
1830 * the ICON rect is also cheap, but all the others are potentaily
1831 * expensive. This gives an easy and effective optimization when
1832 * searching (like point inclusion, or rectangle intersection):
1833 * first test against the BOX, and if TRUE, test agains the desired
1835 * If the function does not have all the necessary information
1836 * to computed the requested rectangles, will crash with a
1837 * failed assertion. This is done so we catch all programming
1838 * errors, given that the function is called only from our code.
1840 * We have the following 'special' meanings for a few fields:
1841 * * If LVIS_FOCUSED is set, we assume the item has the focus
1842 * This is important in ICON mode, where it might get a larger
1843 * then usual rectange
1845 * Please note that subitem support works only in REPORT mode.
1848 * [I] infoPtr : valid pointer to the listview structure
1849 * [I] lpLVItem : item to compute the measures for
1850 * [O] lprcBox : ptr to Box rectangle
1851 * The internal LVIR_BOX rectangle
1852 * [0] lprcState : ptr to State icon rectangle
1853 * The internal LVIR_STATE rectangle
1854 * [O] lprcIcon : ptr to Icon rectangle
1855 * Same as LVM_GETITEMRECT with LVIR_ICON
1856 * [O] lprcLabel : ptr to Label rectangle
1857 * Same as LVM_GETITEMRECT with LVIR_LABEL
1862 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1863 LPRECT lprcBox, LPRECT lprcState,
1864 LPRECT lprcIcon, LPRECT lprcLabel)
1866 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1867 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1868 RECT Box, State, Icon, Label;
1869 COLUMN_INFO *lpColumnInfo = NULL;
1871 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1873 /* Be smart and try to figure out the minimum we have to do */
1874 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1875 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1877 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1878 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1880 if (lprcLabel) doLabel = TRUE;
1881 if (doLabel || lprcIcon) doIcon = TRUE;
1882 if (doIcon || lprcState) doState = TRUE;
1884 /************************************************************/
1885 /* compute the box rectangle (it should be cheap to do) */
1886 /************************************************************/
1887 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1888 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1890 if (lpLVItem->iSubItem)
1892 Box = lpColumnInfo->rcHeader;
1897 Box.right = infoPtr->nItemWidth;
1900 Box.bottom = infoPtr->nItemHeight;
1902 /************************************************************/
1903 /* compute STATEICON bounding box */
1904 /************************************************************/
1907 if (uView == LVS_ICON)
1909 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1910 if (infoPtr->himlNormal)
1911 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1912 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1916 /* we need the ident in report mode, if we don't have it, we fail */
1917 State.left = Box.left;
1918 if (uView == LVS_REPORT)
1920 if (lpLVItem->iSubItem == 0)
1922 State.left += REPORT_MARGINX;
1923 assert(lpLVItem->mask & LVIF_INDENT);
1924 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1927 State.top = Box.top;
1929 State.right = State.left;
1930 State.bottom = State.top;
1931 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1933 State.right += infoPtr->iconStateSize.cx;
1934 State.bottom += infoPtr->iconStateSize.cy;
1936 if (lprcState) *lprcState = State;
1937 TRACE(" - state=%s\n", debugrect(&State));
1940 /************************************************************/
1941 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1942 /************************************************************/
1945 if (uView == LVS_ICON)
1947 Icon.left = Box.left;
1948 if (infoPtr->himlNormal)
1949 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1950 Icon.top = Box.top + ICON_TOP_PADDING;
1951 Icon.right = Icon.left;
1952 Icon.bottom = Icon.top;
1953 if (infoPtr->himlNormal)
1955 Icon.right += infoPtr->iconSize.cx;
1956 Icon.bottom += infoPtr->iconSize.cy;
1959 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1961 Icon.left = State.right;
1963 Icon.right = Icon.left;
1964 if (infoPtr->himlSmall &&
1965 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1966 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1967 Icon.right += infoPtr->iconSize.cx;
1968 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1970 if(lprcIcon) *lprcIcon = Icon;
1971 TRACE(" - icon=%s\n", debugrect(&Icon));
1974 /************************************************************/
1975 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1976 /************************************************************/
1979 SIZE labelSize = { 0, 0 };
1981 /* calculate how far to the right can the label strech */
1982 Label.right = Box.right;
1983 if (uView == LVS_REPORT)
1985 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1988 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1990 labelSize.cx = infoPtr->nItemWidth;
1991 labelSize.cy = infoPtr->nItemHeight;
1995 /* we need the text in non owner draw mode */
1996 assert(lpLVItem->mask & LVIF_TEXT);
1997 if (is_textT(lpLVItem->pszText, TRUE))
1999 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2000 HDC hdc = GetDC(infoPtr->hwndSelf);
2001 HFONT hOldFont = SelectObject(hdc, hFont);
2005 /* compute rough rectangle where the label will go */
2006 SetRectEmpty(&rcText);
2007 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2008 rcText.bottom = infoPtr->nItemHeight;
2009 if (uView == LVS_ICON)
2010 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2012 /* now figure out the flags */
2013 if (uView == LVS_ICON)
2014 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2016 uFormat = LV_SL_DT_FLAGS;
2018 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2020 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2021 labelSize.cy = rcText.bottom - rcText.top;
2023 SelectObject(hdc, hOldFont);
2024 ReleaseDC(infoPtr->hwndSelf, hdc);
2028 if (uView == LVS_ICON)
2030 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2031 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2032 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2033 Label.right = Label.left + labelSize.cx;
2034 Label.bottom = Label.top + infoPtr->nItemHeight;
2035 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2037 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2038 labelSize.cy /= infoPtr->ntmHeight;
2039 labelSize.cy = max(labelSize.cy, 1);
2040 labelSize.cy *= infoPtr->ntmHeight;
2042 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2044 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2046 Label.left = Icon.right;
2047 Label.top = Box.top;
2048 Label.right = min(Label.left + labelSize.cx, Label.right);
2049 Label.bottom = Label.top + infoPtr->nItemHeight;
2052 if (lprcLabel) *lprcLabel = Label;
2053 TRACE(" - label=%s\n", debugrect(&Label));
2056 /* Fix the Box if necessary */
2059 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2060 else *lprcBox = Box;
2062 TRACE(" - box=%s\n", debugrect(&Box));
2066 * DESCRIPTION: [INTERNAL]
2069 * [I] infoPtr : valid pointer to the listview structure
2070 * [I] nItem : item number
2071 * [O] lprcBox : ptr to Box rectangle
2076 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2078 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2079 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2080 POINT Position, Origin;
2083 LISTVIEW_GetOrigin(infoPtr, &Origin);
2084 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2086 /* Be smart and try to figure out the minimum we have to do */
2088 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2089 lvItem.mask |= LVIF_TEXT;
2090 lvItem.iItem = nItem;
2091 lvItem.iSubItem = 0;
2092 lvItem.pszText = szDispText;
2093 lvItem.cchTextMax = DISP_TEXT_SIZE;
2094 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2095 if (uView == LVS_ICON)
2097 lvItem.mask |= LVIF_STATE;
2098 lvItem.stateMask = LVIS_FOCUSED;
2099 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2101 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2103 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2109 * Returns the current icon position, and advances it along the top.
2110 * The returned position is not offset by Origin.
2113 * [I] infoPtr : valid pointer to the listview structure
2114 * [O] lpPos : will get the current icon position
2119 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2121 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2123 *lpPos = infoPtr->currIconPos;
2125 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2126 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2128 infoPtr->currIconPos.x = 0;
2129 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2135 * Returns the current icon position, and advances it down the left edge.
2136 * The returned position is not offset by Origin.
2139 * [I] infoPtr : valid pointer to the listview structure
2140 * [O] lpPos : will get the current icon position
2145 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2147 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2149 *lpPos = infoPtr->currIconPos;
2151 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2152 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2154 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2155 infoPtr->currIconPos.y = 0;
2161 * Moves an icon to the specified position.
2162 * It takes care of invalidating the item, etc.
2165 * [I] infoPtr : valid pointer to the listview structure
2166 * [I] nItem : the item to move
2167 * [I] lpPos : the new icon position
2168 * [I] isNew : flags the item as being new
2174 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2180 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2181 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2183 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2184 LISTVIEW_InvalidateItem(infoPtr, nItem);
2187 /* Allocating a POINTER for every item is too resource intensive,
2188 * so we'll keep the (x,y) in different arrays */
2189 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2190 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2192 LISTVIEW_InvalidateItem(infoPtr, nItem);
2199 * Arranges listview items in icon display mode.
2202 * [I] infoPtr : valid pointer to the listview structure
2203 * [I] nAlignCode : alignment code
2209 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2211 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2212 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2216 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2218 TRACE("nAlignCode=%d\n", nAlignCode);
2220 if (nAlignCode == LVA_DEFAULT)
2222 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2223 else nAlignCode = LVA_ALIGNTOP;
2228 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2229 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2230 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2231 default: return FALSE;
2234 infoPtr->bAutoarrange = TRUE;
2235 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2236 for (i = 0; i < infoPtr->nItemCount; i++)
2238 next_pos(infoPtr, &pos);
2239 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2247 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2250 * [I] infoPtr : valid pointer to the listview structure
2251 * [O] lprcView : bounding rectangle
2257 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2261 SetRectEmpty(lprcView);
2263 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2267 for (i = 0; i < infoPtr->nItemCount; i++)
2269 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2270 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2271 lprcView->right = max(lprcView->right, x);
2272 lprcView->bottom = max(lprcView->bottom, y);
2274 if (infoPtr->nItemCount > 0)
2276 lprcView->right += infoPtr->nItemWidth;
2277 lprcView->bottom += infoPtr->nItemHeight;
2282 y = LISTVIEW_GetCountPerColumn(infoPtr);
2283 x = infoPtr->nItemCount / y;
2284 if (infoPtr->nItemCount % y) x++;
2285 lprcView->right = x * infoPtr->nItemWidth;
2286 lprcView->bottom = y * infoPtr->nItemHeight;
2290 lprcView->right = infoPtr->nItemWidth;
2291 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2298 * Retrieves the bounding rectangle of all the items.
2301 * [I] infoPtr : valid pointer to the listview structure
2302 * [O] lprcView : bounding rectangle
2308 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2312 TRACE("(lprcView=%p)\n", lprcView);
2314 if (!lprcView) return FALSE;
2316 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2317 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2318 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2320 TRACE("lprcView=%s\n", debugrect(lprcView));
2327 * Retrieves the subitem pointer associated with the subitem index.
2330 * [I] hdpaSubItems : DPA handle for a specific item
2331 * [I] nSubItem : index of subitem
2334 * SUCCESS : subitem pointer
2337 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2339 SUBITEM_INFO *lpSubItem;
2342 /* we should binary search here if need be */
2343 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2345 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2346 if (lpSubItem->iSubItem == nSubItem)
2356 * Caclulates the desired item width.
2359 * [I] infoPtr : valid pointer to the listview structure
2362 * The desired item width.
2364 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2366 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2369 TRACE("uView=%d\n", uView);
2371 if (uView == LVS_ICON)
2372 nItemWidth = infoPtr->iconSpacing.cx;
2373 else if (uView == LVS_REPORT)
2377 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2379 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2380 nItemWidth = rcHeader.right;
2383 else /* LVS_SMALLICON, or LVS_LIST */
2387 for (i = 0; i < infoPtr->nItemCount; i++)
2388 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2390 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2391 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2393 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2396 return max(nItemWidth, 1);
2401 * Caclulates the desired item height.
2404 * [I] infoPtr : valid pointer to the listview structure
2407 * The desired item height.
2409 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2411 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2414 TRACE("uView=%d\n", uView);
2416 if (uView == LVS_ICON)
2417 nItemHeight = infoPtr->iconSpacing.cy;
2420 nItemHeight = infoPtr->ntmHeight;
2421 if (infoPtr->himlState)
2422 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2423 if (infoPtr->himlSmall)
2424 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2425 if (infoPtr->himlState || infoPtr->himlSmall)
2426 nItemHeight += HEIGHT_PADDING;
2429 return max(nItemHeight, 1);
2434 * Updates the width, and height of an item.
2437 * [I] infoPtr : valid pointer to the listview structure
2442 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2444 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2445 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2451 * Retrieves and saves important text metrics info for the current
2455 * [I] infoPtr : valid pointer to the listview structure
2458 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2460 HDC hdc = GetDC(infoPtr->hwndSelf);
2461 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2462 HFONT hOldFont = SelectObject(hdc, hFont);
2465 if (GetTextMetricsW(hdc, &tm))
2467 infoPtr->ntmHeight = tm.tmHeight;
2468 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2470 SelectObject(hdc, hOldFont);
2471 ReleaseDC(infoPtr->hwndSelf, hdc);
2473 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2478 * A compare function for ranges
2481 * [I] range1 : pointer to range 1;
2482 * [I] range2 : pointer to range 2;
2486 * > 0 : if range 1 > range 2
2487 * < 0 : if range 2 > range 1
2488 * = 0 : if range intersects range 2
2490 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2494 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2496 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2501 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2507 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2509 #define ranges_check(ranges, desc) do { } while(0)
2512 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2517 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2519 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2520 ranges_dump(ranges);
2521 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2522 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2523 assert (prev->lower >= 0 && prev->lower < prev->upper);
2524 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2526 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2527 assert (prev->upper <= curr->lower);
2528 assert (curr->lower < curr->upper);
2531 TRACE("--- Done checking---\n");
2534 static RANGES ranges_create(int count)
2536 RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2537 if (!ranges) return NULL;
2538 ranges->hdpa = DPA_Create(count);
2539 if (ranges->hdpa) return ranges;
2544 static void ranges_clear(RANGES ranges)
2548 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2549 Free(DPA_GetPtr(ranges->hdpa, i));
2550 DPA_DeleteAllPtrs(ranges->hdpa);
2554 static void ranges_destroy(RANGES ranges)
2556 if (!ranges) return;
2557 ranges_clear(ranges);
2558 DPA_Destroy(ranges->hdpa);
2562 static RANGES ranges_clone(RANGES ranges)
2567 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2569 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2571 RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2572 if (!newrng) goto fail;
2573 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2574 DPA_SetPtr(clone->hdpa, i, newrng);
2579 TRACE ("clone failed\n");
2580 ranges_destroy(clone);
2584 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2588 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2589 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2594 static void ranges_dump(RANGES ranges)
2598 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2599 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2602 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2604 RANGE srchrng = { nItem, nItem + 1 };
2606 TRACE("(nItem=%d)\n", nItem);
2607 ranges_check(ranges, "before contain");
2608 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2611 static INT ranges_itemcount(RANGES ranges)
2615 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2617 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2618 count += sel->upper - sel->lower;
2624 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2626 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2629 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2630 if (index == -1) return TRUE;
2632 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2634 chkrng = DPA_GetPtr(ranges->hdpa, index);
2635 if (chkrng->lower >= nItem)
2636 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2637 if (chkrng->upper > nItem)
2638 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2643 static BOOL ranges_add(RANGES ranges, RANGE range)
2648 TRACE("(%s)\n", debugrange(&range));
2649 ranges_check(ranges, "before add");
2651 /* try find overlapping regions first */
2652 srchrgn.lower = range.lower - 1;
2653 srchrgn.upper = range.upper + 1;
2654 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2660 TRACE("Adding new range\n");
2662 /* create the brand new range to insert */
2663 newrgn = (RANGE *)Alloc(sizeof(RANGE));
2664 if(!newrgn) goto fail;
2667 /* figure out where to insert it */
2668 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2669 TRACE("index=%d\n", index);
2670 if (index == -1) index = 0;
2672 /* and get it over with */
2673 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2681 RANGE *chkrgn, *mrgrgn;
2682 INT fromindex, mergeindex;
2684 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2685 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2687 chkrgn->lower = min(range.lower, chkrgn->lower);
2688 chkrgn->upper = max(range.upper, chkrgn->upper);
2690 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2692 /* merge now common anges */
2694 srchrgn.lower = chkrgn->lower - 1;
2695 srchrgn.upper = chkrgn->upper + 1;
2699 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2700 if (mergeindex == -1) break;
2701 if (mergeindex == index)
2703 fromindex = index + 1;
2707 TRACE("Merge with index %i\n", mergeindex);
2709 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2710 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2711 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2713 DPA_DeletePtr(ranges->hdpa, mergeindex);
2714 if (mergeindex < index) index --;
2718 ranges_check(ranges, "after add");
2722 ranges_check(ranges, "failed add");
2726 static BOOL ranges_del(RANGES ranges, RANGE range)
2731 TRACE("(%s)\n", debugrange(&range));
2732 ranges_check(ranges, "before del");
2734 /* we don't use DPAS_SORTED here, since we need *
2735 * to find the first overlapping range */
2736 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2739 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2741 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2743 /* case 1: Same range */
2744 if ( (chkrgn->upper == range.upper) &&
2745 (chkrgn->lower == range.lower) )
2747 DPA_DeletePtr(ranges->hdpa, index);
2750 /* case 2: engulf */
2751 else if ( (chkrgn->upper <= range.upper) &&
2752 (chkrgn->lower >= range.lower) )
2754 DPA_DeletePtr(ranges->hdpa, index);
2756 /* case 3: overlap upper */
2757 else if ( (chkrgn->upper <= range.upper) &&
2758 (chkrgn->lower < range.lower) )
2760 chkrgn->upper = range.lower;
2762 /* case 4: overlap lower */
2763 else if ( (chkrgn->upper > range.upper) &&
2764 (chkrgn->lower >= range.lower) )
2766 chkrgn->lower = range.upper;
2769 /* case 5: fully internal */
2772 RANGE tmprgn = *chkrgn, *newrgn;
2774 if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2775 newrgn->lower = chkrgn->lower;
2776 newrgn->upper = range.lower;
2777 chkrgn->lower = range.upper;
2778 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2787 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2790 ranges_check(ranges, "after del");
2794 ranges_check(ranges, "failed del");
2800 * Removes all selection ranges
2803 * [I] infoPtr : valid pointer to the listview structure
2804 * [I] toSkip : item range to skip removing the selection
2810 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2819 lvItem.stateMask = LVIS_SELECTED;
2821 /* need to clone the DPA because callbacks can change it */
2822 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2823 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2824 while(iterator_next(&i))
2825 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2826 /* note that the iterator destructor will free the cloned range */
2827 iterator_destroy(&i);
2832 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2836 if (!(toSkip = ranges_create(1))) return FALSE;
2837 if (nItem != -1) ranges_additem(toSkip, nItem);
2838 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2839 ranges_destroy(toSkip);
2843 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2845 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2850 * Retrieves the number of items that are marked as selected.
2853 * [I] infoPtr : valid pointer to the listview structure
2856 * Number of items selected.
2858 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2860 INT nSelectedCount = 0;
2862 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2865 for (i = 0; i < infoPtr->nItemCount; i++)
2867 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2872 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2874 TRACE("nSelectedCount=%d\n", nSelectedCount);
2875 return nSelectedCount;
2880 * Manages the item focus.
2883 * [I] infoPtr : valid pointer to the listview structure
2884 * [I] nItem : item index
2887 * TRUE : focused item changed
2888 * FALSE : focused item has NOT changed
2890 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2892 INT oldFocus = infoPtr->nFocusedItem;
2895 if (nItem == infoPtr->nFocusedItem) return FALSE;
2897 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2898 lvItem.stateMask = LVIS_FOCUSED;
2899 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2901 return oldFocus != infoPtr->nFocusedItem;
2904 /* Helper function for LISTVIEW_ShiftIndices *only* */
2905 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2907 if (nShiftItem < nItem) return nShiftItem;
2909 if (nShiftItem > nItem) return nShiftItem + direction;
2911 if (direction > 0) return nShiftItem + direction;
2913 return min(nShiftItem, infoPtr->nItemCount - 1);
2918 * Updates the various indices after an item has been inserted or deleted.
2921 * [I] infoPtr : valid pointer to the listview structure
2922 * [I] nItem : item index
2923 * [I] direction : Direction of shift, +1 or -1.
2928 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2933 /* temporarily disable change notification while shifting items */
2934 bOldChange = infoPtr->bDoChangeNotify;
2935 infoPtr->bDoChangeNotify = FALSE;
2937 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2939 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2941 assert(abs(direction) == 1);
2943 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2945 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2946 if (nNewFocus != infoPtr->nFocusedItem)
2947 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2949 /* But we are not supposed to modify nHotItem! */
2951 infoPtr->bDoChangeNotify = bOldChange;
2957 * Adds a block of selections.
2960 * [I] infoPtr : valid pointer to the listview structure
2961 * [I] nItem : item index
2966 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2968 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2969 INT nLast = max(infoPtr->nSelectionMark, nItem);
2973 if (nFirst == -1) nFirst = nItem;
2975 item.state = LVIS_SELECTED;
2976 item.stateMask = LVIS_SELECTED;
2978 /* FIXME: this is not correct LVS_OWNERDATA
2979 * setting the item states individually will generate
2980 * a LVN_ITEMCHANGED notification for each one. Instead,
2981 * we have to send a LVN_ODSTATECHANGED notification.
2982 * See MSDN documentation for LVN_ITEMCHANGED.
2984 for (i = nFirst; i <= nLast; i++)
2985 LISTVIEW_SetItemState(infoPtr,i,&item);
2991 * Sets a single group selection.
2994 * [I] infoPtr : valid pointer to the listview structure
2995 * [I] nItem : item index
3000 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3002 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3007 if (!(selection = ranges_create(100))) return;
3009 item.state = LVIS_SELECTED;
3010 item.stateMask = LVIS_SELECTED;
3012 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3014 if (infoPtr->nSelectionMark == -1)
3016 infoPtr->nSelectionMark = nItem;
3017 ranges_additem(selection, nItem);
3023 sel.lower = min(infoPtr->nSelectionMark, nItem);
3024 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3025 ranges_add(selection, sel);
3030 RECT rcItem, rcSel, rcSelMark;
3033 rcItem.left = LVIR_BOUNDS;
3034 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3035 rcSelMark.left = LVIR_BOUNDS;
3036 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3037 UnionRect(&rcSel, &rcItem, &rcSelMark);
3038 iterator_frameditems(&i, infoPtr, &rcSel);
3039 while(iterator_next(&i))
3041 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3042 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3044 iterator_destroy(&i);
3047 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3048 iterator_rangesitems(&i, selection);
3049 while(iterator_next(&i))
3050 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3051 /* this will also destroy the selection */
3052 iterator_destroy(&i);
3054 LISTVIEW_SetItemFocus(infoPtr, nItem);
3059 * Sets a single selection.
3062 * [I] infoPtr : valid pointer to the listview structure
3063 * [I] nItem : item index
3068 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3072 TRACE("nItem=%d\n", nItem);
3074 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3076 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3077 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3078 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3080 infoPtr->nSelectionMark = nItem;
3085 * Set selection(s) with keyboard.
3088 * [I] infoPtr : valid pointer to the listview structure
3089 * [I] nItem : item index
3092 * SUCCESS : TRUE (needs to be repainted)
3093 * FAILURE : FALSE (nothing has changed)
3095 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3097 /* FIXME: pass in the state */
3098 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3099 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3100 BOOL bResult = FALSE;
3102 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3104 if (infoPtr->dwStyle & LVS_SINGLESEL)
3107 LISTVIEW_SetSelection(infoPtr, nItem);
3114 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3118 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3123 LISTVIEW_SetSelection(infoPtr, nItem);
3126 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3129 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3136 * Called when the mouse is being actively tracked and has hovered for a specified
3140 * [I] infoPtr : valid pointer to the listview structure
3141 * [I] fwKeys : key indicator
3142 * [I] x,y : mouse position
3145 * 0 if the message was processed, non-zero if there was an error
3148 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3149 * over the item for a certain period of time.
3152 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, INT x, INT y)
3154 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3155 /* FIXME: select the item!!! */
3156 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3163 * Called whenever WM_MOUSEMOVE is received.
3166 * [I] infoPtr : valid pointer to the listview structure
3167 * [I] fwKeys : key indicator
3168 * [I] x,y : mouse position
3171 * 0 if the message is processed, non-zero if there was an error
3173 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3175 TRACKMOUSEEVENT trackinfo;
3177 /* see if we are supposed to be tracking mouse hovering */
3178 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3179 /* fill in the trackinfo struct */
3180 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3181 trackinfo.dwFlags = TME_QUERY;
3182 trackinfo.hwndTrack = infoPtr->hwndSelf;
3183 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3185 /* see if we are already tracking this hwnd */
3186 _TrackMouseEvent(&trackinfo);
3188 if(!(trackinfo.dwFlags & TME_HOVER)) {
3189 trackinfo.dwFlags = TME_HOVER;
3191 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3192 _TrackMouseEvent(&trackinfo);
3201 * Tests wheather the item is assignable to a list with style lStyle
3203 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3205 if ( (lpLVItem->mask & LVIF_TEXT) &&
3206 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3207 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3215 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3218 * [I] infoPtr : valid pointer to the listview structure
3219 * [I] lpLVItem : valid pointer to new item atttributes
3220 * [I] isNew : the item being set is being inserted
3221 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3222 * [O] bChanged : will be set to TRUE if the item really changed
3228 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3230 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3238 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3240 if (lpLVItem->mask == 0) return TRUE;
3242 if (infoPtr->dwStyle & LVS_OWNERDATA)
3244 /* a virtual listview we stores only selection and focus */
3245 if (lpLVItem->mask & ~LVIF_STATE)
3251 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3252 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3256 /* we need to get the lParam and state of the item */
3257 item.iItem = lpLVItem->iItem;
3258 item.iSubItem = lpLVItem->iSubItem;
3259 item.mask = LVIF_STATE | LVIF_PARAM;
3260 item.stateMask = ~0;
3263 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3265 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3266 /* determine what fields will change */
3267 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3268 uChanged |= LVIF_STATE;
3270 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3271 uChanged |= LVIF_IMAGE;
3273 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3274 uChanged |= LVIF_PARAM;
3276 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3277 uChanged |= LVIF_INDENT;
3279 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3280 uChanged |= LVIF_TEXT;
3282 TRACE("uChanged=0x%x\n", uChanged);
3283 if (!uChanged) return TRUE;
3286 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3287 nmlv.iItem = lpLVItem->iItem;
3288 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3289 nmlv.uOldState = item.state;
3290 nmlv.uChanged = uChanged;
3291 nmlv.lParam = item.lParam;
3293 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3294 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3296 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3297 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3300 /* copy information */
3301 if (lpLVItem->mask & LVIF_TEXT)
3302 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3304 if (lpLVItem->mask & LVIF_IMAGE)
3305 lpItem->hdr.iImage = lpLVItem->iImage;
3307 if (lpLVItem->mask & LVIF_PARAM)
3308 lpItem->lParam = lpLVItem->lParam;
3310 if (lpLVItem->mask & LVIF_INDENT)
3311 lpItem->iIndent = lpLVItem->iIndent;
3313 if (uChanged & LVIF_STATE)
3315 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3317 lpItem->state &= ~lpLVItem->stateMask;
3318 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3320 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3322 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3323 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3325 else if (lpLVItem->stateMask & LVIS_SELECTED)
3326 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3328 /* if we are asked to change focus, and we manage it, do it */
3329 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3331 if (lpLVItem->state & LVIS_FOCUSED)
3333 LISTVIEW_SetItemFocus(infoPtr, -1);
3334 infoPtr->nFocusedItem = lpLVItem->iItem;
3335 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3337 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3338 infoPtr->nFocusedItem = -1;
3342 /* if we're inserting the item, we're done */
3343 if (isNew) return TRUE;
3345 /* send LVN_ITEMCHANGED notification */
3346 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3347 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3354 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3357 * [I] infoPtr : valid pointer to the listview structure
3358 * [I] lpLVItem : valid pointer to new subitem atttributes
3359 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3360 * [O] bChanged : will be set to TRUE if the item really changed
3366 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3369 SUBITEM_INFO *lpSubItem;
3371 /* we do not support subitems for virtual listviews */
3372 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3374 /* set subitem only if column is present */
3375 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3377 /* First do some sanity checks */
3378 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3379 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3381 /* get the subitem structure, and create it if not there */
3382 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3383 assert (hdpaSubItems);
3385 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3388 SUBITEM_INFO *tmpSubItem;
3391 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3392 if (!lpSubItem) return FALSE;
3393 /* we could binary search here, if need be...*/
3394 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3396 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3397 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3399 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3404 lpSubItem->iSubItem = lpLVItem->iSubItem;
3405 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3409 if (lpLVItem->mask & LVIF_IMAGE)
3410 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3412 lpSubItem->hdr.iImage = lpLVItem->iImage;
3416 if (lpLVItem->mask & LVIF_TEXT)
3417 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3419 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3428 * Sets item attributes.
3431 * [I] infoPtr : valid pointer to the listview structure
3432 * [I] lpLVItem : new item atttributes
3433 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3439 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3441 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3442 LPWSTR pszText = NULL;
3443 BOOL bResult, bChanged = FALSE;
3445 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3447 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3450 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3451 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3453 pszText = lpLVItem->pszText;
3454 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3457 /* actually set the fields */
3458 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3460 if (lpLVItem->iSubItem)
3461 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3463 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3465 /* redraw item, if necessary */
3466 if (bChanged && !infoPtr->bIsDrawing)
3468 /* this little optimization eliminates some nasty flicker */
3469 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3470 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3471 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3473 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3478 textfreeT(lpLVItem->pszText, isW);
3479 ((LVITEMW *)lpLVItem)->pszText = pszText;
3487 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3490 * [I] infoPtr : valid pointer to the listview structure
3495 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3497 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3499 SCROLLINFO scrollInfo;
3501 scrollInfo.cbSize = sizeof(SCROLLINFO);
3502 scrollInfo.fMask = SIF_POS;
3504 if (uView == LVS_LIST)
3506 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3507 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3509 else if (uView == LVS_REPORT)
3511 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3512 nItem = scrollInfo.nPos;
3516 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3517 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3520 TRACE("nItem=%d\n", nItem);
3528 * Erases the background of the given rectangle
3531 * [I] infoPtr : valid pointer to the listview structure
3532 * [I] hdc : device context handle
3533 * [I] lprcBox : clipping rectangle
3539 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3541 if (!infoPtr->hBkBrush) return FALSE;
3543 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3545 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3553 * [I] infoPtr : valid pointer to the listview structure
3554 * [I] hdc : device context handle
3555 * [I] nItem : item index
3556 * [I] nSubItem : subitem index
3557 * [I] pos : item position in client coordinates
3558 * [I] cdmode : custom draw mode
3564 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3566 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3567 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3568 static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3569 DWORD cdsubitemmode = CDRF_DODEFAULT;
3570 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3571 NMLVCUSTOMDRAW nmlvcd;
3575 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3577 /* get information needed for drawing the item */
3578 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3579 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3580 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3581 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3582 lvItem.iItem = nItem;
3583 lvItem.iSubItem = nSubItem;
3586 lvItem.cchTextMax = DISP_TEXT_SIZE;
3587 lvItem.pszText = szDispText;
3588 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3589 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3590 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3591 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3592 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3594 /* now check if we need to update the focus rectangle */
3595 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3597 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3598 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3599 OffsetRect(&rcBox, pos.x, pos.y);
3600 OffsetRect(&rcState, pos.x, pos.y);
3601 OffsetRect(&rcIcon, pos.x, pos.y);
3602 OffsetRect(&rcLabel, pos.x, pos.y);
3603 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3604 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3606 /* fill in the custom draw structure */
3607 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3609 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3610 if (cdmode & CDRF_NOTIFYITEMDRAW)
3611 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3612 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3613 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3614 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3615 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3617 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3618 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3620 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3621 prepaint_setup(infoPtr, hdc, &nmlvcd);
3623 /* in full row select, subitems, will just use main item's colors */
3624 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3625 nmlvcd.clrTextBk = CLR_NONE;
3628 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3630 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3633 TRACE("uStateImage=%d\n", uStateImage);
3634 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3639 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3640 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3642 TRACE("iImage=%d\n", lvItem.iImage);
3643 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3644 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3647 /* Don't bother painting item being edited */
3648 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3650 /* draw the selection background, if we're drawing the main item */
3654 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3655 rcSelect.right = rcBox.right;
3657 if (nmlvcd.clrTextBk != CLR_NONE)
3658 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3659 if(lprcFocus) *lprcFocus = rcSelect;
3662 /* figure out the text drawing flags */
3663 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3664 if (uView == LVS_ICON)
3665 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3668 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3670 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3671 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3672 default: uFormat |= DT_LEFT;
3675 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3677 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3678 else rcLabel.left += LABEL_HOR_PADDING;
3680 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3681 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3684 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3685 notify_postpaint(infoPtr, &nmlvcd);
3691 * Draws listview items when in owner draw mode.
3694 * [I] infoPtr : valid pointer to the listview structure
3695 * [I] hdc : device context handle
3700 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3702 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3703 DWORD cditemmode = CDRF_DODEFAULT;
3704 NMLVCUSTOMDRAW nmlvcd;
3705 POINT Origin, Position;
3711 ZeroMemory(&dis, sizeof(dis));
3713 /* Get scroll info once before loop */
3714 LISTVIEW_GetOrigin(infoPtr, &Origin);
3716 /* iterate through the invalidated rows */
3717 while(iterator_next(i))
3719 item.iItem = i->nItem;
3721 item.mask = LVIF_PARAM | LVIF_STATE;
3722 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3723 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3725 dis.CtlType = ODT_LISTVIEW;
3727 dis.itemID = item.iItem;
3728 dis.itemAction = ODA_DRAWENTIRE;
3730 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3731 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3732 dis.hwndItem = infoPtr->hwndSelf;
3734 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3735 dis.rcItem.left = Position.x + Origin.x;
3736 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3737 dis.rcItem.top = Position.y + Origin.y;
3738 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3739 dis.itemData = item.lParam;
3741 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3744 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3745 * structure for the rest. of the paint cycle
3747 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3748 if (cdmode & CDRF_NOTIFYITEMDRAW)
3749 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3751 if (!(cditemmode & CDRF_SKIPDEFAULT))
3753 prepaint_setup (infoPtr, hdc, &nmlvcd);
3754 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3757 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3758 notify_postpaint(infoPtr, &nmlvcd);
3764 * Draws listview items when in report display mode.
3767 * [I] infoPtr : valid pointer to the listview structure
3768 * [I] hdc : device context handle
3769 * [I] cdmode : custom draw mode
3774 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3777 RECT rcClip, rcItem;
3778 POINT Origin, Position;
3784 /* figure out what to draw */
3785 rgntype = GetClipBox(hdc, &rcClip);
3786 if (rgntype == NULLREGION) return;
3788 /* Get scroll info once before loop */
3789 LISTVIEW_GetOrigin(infoPtr, &Origin);
3791 /* narrow down the columns we need to paint */
3792 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3794 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3795 if (rcItem.right + Origin.x >= rcClip.left) break;
3797 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3799 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3800 if (rcItem.left + Origin.x < rcClip.right) break;
3802 iterator_rangeitems(&j, colRange);
3804 /* in full row select, we _have_ to draw the main item */
3805 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3808 /* iterate through the invalidated rows */
3809 while(iterator_next(i))
3811 /* iterate through the invalidated columns */
3812 while(iterator_next(&j))
3814 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3815 Position.x += Origin.x;
3816 Position.y += Origin.y;
3818 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3820 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3822 rcItem.bottom = infoPtr->nItemHeight;
3823 OffsetRect(&rcItem, Position.x, Position.y);
3824 if (!RectVisible(hdc, &rcItem)) continue;
3827 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3830 iterator_destroy(&j);
3835 * Draws listview items when in list display mode.
3838 * [I] infoPtr : valid pointer to the listview structure
3839 * [I] hdc : device context handle
3840 * [I] cdmode : custom draw mode
3845 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3847 POINT Origin, Position;
3849 /* Get scroll info once before loop */
3850 LISTVIEW_GetOrigin(infoPtr, &Origin);
3852 while(iterator_prev(i))
3854 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3855 Position.x += Origin.x;
3856 Position.y += Origin.y;
3858 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3865 * Draws listview items.
3868 * [I] infoPtr : valid pointer to the listview structure
3869 * [I] hdc : device context handle
3874 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3876 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3877 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3878 NMLVCUSTOMDRAW nmlvcd;
3885 LISTVIEW_DUMP(infoPtr);
3887 infoPtr->bIsDrawing = TRUE;
3889 /* save dc values we're gonna trash while drawing */
3890 hOldFont = SelectObject(hdc, infoPtr->hFont);
3891 oldBkMode = GetBkMode(hdc);
3892 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3893 oldTextColor = GetTextColor(hdc);
3895 oldClrTextBk = infoPtr->clrTextBk;
3896 oldClrText = infoPtr->clrText;
3898 infoPtr->cditemmode = CDRF_DODEFAULT;
3900 GetClientRect(infoPtr->hwndSelf, &rcClient);
3901 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3902 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3903 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3904 prepaint_setup(infoPtr, hdc, &nmlvcd);
3906 /* Use these colors to draw the items */
3907 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3908 infoPtr->clrText = nmlvcd.clrText;
3910 /* nothing to draw */
3911 if(infoPtr->nItemCount == 0) goto enddraw;
3913 /* figure out what we need to draw */
3914 iterator_visibleitems(&i, infoPtr, hdc);
3916 /* send cache hint notification */
3917 if (infoPtr->dwStyle & LVS_OWNERDATA)
3919 RANGE range = iterator_range(&i);
3922 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3923 nmlv.iFrom = range.lower;
3924 nmlv.iTo = range.upper - 1;
3925 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3928 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3929 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3932 if (uView == LVS_REPORT)
3933 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3934 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3935 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3937 /* if we have a focus rect, draw it */
3938 if (infoPtr->bFocus)
3939 DrawFocusRect(hdc, &infoPtr->rcFocus);
3941 iterator_destroy(&i);
3944 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3945 notify_postpaint(infoPtr, &nmlvcd);
3947 infoPtr->clrTextBk = oldClrTextBk;
3948 infoPtr->clrText = oldClrText;
3950 SelectObject(hdc, hOldFont);
3951 SetBkMode(hdc, oldBkMode);
3952 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3953 SetTextColor(hdc, oldTextColor);
3954 infoPtr->bIsDrawing = FALSE;
3960 * Calculates the approximate width and height of a given number of items.
3963 * [I] infoPtr : valid pointer to the listview structure
3964 * [I] nItemCount : number of items
3965 * [I] wWidth : width
3966 * [I] wHeight : height
3969 * Returns a DWORD. The width in the low word and the height in high word.
3971 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3972 WORD wWidth, WORD wHeight)
3974 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3975 INT nItemCountPerColumn = 1;
3976 INT nColumnCount = 0;
3977 DWORD dwViewRect = 0;
3979 if (nItemCount == -1)
3980 nItemCount = infoPtr->nItemCount;
3982 if (uView == LVS_LIST)
3984 if (wHeight == 0xFFFF)
3986 /* use current height */
3987 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3990 if (wHeight < infoPtr->nItemHeight)
3991 wHeight = infoPtr->nItemHeight;
3995 if (infoPtr->nItemHeight > 0)
3997 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3998 if (nItemCountPerColumn == 0)
3999 nItemCountPerColumn = 1;
4001 if (nItemCount % nItemCountPerColumn != 0)
4002 nColumnCount = nItemCount / nItemCountPerColumn;
4004 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4008 /* Microsoft padding magic */
4009 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4010 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4012 dwViewRect = MAKELONG(wWidth, wHeight);
4014 else if (uView == LVS_REPORT)
4018 if (infoPtr->nItemCount > 0)
4020 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4021 wWidth = rcBox.right - rcBox.left;
4022 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4026 /* use current height and width */
4027 if (wHeight == 0xffff)
4028 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4029 if (wWidth == 0xffff)
4030 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4033 dwViewRect = MAKELONG(wWidth, wHeight);
4035 else if (uView == LVS_SMALLICON)
4036 FIXME("uView == LVS_SMALLICON: not implemented\n");
4037 else if (uView == LVS_ICON)
4038 FIXME("uView == LVS_ICON: not implemented\n");
4046 * Create a drag image list for the specified item.
4049 * [I] infoPtr : valid pointer to the listview structure
4050 * [I] iItem : index of item
4051 * [O] lppt : Upperr-left corner of the image
4054 * Returns a handle to the image list if successful, NULL otherwise.
4056 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4062 HBITMAP hbmp, hOldbmp;
4063 HIMAGELIST dragList = 0;
4064 TRACE("iItem=%d Count=%d \n", iItem, infoPtr->nItemCount);
4066 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4069 rcItem.left = LVIR_BOUNDS;
4070 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4073 lppt->x = rcItem.left;
4074 lppt->y = rcItem.top;
4076 size.cx = rcItem.right - rcItem.left;
4077 size.cy = rcItem.bottom - rcItem.top;
4079 hdcOrig = GetDC(infoPtr->hwndSelf);
4080 hdc = CreateCompatibleDC(hdcOrig);
4081 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4082 hOldbmp = SelectObject(hdc, hbmp);
4084 rcItem.left = rcItem.top = 0;
4085 rcItem.right = size.cx;
4086 rcItem.bottom = size.cy;
4087 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4090 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4092 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4093 SelectObject(hdc, hOldbmp);
4094 ImageList_Add(dragList, hbmp, 0);
4097 SelectObject(hdc, hOldbmp);
4101 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4103 TRACE("ret=%p\n", dragList);
4111 * Removes all listview items and subitems.
4114 * [I] infoPtr : valid pointer to the listview structure
4120 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4123 HDPA hdpaSubItems = NULL;
4130 /* we do it directly, to avoid notifications */
4131 ranges_clear(infoPtr->selectionRanges);
4132 infoPtr->nSelectionMark = -1;
4133 infoPtr->nFocusedItem = -1;
4134 SetRectEmpty(&infoPtr->rcFocus);
4135 /* But we are supposed to leave nHotItem as is! */
4138 /* send LVN_DELETEALLITEMS notification */
4139 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4141 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4143 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4145 /* send LVN_DELETEITEM notification, if not suppressed */
4146 if (!bSuppress) notify_deleteitem(infoPtr, i);
4147 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4149 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4150 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4152 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4153 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4156 DPA_Destroy(hdpaSubItems);
4157 DPA_DeletePtr(infoPtr->hdpaItems, i);
4159 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4160 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4161 infoPtr->nItemCount --;
4164 LISTVIEW_UpdateScroll(infoPtr);
4166 LISTVIEW_InvalidateList(infoPtr);
4173 * Scrolls, and updates the columns, when a column is changing width.
4176 * [I] infoPtr : valid pointer to the listview structure
4177 * [I] nColumn : column to scroll
4178 * [I] dx : amount of scroll, in pixels
4183 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4185 COLUMN_INFO *lpColumnInfo;
4189 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4190 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4191 rcCol = lpColumnInfo->rcHeader;
4192 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4193 rcCol.left = rcCol.right;
4195 /* ajust the other columns */
4196 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4198 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4199 lpColumnInfo->rcHeader.left += dx;
4200 lpColumnInfo->rcHeader.right += dx;
4203 /* do not update screen if not in report mode */
4204 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4206 /* if we have a focus, must first erase the focus rect */
4207 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4209 /* Need to reset the item width when inserting a new column */
4210 infoPtr->nItemWidth += dx;
4212 LISTVIEW_UpdateScroll(infoPtr);
4214 /* scroll to cover the deleted column, and invalidate for redraw */
4215 rcOld = infoPtr->rcList;
4216 rcOld.left = rcCol.left;
4217 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4219 /* we can restore focus now */
4220 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4225 * Removes a column from the listview control.
4228 * [I] infoPtr : valid pointer to the listview structure
4229 * [I] nColumn : column index
4235 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4239 TRACE("nColumn=%d\n", nColumn);
4241 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4242 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4244 /* While the MSDN specifically says that column zero should not be deleted,
4245 what actually happens is that the column itself is deleted but no items or subitems
4249 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4251 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4254 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4255 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4257 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4259 SUBITEM_INFO *lpSubItem, *lpDelItem;
4261 INT nItem, nSubItem, i;
4263 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4265 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4268 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4270 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4271 if (lpSubItem->iSubItem == nColumn)
4274 lpDelItem = lpSubItem;
4276 else if (lpSubItem->iSubItem > nColumn)
4278 lpSubItem->iSubItem--;
4282 /* if we found our subitem, zapp it */
4286 if (is_textW(lpDelItem->hdr.pszText))
4287 Free(lpDelItem->hdr.pszText);
4292 /* free dpa memory */
4293 DPA_DeletePtr(hdpaSubItems, nSubItem);
4298 /* update the other column info */
4299 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4300 LISTVIEW_InvalidateList(infoPtr);
4302 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4309 * Invalidates the listview after an item's insertion or deletion.
4312 * [I] infoPtr : valid pointer to the listview structure
4313 * [I] nItem : item index
4314 * [I] dir : -1 if deleting, 1 if inserting
4319 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4321 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4322 INT nPerCol, nItemCol, nItemRow;
4326 /* if we don't refresh, what's the point of scrolling? */
4327 if (!is_redrawing(infoPtr)) return;
4329 assert (abs(dir) == 1);
4331 /* arrange icons if autoarrange is on */
4332 if (is_autoarrange(infoPtr))
4334 BOOL arrange = TRUE;
4335 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4336 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4337 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4340 /* scrollbars need updating */
4341 LISTVIEW_UpdateScroll(infoPtr);
4343 /* figure out the item's position */
4344 if (uView == LVS_REPORT)
4345 nPerCol = infoPtr->nItemCount + 1;
4346 else if (uView == LVS_LIST)
4347 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4348 else /* LVS_ICON, or LVS_SMALLICON */
4351 nItemCol = nItem / nPerCol;
4352 nItemRow = nItem % nPerCol;
4353 LISTVIEW_GetOrigin(infoPtr, &Origin);
4355 /* move the items below up a slot */
4356 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4357 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4358 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4359 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4360 OffsetRect(&rcScroll, Origin.x, Origin.y);
4361 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4362 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4364 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4365 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4366 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4369 /* report has only that column, so we're done */
4370 if (uView == LVS_REPORT) return;
4372 /* now for LISTs, we have to deal with the columns to the right */
4373 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4375 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4376 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4377 OffsetRect(&rcScroll, Origin.x, Origin.y);
4378 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4379 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4380 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4385 * Removes an item from the listview control.
4388 * [I] infoPtr : valid pointer to the listview structure
4389 * [I] nItem : item index
4395 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4397 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4400 TRACE("(nItem=%d)\n", nItem);
4402 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4404 /* remove selection, and focus */
4406 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4407 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4409 /* send LVN_DELETEITEM notification. */
4410 notify_deleteitem(infoPtr, nItem);
4412 /* we need to do this here, because we'll be deleting stuff */
4413 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4414 LISTVIEW_InvalidateItem(infoPtr, nItem);
4416 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4422 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4423 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4425 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4426 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4429 DPA_Destroy(hdpaSubItems);
4432 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4434 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4435 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4438 infoPtr->nItemCount--;
4439 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4441 /* now is the invalidation fun */
4442 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4449 * Callback implementation for editlabel control
4452 * [I] infoPtr : valid pointer to the listview structure
4453 * [I] pszText : modified text
4454 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4460 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4462 NMLVDISPINFOW dispInfo;
4464 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4466 ZeroMemory(&dispInfo, sizeof(dispInfo));
4467 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4468 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4469 dispInfo.item.iSubItem = 0;
4470 dispInfo.item.stateMask = ~0;
4471 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4472 /* add the text from the edit in */
4473 dispInfo.item.mask |= LVIF_TEXT;
4474 dispInfo.item.pszText = pszText;
4475 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4477 /* Do we need to update the Item Text */
4478 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4479 if (!pszText) return TRUE;
4481 ZeroMemory(&dispInfo, sizeof(dispInfo));
4482 dispInfo.item.mask = LVIF_TEXT;
4483 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4484 dispInfo.item.iSubItem = 0;
4485 dispInfo.item.pszText = pszText;
4486 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4487 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4492 * Begin in place editing of specified list view item
4495 * [I] infoPtr : valid pointer to the listview structure
4496 * [I] nItem : item index
4497 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4503 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4505 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4506 NMLVDISPINFOW dispInfo;
4509 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4511 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4512 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4514 infoPtr->nEditLabelItem = nItem;
4516 /* Is the EditBox still there, if so remove it */
4517 if(infoPtr->hwndEdit != 0)
4519 SetFocus(infoPtr->hwndSelf);
4520 infoPtr->hwndEdit = 0;
4523 LISTVIEW_SetSelection(infoPtr, nItem);
4524 LISTVIEW_SetItemFocus(infoPtr, nItem);
4525 LISTVIEW_InvalidateItem(infoPtr, nItem);
4527 rect.left = LVIR_LABEL;
4528 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4530 ZeroMemory(&dispInfo, sizeof(dispInfo));
4531 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4532 dispInfo.item.iItem = nItem;
4533 dispInfo.item.iSubItem = 0;
4534 dispInfo.item.stateMask = ~0;
4535 dispInfo.item.pszText = szDispText;
4536 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4537 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4539 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4540 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4541 if (!infoPtr->hwndEdit) return 0;
4543 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4545 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4546 infoPtr->hwndEdit = 0;
4550 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4551 SetFocus(infoPtr->hwndEdit);
4552 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4553 return infoPtr->hwndEdit;
4559 * Ensures the specified item is visible, scrolling into view if necessary.
4562 * [I] infoPtr : valid pointer to the listview structure
4563 * [I] nItem : item index
4564 * [I] bPartial : partially or entirely visible
4570 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4572 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4573 INT nScrollPosHeight = 0;
4574 INT nScrollPosWidth = 0;
4575 INT nHorzAdjust = 0;
4576 INT nVertAdjust = 0;
4579 RECT rcItem, rcTemp;
4581 rcItem.left = LVIR_BOUNDS;
4582 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4584 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4586 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4588 /* scroll left/right, but in LVS_REPORT mode */
4589 if (uView == LVS_LIST)
4590 nScrollPosWidth = infoPtr->nItemWidth;
4591 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4592 nScrollPosWidth = 1;
4594 if (rcItem.left < infoPtr->rcList.left)
4597 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4602 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4606 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4608 /* scroll up/down, but not in LVS_LIST mode */
4609 if (uView == LVS_REPORT)
4610 nScrollPosHeight = infoPtr->nItemHeight;
4611 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4612 nScrollPosHeight = 1;
4614 if (rcItem.top < infoPtr->rcList.top)
4617 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4622 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4626 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4628 if (nScrollPosWidth)
4630 INT diff = nHorzDiff / nScrollPosWidth;
4631 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4632 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4635 if (nScrollPosHeight)
4637 INT diff = nVertDiff / nScrollPosHeight;
4638 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4639 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4647 * Searches for an item with specific characteristics.
4650 * [I] hwnd : window handle
4651 * [I] nStart : base item index
4652 * [I] lpFindInfo : item information to look for
4655 * SUCCESS : index of item
4658 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4659 const LVFINDINFOW *lpFindInfo)
4661 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4662 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4663 BOOL bWrap = FALSE, bNearest = FALSE;
4664 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4665 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4666 POINT Position, Destination;
4669 if (!lpFindInfo || nItem < 0) return -1;
4672 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4674 lvItem.mask |= LVIF_TEXT;
4675 lvItem.pszText = szDispText;
4676 lvItem.cchTextMax = DISP_TEXT_SIZE;
4679 if (lpFindInfo->flags & LVFI_WRAP)
4682 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4683 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4688 LISTVIEW_GetOrigin(infoPtr, &Origin);
4689 Destination.x = lpFindInfo->pt.x - Origin.x;
4690 Destination.y = lpFindInfo->pt.y - Origin.y;
4691 switch(lpFindInfo->vkDirection)
4693 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4694 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4695 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4696 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4697 case VK_HOME: Destination.x = Destination.y = 0; break;
4698 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4699 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4701 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4702 Destination.x = rcArea.right;
4703 Destination.y = rcArea.bottom;
4705 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4710 /* if LVFI_PARAM is specified, all other flags are ignored */
4711 if (lpFindInfo->flags & LVFI_PARAM)
4713 lvItem.mask |= LVIF_PARAM;
4715 lvItem.mask &= ~LVIF_TEXT;
4719 for (; nItem < nLast; nItem++)
4721 lvItem.iItem = nItem;
4722 lvItem.iSubItem = 0;
4723 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4725 if (lvItem.mask & LVIF_PARAM)
4727 if (lpFindInfo->lParam == lvItem.lParam)
4733 if (lvItem.mask & LVIF_TEXT)
4735 if (lpFindInfo->flags & LVFI_PARTIAL)
4737 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4741 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4745 if (!bNearest) return nItem;
4747 /* This is very inefficient. To do a good job here,
4748 * we need a sorted array of (x,y) item positions */
4749 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4751 /* compute the distance^2 to the destination */
4752 xdist = Destination.x - Position.x;
4753 ydist = Destination.y - Position.y;
4754 dist = xdist * xdist + ydist * ydist;
4756 /* remember the distance, and item if it's closer */
4760 nNearestItem = nItem;
4767 nLast = min(nStart + 1, infoPtr->nItemCount);
4772 return nNearestItem;
4777 * Searches for an item with specific characteristics.
4780 * [I] hwnd : window handle
4781 * [I] nStart : base item index
4782 * [I] lpFindInfo : item information to look for
4785 * SUCCESS : index of item
4788 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4789 const LVFINDINFOA *lpFindInfo)
4791 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4795 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4796 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4797 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4798 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4804 * Retrieves the background image of the listview control.
4807 * [I] infoPtr : valid pointer to the listview structure
4808 * [O] lpBkImage : background image attributes
4814 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4816 /* FIXME (listview, "empty stub!\n"); */
4822 * Retrieves column attributes.
4825 * [I] infoPtr : valid pointer to the listview structure
4826 * [I] nColumn : column index
4827 * [IO] lpColumn : column information
4828 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4829 * otherwise it is in fact a LPLVCOLUMNA
4835 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4837 COLUMN_INFO *lpColumnInfo;
4840 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4841 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4843 /* initialize memory */
4844 ZeroMemory(&hdi, sizeof(hdi));
4846 if (lpColumn->mask & LVCF_TEXT)
4848 hdi.mask |= HDI_TEXT;
4849 hdi.pszText = lpColumn->pszText;
4850 hdi.cchTextMax = lpColumn->cchTextMax;
4853 if (lpColumn->mask & LVCF_IMAGE)
4854 hdi.mask |= HDI_IMAGE;
4856 if (lpColumn->mask & LVCF_ORDER)
4857 hdi.mask |= HDI_ORDER;
4859 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4861 if (lpColumn->mask & LVCF_FMT)
4862 lpColumn->fmt = lpColumnInfo->fmt;
4864 if (lpColumn->mask & LVCF_WIDTH)
4865 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4867 if (lpColumn->mask & LVCF_IMAGE)
4868 lpColumn->iImage = hdi.iImage;
4870 if (lpColumn->mask & LVCF_ORDER)
4871 lpColumn->iOrder = hdi.iOrder;
4877 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4884 /* FIXME: little hack */
4885 for (i = 0; i < iCount; i++)
4893 * Retrieves the column width.
4896 * [I] infoPtr : valid pointer to the listview structure
4897 * [I] int : column index
4900 * SUCCESS : column width
4903 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4905 INT nColumnWidth = 0;
4908 TRACE("nColumn=%d\n", nColumn);
4910 /* we have a 'column' in LIST and REPORT mode only */
4911 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4914 nColumnWidth = infoPtr->nItemWidth;
4917 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
4918 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4919 nColumnWidth = rcHeader.right - rcHeader.left;
4923 TRACE("nColumnWidth=%d\n", nColumnWidth);
4924 return nColumnWidth;
4929 * In list or report display mode, retrieves the number of items that can fit
4930 * vertically in the visible area. In icon or small icon display mode,
4931 * retrieves the total number of visible items.
4934 * [I] infoPtr : valid pointer to the listview structure
4937 * Number of fully visible items.
4939 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4941 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4945 return infoPtr->nItemCount;
4947 return LISTVIEW_GetCountPerColumn(infoPtr);
4949 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4957 * Retrieves an image list handle.
4960 * [I] infoPtr : valid pointer to the listview structure
4961 * [I] nImageList : image list identifier
4964 * SUCCESS : image list handle
4967 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4971 case LVSIL_NORMAL: return infoPtr->himlNormal;
4972 case LVSIL_SMALL: return infoPtr->himlSmall;
4973 case LVSIL_STATE: return infoPtr->himlState;
4978 /* LISTVIEW_GetISearchString */
4982 * Retrieves item attributes.
4985 * [I] hwnd : window handle
4986 * [IO] lpLVItem : item info
4987 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4988 * if FALSE, the lpLVItem is a LPLVITEMA.
4991 * This is the internal 'GetItem' interface -- it tries to
4992 * be smart, and avoids text copies, if possible, by modifing
4993 * lpLVItem->pszText to point to the text string. Please note
4994 * that this is not always possible (e.g. OWNERDATA), so on
4995 * entry you *must* supply valid values for pszText, and cchTextMax.
4996 * The only difference to the documented interface is that upon
4997 * return, you should use *only* the lpLVItem->pszText, rather than
4998 * the buffer pointer you provided on input. Most code already does
4999 * that, so it's not a problem.
5000 * For the two cases when the text must be copied (that is,
5001 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5007 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5009 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5010 NMLVDISPINFOW dispInfo;
5016 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5018 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5021 if (lpLVItem->mask == 0) return TRUE;
5023 /* make a local copy */
5024 isubitem = lpLVItem->iSubItem;
5026 /* a quick optimization if all we're asked is the focus state
5027 * these queries are worth optimising since they are common,
5028 * and can be answered in constant time, without the heavy accesses */
5029 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5030 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5032 lpLVItem->state = 0;
5033 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5034 lpLVItem->state |= LVIS_FOCUSED;
5038 ZeroMemory(&dispInfo, sizeof(dispInfo));
5040 /* if the app stores all the data, handle it separately */
5041 if (infoPtr->dwStyle & LVS_OWNERDATA)
5043 dispInfo.item.state = 0;
5045 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5046 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5048 /* NOTE: copy only fields which we _know_ are initialized, some apps
5049 * depend on the uninitialized fields being 0 */
5050 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5051 dispInfo.item.iItem = lpLVItem->iItem;
5052 dispInfo.item.iSubItem = isubitem;
5053 if (lpLVItem->mask & LVIF_TEXT)
5055 dispInfo.item.pszText = lpLVItem->pszText;
5056 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5058 if (lpLVItem->mask & LVIF_STATE)
5059 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5060 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5061 dispInfo.item.stateMask = lpLVItem->stateMask;
5062 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5064 /* full size structure expected - _WIN32IE >= 0x560 */
5065 *lpLVItem = dispInfo.item;
5067 else if (lpLVItem->mask & LVIF_INDENT)
5069 /* indent member expected - _WIN32IE >= 0x300 */
5070 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5074 /* minimal structure expected */
5075 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5077 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5080 /* make sure lParam is zeroed out */
5081 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5083 /* we store only a little state, so if we're not asked, we're done */
5084 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5086 /* if focus is handled by us, report it */
5087 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5089 lpLVItem->state &= ~LVIS_FOCUSED;
5090 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5091 lpLVItem->state |= LVIS_FOCUSED;
5094 /* and do the same for selection, if we handle it */
5095 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5097 lpLVItem->state &= ~LVIS_SELECTED;
5098 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5099 lpLVItem->state |= LVIS_SELECTED;
5105 /* find the item and subitem structures before we proceed */
5106 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5107 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5112 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5113 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5116 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5121 pItemHdr = &lpItem->hdr;
5123 /* Do we need to query the state from the app? */
5124 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5126 dispInfo.item.mask |= LVIF_STATE;
5127 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5130 /* Do we need to enquire about the image? */
5131 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5132 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5134 dispInfo.item.mask |= LVIF_IMAGE;
5135 dispInfo.item.iImage = I_IMAGECALLBACK;
5138 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5139 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5141 dispInfo.item.mask |= LVIF_TEXT;
5142 dispInfo.item.pszText = lpLVItem->pszText;
5143 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5144 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5145 *dispInfo.item.pszText = '\0';
5148 /* If we don't have all the requested info, query the application */
5149 if (dispInfo.item.mask != 0)
5151 dispInfo.item.iItem = lpLVItem->iItem;
5152 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5153 dispInfo.item.lParam = lpItem->lParam;
5154 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5155 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5158 /* we should not store values for subitems */
5159 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5161 /* Now, handle the iImage field */
5162 if (dispInfo.item.mask & LVIF_IMAGE)
5164 lpLVItem->iImage = dispInfo.item.iImage;
5165 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5166 pItemHdr->iImage = dispInfo.item.iImage;
5168 else if (lpLVItem->mask & LVIF_IMAGE)
5170 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5171 lpLVItem->iImage = pItemHdr->iImage;
5173 lpLVItem->iImage = 0;
5176 /* The pszText field */
5177 if (dispInfo.item.mask & LVIF_TEXT)
5179 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5180 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5182 lpLVItem->pszText = dispInfo.item.pszText;
5184 else if (lpLVItem->mask & LVIF_TEXT)
5186 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5187 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5190 /* if this is a subitem, we're done */
5191 if (isubitem) return TRUE;
5193 /* Next is the lParam field */
5194 if (dispInfo.item.mask & LVIF_PARAM)
5196 lpLVItem->lParam = dispInfo.item.lParam;
5197 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5198 lpItem->lParam = dispInfo.item.lParam;
5200 else if (lpLVItem->mask & LVIF_PARAM)
5201 lpLVItem->lParam = lpItem->lParam;
5203 /* ... the state field (this one is different due to uCallbackmask) */
5204 if (lpLVItem->mask & LVIF_STATE)
5206 lpLVItem->state = lpItem->state;
5207 if (dispInfo.item.mask & LVIF_STATE)
5209 lpLVItem->state &= ~dispInfo.item.stateMask;
5210 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5212 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5214 lpLVItem->state &= ~LVIS_FOCUSED;
5215 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5216 lpLVItem->state |= LVIS_FOCUSED;
5218 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5220 lpLVItem->state &= ~LVIS_SELECTED;
5221 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5222 lpLVItem->state |= LVIS_SELECTED;
5226 /* and last, but not least, the indent field */
5227 if (lpLVItem->mask & LVIF_INDENT)
5228 lpLVItem->iIndent = lpItem->iIndent;
5235 * Retrieves item attributes.
5238 * [I] hwnd : window handle
5239 * [IO] lpLVItem : item info
5240 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5241 * if FALSE, the lpLVItem is a LPLVITEMA.
5244 * This is the external 'GetItem' interface -- it properly copies
5245 * the text in the provided buffer.
5251 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5256 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5259 pszText = lpLVItem->pszText;
5260 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5261 if (bResult && lpLVItem->pszText != pszText)
5262 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5263 lpLVItem->pszText = pszText;
5271 * Retrieves the position (upper-left) of the listview control item.
5272 * Note that for LVS_ICON style, the upper-left is that of the icon
5273 * and not the bounding box.
5276 * [I] infoPtr : valid pointer to the listview structure
5277 * [I] nItem : item index
5278 * [O] lpptPosition : coordinate information
5284 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5286 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5289 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5291 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5293 LISTVIEW_GetOrigin(infoPtr, &Origin);
5294 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5296 if (uView == LVS_ICON)
5298 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5299 lpptPosition->y += ICON_TOP_PADDING;
5301 lpptPosition->x += Origin.x;
5302 lpptPosition->y += Origin.y;
5304 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5311 * Retrieves the bounding rectangle for a listview control item.
5314 * [I] infoPtr : valid pointer to the listview structure
5315 * [I] nItem : item index
5316 * [IO] lprc : bounding rectangle coordinates
5317 * lprc->left specifies the portion of the item for which the bounding
5318 * rectangle will be retrieved.
5320 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5321 * including the icon and label.
5324 * * Experiment shows that native control returns:
5325 * * width = min (48, length of text line)
5326 * * .left = position.x - (width - iconsize.cx)/2
5327 * * .right = .left + width
5328 * * height = #lines of text * ntmHeight + icon height + 8
5329 * * .top = position.y - 2
5330 * * .bottom = .top + height
5331 * * separation between items .y = itemSpacing.cy - height
5332 * * .x = itemSpacing.cx - width
5333 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5336 * * Experiment shows that native control returns:
5337 * * width = iconSize.cx + 16
5338 * * .left = position.x - (width - iconsize.cx)/2
5339 * * .right = .left + width
5340 * * height = iconSize.cy + 4
5341 * * .top = position.y - 2
5342 * * .bottom = .top + height
5343 * * separation between items .y = itemSpacing.cy - height
5344 * * .x = itemSpacing.cx - width
5345 * LVIR_LABEL Returns the bounding rectangle of the item text.
5348 * * Experiment shows that native control returns:
5349 * * width = text length
5350 * * .left = position.x - width/2
5351 * * .right = .left + width
5352 * * height = ntmH * linecount + 2
5353 * * .top = position.y + iconSize.cy + 6
5354 * * .bottom = .top + height
5355 * * separation between items .y = itemSpacing.cy - height
5356 * * .x = itemSpacing.cx - width
5357 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5358 * rectangles, but excludes columns in report view.
5365 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5366 * upon whether the window has the focus currently and on whether the item
5367 * is the one with the focus. Ensure that the control's record of which
5368 * item has the focus agrees with the items' records.
5370 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5372 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5373 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5374 BOOL doLabel = TRUE, oversizedBox = FALSE;
5375 POINT Position, Origin;
5379 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5381 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5383 LISTVIEW_GetOrigin(infoPtr, &Origin);
5384 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5386 /* Be smart and try to figure out the minimum we have to do */
5387 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5388 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5389 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5390 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5391 oversizedBox = TRUE;
5393 /* get what we need from the item before hand, so we make
5394 * only one request. This can speed up things, if data
5395 * is stored on the app side */
5397 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5398 if (doLabel) lvItem.mask |= LVIF_TEXT;
5399 lvItem.iItem = nItem;
5400 lvItem.iSubItem = 0;
5401 lvItem.pszText = szDispText;
5402 lvItem.cchTextMax = DISP_TEXT_SIZE;
5403 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5404 /* we got the state already up, simulate it here, to avoid a reget */
5405 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5407 lvItem.mask |= LVIF_STATE;
5408 lvItem.stateMask = LVIS_FOCUSED;
5409 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5412 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5413 lprc->left = LVIR_BOUNDS;
5417 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5421 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5425 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5428 case LVIR_SELECTBOUNDS:
5429 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5430 UnionRect(lprc, lprc, &label_rect);
5434 WARN("Unknown value: %ld\n", lprc->left);
5438 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5440 TRACE(" rect=%s\n", debugrect(lprc));
5447 * Retrieves the spacing between listview control items.
5450 * [I] infoPtr : valid pointer to the listview structure
5451 * [IO] lprc : rectangle to receive the output
5452 * on input, lprc->top = nSubItem
5453 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5455 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5456 * not only those of the first column.
5457 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5463 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5468 if (!lprc) return FALSE;
5470 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5471 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5473 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5475 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5477 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5480 lvItem.iItem = nItem;
5481 lvItem.iSubItem = lprc->top;
5483 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5487 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5492 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5496 ERR("Unknown bounds=%ld\n", lprc->left);
5500 OffsetRect(lprc, Position.x, Position.y);
5507 * Retrieves the width of a label.
5510 * [I] infoPtr : valid pointer to the listview structure
5513 * SUCCESS : string width (in pixels)
5516 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5518 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5521 TRACE("(nItem=%d)\n", nItem);
5523 lvItem.mask = LVIF_TEXT;
5524 lvItem.iItem = nItem;
5525 lvItem.iSubItem = 0;
5526 lvItem.pszText = szDispText;
5527 lvItem.cchTextMax = DISP_TEXT_SIZE;
5528 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5530 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5535 * Retrieves the spacing between listview control items.
5538 * [I] infoPtr : valid pointer to the listview structure
5539 * [I] bSmall : flag for small or large icon
5542 * Horizontal + vertical spacing
5544 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5550 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5554 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5555 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5557 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5564 * Retrieves the state of a listview control item.
5567 * [I] infoPtr : valid pointer to the listview structure
5568 * [I] nItem : item index
5569 * [I] uMask : state mask
5572 * State specified by the mask.
5574 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5578 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5580 lvItem.iItem = nItem;
5581 lvItem.iSubItem = 0;
5582 lvItem.mask = LVIF_STATE;
5583 lvItem.stateMask = uMask;
5584 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5586 return lvItem.state & uMask;
5591 * Retrieves the text of a listview control item or subitem.
5594 * [I] hwnd : window handle
5595 * [I] nItem : item index
5596 * [IO] lpLVItem : item information
5597 * [I] isW : TRUE if lpLVItem is Unicode
5600 * SUCCESS : string length
5603 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5605 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5607 lpLVItem->mask = LVIF_TEXT;
5608 lpLVItem->iItem = nItem;
5609 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5611 return textlenT(lpLVItem->pszText, isW);
5616 * Searches for an item based on properties + relationships.
5619 * [I] infoPtr : valid pointer to the listview structure
5620 * [I] nItem : item index
5621 * [I] uFlags : relationship flag
5624 * SUCCESS : item index
5627 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5629 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5631 LVFINDINFOW lvFindInfo;
5632 INT nCountPerColumn;
5636 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5637 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5639 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5641 if (uFlags & LVNI_CUT)
5644 if (uFlags & LVNI_DROPHILITED)
5645 uMask |= LVIS_DROPHILITED;
5647 if (uFlags & LVNI_FOCUSED)
5648 uMask |= LVIS_FOCUSED;
5650 if (uFlags & LVNI_SELECTED)
5651 uMask |= LVIS_SELECTED;
5653 /* if we're asked for the focused item, that's only one,
5654 * so it's worth optimizing */
5655 if (uFlags & LVNI_FOCUSED)
5657 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5658 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5661 if (uFlags & LVNI_ABOVE)
5663 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5668 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5674 /* Special case for autoarrange - move 'til the top of a list */
5675 if (is_autoarrange(infoPtr))
5677 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5678 while (nItem - nCountPerRow >= 0)
5680 nItem -= nCountPerRow;
5681 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5686 lvFindInfo.flags = LVFI_NEARESTXY;
5687 lvFindInfo.vkDirection = VK_UP;
5688 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5689 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5691 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5696 else if (uFlags & LVNI_BELOW)
5698 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5700 while (nItem < infoPtr->nItemCount)
5703 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5709 /* Special case for autoarrange - move 'til the bottom of a list */
5710 if (is_autoarrange(infoPtr))
5712 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5713 while (nItem + nCountPerRow < infoPtr->nItemCount )
5715 nItem += nCountPerRow;
5716 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5721 lvFindInfo.flags = LVFI_NEARESTXY;
5722 lvFindInfo.vkDirection = VK_DOWN;
5723 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5724 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5726 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5731 else if (uFlags & LVNI_TOLEFT)
5733 if (uView == LVS_LIST)
5735 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5736 while (nItem - nCountPerColumn >= 0)
5738 nItem -= nCountPerColumn;
5739 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5743 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5745 /* Special case for autoarrange - move 'ti the beginning of a row */
5746 if (is_autoarrange(infoPtr))
5748 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5749 while (nItem % nCountPerRow > 0)
5752 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5757 lvFindInfo.flags = LVFI_NEARESTXY;
5758 lvFindInfo.vkDirection = VK_LEFT;
5759 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5760 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5762 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5767 else if (uFlags & LVNI_TORIGHT)
5769 if (uView == LVS_LIST)
5771 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5772 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5774 nItem += nCountPerColumn;
5775 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5779 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5781 /* Special case for autoarrange - move 'til the end of a row */
5782 if (is_autoarrange(infoPtr))
5784 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5785 while (nItem % nCountPerRow < nCountPerRow - 1 )
5788 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5793 lvFindInfo.flags = LVFI_NEARESTXY;
5794 lvFindInfo.vkDirection = VK_RIGHT;
5795 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5796 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5798 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5807 /* search by index */
5808 for (i = nItem; i < infoPtr->nItemCount; i++)
5810 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5818 /* LISTVIEW_GetNumberOfWorkAreas */
5822 * Retrieves the origin coordinates when in icon or small icon display mode.
5825 * [I] infoPtr : valid pointer to the listview structure
5826 * [O] lpptOrigin : coordinate information
5831 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5833 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5834 INT nHorzPos = 0, nVertPos = 0;
5835 SCROLLINFO scrollInfo;
5837 scrollInfo.cbSize = sizeof(SCROLLINFO);
5838 scrollInfo.fMask = SIF_POS;
5840 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5841 nHorzPos = scrollInfo.nPos;
5842 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5843 nVertPos = scrollInfo.nPos;
5845 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5847 lpptOrigin->x = infoPtr->rcList.left;
5848 lpptOrigin->y = infoPtr->rcList.top;
5849 if (uView == LVS_LIST)
5850 nHorzPos *= infoPtr->nItemWidth;
5851 else if (uView == LVS_REPORT)
5852 nVertPos *= infoPtr->nItemHeight;
5854 lpptOrigin->x -= nHorzPos;
5855 lpptOrigin->y -= nVertPos;
5857 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5862 * Retrieves the width of a string.
5865 * [I] hwnd : window handle
5866 * [I] lpszText : text string to process
5867 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5870 * SUCCESS : string width (in pixels)
5873 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5878 if (is_textT(lpszText, isW))
5880 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5881 HDC hdc = GetDC(infoPtr->hwndSelf);
5882 HFONT hOldFont = SelectObject(hdc, hFont);
5885 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5887 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5888 SelectObject(hdc, hOldFont);
5889 ReleaseDC(infoPtr->hwndSelf, hdc);
5891 return stringSize.cx;
5896 * Determines which listview item is located at the specified position.
5899 * [I] infoPtr : valid pointer to the listview structure
5900 * [IO] lpht : hit test information
5901 * [I] subitem : fill out iSubItem.
5902 * [I] select : return the index only if the hit selects the item
5905 * (mm 20001022): We must not allow iSubItem to be touched, for
5906 * an app might pass only a structure with space up to iItem!
5907 * (MS Office 97 does that for instance in the file open dialog)
5910 * SUCCESS : item index
5913 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5915 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5916 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5917 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5918 POINT Origin, Position, opt;
5923 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5927 if (subitem) lpht->iSubItem = 0;
5929 if (infoPtr->rcList.left > lpht->pt.x)
5930 lpht->flags |= LVHT_TOLEFT;
5931 else if (infoPtr->rcList.right < lpht->pt.x)
5932 lpht->flags |= LVHT_TORIGHT;
5934 if (infoPtr->rcList.top > lpht->pt.y)
5935 lpht->flags |= LVHT_ABOVE;
5936 else if (infoPtr->rcList.bottom < lpht->pt.y)
5937 lpht->flags |= LVHT_BELOW;
5939 TRACE("lpht->flags=0x%x\n", lpht->flags);
5940 if (lpht->flags) return -1;
5942 lpht->flags |= LVHT_NOWHERE;
5944 LISTVIEW_GetOrigin(infoPtr, &Origin);
5946 /* first deal with the large items */
5947 rcSearch.left = lpht->pt.x;
5948 rcSearch.top = lpht->pt.y;
5949 rcSearch.right = rcSearch.left + 1;
5950 rcSearch.bottom = rcSearch.top + 1;
5952 iterator_frameditems(&i, infoPtr, &rcSearch);
5953 iterator_next(&i); /* go to first item in the sequence */
5955 iterator_destroy(&i);
5957 TRACE("lpht->iItem=%d\n", iItem);
5958 if (iItem == -1) return -1;
5960 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5961 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5962 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5963 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5964 lvItem.iItem = iItem;
5965 lvItem.iSubItem = 0;
5966 lvItem.pszText = szDispText;
5967 lvItem.cchTextMax = DISP_TEXT_SIZE;
5968 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5969 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5971 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5972 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
5973 opt.x = lpht->pt.x - Position.x - Origin.x;
5974 opt.y = lpht->pt.y - Position.y - Origin.y;
5976 if (uView == LVS_REPORT)
5979 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5980 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5981 if (!PtInRect(&rcBounds, opt)) return -1;
5983 if (PtInRect(&rcIcon, opt))
5984 lpht->flags |= LVHT_ONITEMICON;
5985 else if (PtInRect(&rcLabel, opt))
5986 lpht->flags |= LVHT_ONITEMLABEL;
5987 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5988 lpht->flags |= LVHT_ONITEMSTATEICON;
5989 if (lpht->flags & LVHT_ONITEM)
5990 lpht->flags &= ~LVHT_NOWHERE;
5992 TRACE("lpht->flags=0x%x\n", lpht->flags);
5993 if (uView == LVS_REPORT && subitem)
5997 rcBounds.right = rcBounds.left;
5998 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6000 rcBounds.left = rcBounds.right;
6001 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6002 if (PtInRect(&rcBounds, opt))
6010 if (select && !(uView == LVS_REPORT &&
6011 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6012 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6014 if (uView == LVS_REPORT)
6016 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6017 UnionRect(&rcBounds, &rcBounds, &rcState);
6019 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6021 return lpht->iItem = iItem;
6025 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6026 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6027 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6028 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6029 their own sort proc. when sending LVM_SORTITEMS.
6032 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6034 LVS_SORTXXX must be specified,
6035 LVS_OWNERDRAW is not set,
6036 <item>.pszText is not LPSTR_TEXTCALLBACK.
6038 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6039 are sorted based on item text..."
6041 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6043 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6044 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6045 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6047 /* if we're sorting descending, negate the return value */
6048 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6053 * Inserts a new item in the listview control.
6056 * [I] infoPtr : valid pointer to the listview structure
6057 * [I] lpLVItem : item information
6058 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6061 * SUCCESS : new item index
6064 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6066 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6071 BOOL is_sorted, has_changed;
6074 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6076 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6078 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6079 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6081 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6083 if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6085 /* insert item in listview control data structure */
6086 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6087 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6089 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6090 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6092 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6093 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6094 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6095 if (nItem == -1) goto fail;
6096 infoPtr->nItemCount++;
6098 /* shift indices first so they don't get tangled */
6099 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6101 /* set the item attributes */
6102 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6104 /* full size structure expected - _WIN32IE >= 0x560 */
6107 else if (lpLVItem->mask & LVIF_INDENT)
6109 /* indent member expected - _WIN32IE >= 0x300 */
6110 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6114 /* minimal structure expected */
6115 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6118 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6119 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6121 /* if we're sorted, sort the list, and update the index */
6124 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6125 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6126 assert(nItem != -1);
6129 /* make room for the position, if we are in the right mode */
6130 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6132 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6134 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6136 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6141 /* send LVN_INSERTITEM notification */
6142 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6144 nmlv.lParam = lpItem->lParam;
6145 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6147 /* align items (set position of each item) */
6148 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6152 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6153 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6155 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6157 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6160 /* now is the invalidation fun */
6161 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6165 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6166 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6167 infoPtr->nItemCount--;
6169 DPA_DeletePtr(hdpaSubItems, 0);
6170 DPA_Destroy (hdpaSubItems);
6177 * Redraws a range of items.
6180 * [I] infoPtr : valid pointer to the listview structure
6181 * [I] nFirst : first item
6182 * [I] nLast : last item
6188 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6192 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6193 max(nFirst, nLast) >= infoPtr->nItemCount)
6196 for (i = nFirst; i <= nLast; i++)
6197 LISTVIEW_InvalidateItem(infoPtr, i);
6204 * Scroll the content of a listview.
6207 * [I] infoPtr : valid pointer to the listview structure
6208 * [I] dx : horizontal scroll amount in pixels
6209 * [I] dy : vertical scroll amount in pixels
6216 * If the control is in report mode (LVS_REPORT) the control can
6217 * be scrolled only in line increments. "dy" will be rounded to the
6218 * nearest number of pixels that are a whole line. Ex: if line height
6219 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6220 * is passed the the scroll will be 0. (per MSDN 7/2002)
6222 * For: (per experimentaion with native control and CSpy ListView)
6223 * LVS_ICON dy=1 = 1 pixel (vertical only)
6225 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6227 * LVS_LIST dx=1 = 1 column (horizontal only)
6228 * but will only scroll 1 column per message
6229 * no matter what the value.
6230 * dy must be 0 or FALSE returned.
6231 * LVS_REPORT dx=1 = 1 pixel
6235 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6237 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6239 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6240 dy /= infoPtr->nItemHeight;
6243 if (dy != 0) return FALSE;
6250 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6251 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6258 * Sets the background color.
6261 * [I] infoPtr : valid pointer to the listview structure
6262 * [I] clrBk : background color
6268 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6270 TRACE("(clrBk=%lx)\n", clrBk);
6272 if(infoPtr->clrBk != clrBk) {
6273 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6274 infoPtr->clrBk = clrBk;
6275 if (clrBk == CLR_NONE)
6276 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6278 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6279 LISTVIEW_InvalidateList(infoPtr);
6285 /* LISTVIEW_SetBkImage */
6287 /*** Helper for {Insert,Set}ColumnT *only* */
6288 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6290 if (lpColumn->mask & LVCF_FMT)
6292 /* format member is valid */
6293 lphdi->mask |= HDI_FORMAT;
6295 /* set text alignment (leftmost column must be left-aligned) */
6296 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6297 lphdi->fmt |= HDF_LEFT;
6298 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6299 lphdi->fmt |= HDF_RIGHT;
6300 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6301 lphdi->fmt |= HDF_CENTER;
6303 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6304 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6306 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6308 lphdi->fmt |= HDF_IMAGE;
6309 lphdi->iImage = I_IMAGECALLBACK;
6313 if (lpColumn->mask & LVCF_WIDTH)
6315 lphdi->mask |= HDI_WIDTH;
6316 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6318 /* make it fill the remainder of the controls width */
6322 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6324 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6325 lphdi->cxy += rcHeader.right - rcHeader.left;
6328 /* retrieve the layout of the header */
6329 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6330 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6332 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6335 lphdi->cxy = lpColumn->cx;
6338 if (lpColumn->mask & LVCF_TEXT)
6340 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6341 lphdi->fmt |= HDF_STRING;
6342 lphdi->pszText = lpColumn->pszText;
6343 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6346 if (lpColumn->mask & LVCF_IMAGE)
6348 lphdi->mask |= HDI_IMAGE;
6349 lphdi->iImage = lpColumn->iImage;
6352 if (lpColumn->mask & LVCF_ORDER)
6354 lphdi->mask |= HDI_ORDER;
6355 lphdi->iOrder = lpColumn->iOrder;
6362 * Inserts a new column.
6365 * [I] infoPtr : valid pointer to the listview structure
6366 * [I] nColumn : column index
6367 * [I] lpColumn : column information
6368 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6371 * SUCCESS : new column index
6374 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6375 const LVCOLUMNW *lpColumn, BOOL isW)
6377 COLUMN_INFO *lpColumnInfo;
6381 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6383 if (!lpColumn || nColumn < 0) return -1;
6384 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6386 ZeroMemory(&hdi, sizeof(HDITEMW));
6387 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6389 /* insert item in header control */
6390 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6391 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6392 (WPARAM)nColumn, (LPARAM)&hdi);
6393 if (nNewColumn == -1) return -1;
6394 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6396 /* create our own column info */
6397 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6398 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6400 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6401 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6403 /* now we have to actually adjust the data */
6404 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6406 SUBITEM_INFO *lpSubItem;
6410 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6412 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6413 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6415 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6416 if (lpSubItem->iSubItem >= nNewColumn)
6417 lpSubItem->iSubItem++;
6422 /* make space for the new column */
6423 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6428 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6431 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6439 * Sets the attributes of a header item.
6442 * [I] infoPtr : valid pointer to the listview structure
6443 * [I] nColumn : column index
6444 * [I] lpColumn : column attributes
6445 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6451 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6452 const LVCOLUMNW *lpColumn, BOOL isW)
6454 HDITEMW hdi, hdiget;
6457 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6459 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6461 ZeroMemory(&hdi, sizeof(HDITEMW));
6462 if (lpColumn->mask & LVCF_FMT)
6464 hdi.mask |= HDI_FORMAT;
6465 hdiget.mask = HDI_FORMAT;
6466 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6467 hdi.fmt = hdiget.fmt & HDF_STRING;
6469 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6471 /* set header item attributes */
6472 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6473 if (!bResult) return FALSE;
6475 if (lpColumn->mask & LVCF_FMT)
6477 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6478 int oldFmt = lpColumnInfo->fmt;
6480 lpColumnInfo->fmt = lpColumn->fmt;
6481 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6483 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6484 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6493 * Sets the column order array
6496 * [I] infoPtr : valid pointer to the listview structure
6497 * [I] iCount : number of elements in column order array
6498 * [I] lpiArray : pointer to column order array
6504 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6506 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6517 * Sets the width of a column
6520 * [I] infoPtr : valid pointer to the listview structure
6521 * [I] nColumn : column index
6522 * [I] cx : column width
6528 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6530 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6531 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6535 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6537 /* set column width only if in report or list mode */
6538 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6540 /* take care of invalid cx values */
6541 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6542 else if (uView == LVS_LIST && cx < 1) return FALSE;
6544 /* resize all columns if in LVS_LIST mode */
6545 if(uView == LVS_LIST)
6547 infoPtr->nItemWidth = cx;
6548 LISTVIEW_InvalidateList(infoPtr);
6552 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6554 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6559 lvItem.mask = LVIF_TEXT;
6561 lvItem.iSubItem = nColumn;
6562 lvItem.pszText = szDispText;
6563 lvItem.cchTextMax = DISP_TEXT_SIZE;
6564 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6566 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6567 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6568 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6570 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6571 max_cx += infoPtr->iconSize.cx;
6572 max_cx += TRAILING_LABEL_PADDING;
6575 /* autosize based on listview items width */
6576 if(cx == LVSCW_AUTOSIZE)
6578 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6580 /* if iCol is the last column make it fill the remainder of the controls width */
6581 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6586 LISTVIEW_GetOrigin(infoPtr, &Origin);
6587 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6589 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6593 /* Despite what the MS docs say, if this is not the last
6594 column, then MS resizes the column to the width of the
6595 largest text string in the column, including headers
6596 and items. This is different from LVSCW_AUTOSIZE in that
6597 LVSCW_AUTOSIZE ignores the header string length. */
6600 /* retrieve header text */
6601 hdi.mask = HDI_TEXT;
6602 hdi.cchTextMax = DISP_TEXT_SIZE;
6603 hdi.pszText = szDispText;
6604 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6606 HDC hdc = GetDC(infoPtr->hwndSelf);
6607 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6610 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6611 cx = size.cx + TRAILING_HEADER_PADDING;
6612 /* FIXME: Take into account the header image, if one is present */
6613 SelectObject(hdc, old_font);
6614 ReleaseDC(infoPtr->hwndSelf, hdc);
6616 cx = max (cx, max_cx);
6620 if (cx < 0) return FALSE;
6622 /* call header to update the column change */
6623 hdi.mask = HDI_WIDTH;
6625 TRACE("hdi.cxy=%d\n", hdi.cxy);
6626 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6630 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6633 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6636 HBITMAP hbm_im, hbm_mask, hbm_orig;
6638 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6639 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6642 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6643 ILC_COLOR | ILC_MASK, 2, 2);
6644 hdc_wnd = GetDC(infoPtr->hwndSelf);
6645 hdc = CreateCompatibleDC(hdc_wnd);
6646 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6647 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6648 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6650 rc.left = rc.top = 0;
6651 rc.right = GetSystemMetrics(SM_CXSMICON);
6652 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6654 hbm_orig = SelectObject(hdc, hbm_mask);
6655 FillRect(hdc, &rc, hbr_white);
6656 InflateRect(&rc, -3, -3);
6657 FillRect(hdc, &rc, hbr_black);
6659 SelectObject(hdc, hbm_im);
6660 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6661 SelectObject(hdc, hbm_orig);
6662 ImageList_Add(himl, hbm_im, hbm_mask);
6664 SelectObject(hdc, hbm_im);
6665 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6666 SelectObject(hdc, hbm_orig);
6667 ImageList_Add(himl, hbm_im, hbm_mask);
6669 DeleteObject(hbm_mask);
6670 DeleteObject(hbm_im);
6678 * Sets the extended listview style.
6681 * [I] infoPtr : valid pointer to the listview structure
6683 * [I] dwStyle : style
6686 * SUCCESS : previous style
6689 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6691 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6695 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6697 infoPtr->dwLvExStyle = dwStyle;
6699 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6701 HIMAGELIST himl = 0;
6702 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6703 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6704 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6712 * Sets the new hot cursor used during hot tracking and hover selection.
6715 * [I] infoPtr : valid pointer to the listview structure
6716 * [I} hCurosr : the new hot cursor handle
6719 * Returns the previous hot cursor
6721 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6723 HCURSOR oldCursor = infoPtr->hHotCursor;
6725 infoPtr->hHotCursor = hCursor;
6733 * Sets the hot item index.
6736 * [I] infoPtr : valid pointer to the listview structure
6737 * [I] iIndex : index
6740 * SUCCESS : previous hot item index
6741 * FAILURE : -1 (no hot item)
6743 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6745 INT iOldIndex = infoPtr->nHotItem;
6747 infoPtr->nHotItem = iIndex;
6755 * Sets the amount of time the cursor must hover over an item before it is selected.
6758 * [I] infoPtr : valid pointer to the listview structure
6759 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6762 * Returns the previous hover time
6764 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6766 DWORD oldHoverTime = infoPtr->dwHoverTime;
6768 infoPtr->dwHoverTime = dwHoverTime;
6770 return oldHoverTime;
6775 * Sets spacing for icons of LVS_ICON style.
6778 * [I] infoPtr : valid pointer to the listview structure
6779 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6780 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6783 * MAKELONG(oldcx, oldcy)
6785 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6787 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6788 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6790 TRACE("requested=(%d,%d)\n", cx, cy);
6792 /* this is supported only for LVS_ICON style */
6793 if (uView != LVS_ICON) return oldspacing;
6795 /* set to defaults, if instructed to */
6796 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6797 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6799 /* if 0 then compute width
6800 * FIXME: Should scan each item and determine max width of
6801 * icon or label, then make that the width */
6803 cx = infoPtr->iconSpacing.cx;
6805 /* if 0 then compute height */
6807 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6808 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6811 infoPtr->iconSpacing.cx = cx;
6812 infoPtr->iconSpacing.cy = cy;
6814 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6815 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6816 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6817 infoPtr->ntmHeight);
6819 /* these depend on the iconSpacing */
6820 LISTVIEW_UpdateItemSize(infoPtr);
6825 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6829 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6836 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6837 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6846 * [I] infoPtr : valid pointer to the listview structure
6847 * [I] nType : image list type
6848 * [I] himl : image list handle
6851 * SUCCESS : old image list
6854 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6856 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6857 INT oldHeight = infoPtr->nItemHeight;
6858 HIMAGELIST himlOld = 0;
6860 TRACE("(nType=%d, himl=%p\n", nType, himl);
6865 himlOld = infoPtr->himlNormal;
6866 infoPtr->himlNormal = himl;
6867 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6868 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6872 himlOld = infoPtr->himlSmall;
6873 infoPtr->himlSmall = himl;
6874 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6878 himlOld = infoPtr->himlState;
6879 infoPtr->himlState = himl;
6880 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6881 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6885 ERR("Unknown icon type=%d\n", nType);
6889 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6890 if (infoPtr->nItemHeight != oldHeight)
6891 LISTVIEW_UpdateScroll(infoPtr);
6898 * Preallocates memory (does *not* set the actual count of items !)
6901 * [I] infoPtr : valid pointer to the listview structure
6902 * [I] nItems : item count (projected number of items to allocate)
6903 * [I] dwFlags : update flags
6909 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6911 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6913 if (infoPtr->dwStyle & LVS_OWNERDATA)
6915 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6916 INT nOldCount = infoPtr->nItemCount;
6918 if (nItems < nOldCount)
6920 RANGE range = { nItems, nOldCount };
6921 ranges_del(infoPtr->selectionRanges, range);
6922 if (infoPtr->nFocusedItem >= nItems)
6924 infoPtr->nFocusedItem = -1;
6925 SetRectEmpty(&infoPtr->rcFocus);
6929 infoPtr->nItemCount = nItems;
6930 LISTVIEW_UpdateScroll(infoPtr);
6932 /* the flags are valid only in ownerdata report and list modes */
6933 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6935 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6936 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6938 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6939 LISTVIEW_InvalidateList(infoPtr);
6946 LISTVIEW_GetOrigin(infoPtr, &Origin);
6947 nFrom = min(nOldCount, nItems);
6948 nTo = max(nOldCount, nItems);
6950 if (uView == LVS_REPORT)
6953 rcErase.top = nFrom * infoPtr->nItemHeight;
6954 rcErase.right = infoPtr->nItemWidth;
6955 rcErase.bottom = nTo * infoPtr->nItemHeight;
6956 OffsetRect(&rcErase, Origin.x, Origin.y);
6957 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6958 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6962 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6964 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6965 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6966 rcErase.right = rcErase.left + infoPtr->nItemWidth;
6967 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6968 OffsetRect(&rcErase, Origin.x, Origin.y);
6969 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6970 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6972 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6974 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
6975 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6976 OffsetRect(&rcErase, Origin.x, Origin.y);
6977 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6978 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6984 /* According to MSDN for non-LVS_OWNERDATA this is just
6985 * a performance issue. The control allocates its internal
6986 * data structures for the number of items specified. It
6987 * cuts down on the number of memory allocations. Therefore
6988 * we will just issue a WARN here
6990 WARN("for non-ownerdata performance option not implemented.\n");
6998 * Sets the position of an item.
7001 * [I] infoPtr : valid pointer to the listview structure
7002 * [I] nItem : item index
7003 * [I] pt : coordinate
7009 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7011 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7014 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
7016 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7017 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7019 LISTVIEW_GetOrigin(infoPtr, &Origin);
7021 /* This point value seems to be an undocumented feature.
7022 * The best guess is that it means either at the origin,
7023 * or at true beginning of the list. I will assume the origin. */
7024 if ((pt.x == -1) && (pt.y == -1))
7027 if (uView == LVS_ICON)
7029 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7030 pt.y -= ICON_TOP_PADDING;
7035 infoPtr->bAutoarrange = FALSE;
7037 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7042 * Sets the state of one or many items.
7045 * [I] infoPtr : valid pointer to the listview structure
7046 * [I] nItem : item index
7047 * [I] lpLVItem : item or subitem info
7053 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7055 BOOL bResult = TRUE;
7058 lvItem.iItem = nItem;
7059 lvItem.iSubItem = 0;
7060 lvItem.mask = LVIF_STATE;
7061 lvItem.state = lpLVItem->state;
7062 lvItem.stateMask = lpLVItem->stateMask;
7063 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7067 /* apply to all items */
7068 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7069 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7072 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7075 *update selection mark
7077 * Investigation on windows 2k showed that selection mark was updated
7078 * whenever a new selection was made, but if the selected item was
7079 * unselected it was not updated.
7081 * we are probably still not 100% accurate, but this at least sets the
7082 * proper selection mark when it is needed
7085 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7086 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7089 infoPtr->nSelectionMark = -1;
7090 for (i = 0; i < infoPtr->nItemCount; i++)
7092 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7094 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7096 infoPtr->nSelectionMark = i;
7100 else if (ranges_contain(infoPtr->selectionRanges, i))
7102 infoPtr->nSelectionMark = i;
7113 * Sets the text of an item or subitem.
7116 * [I] hwnd : window handle
7117 * [I] nItem : item index
7118 * [I] lpLVItem : item or subitem info
7119 * [I] isW : TRUE if input is Unicode
7125 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7129 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7131 lvItem.iItem = nItem;
7132 lvItem.iSubItem = lpLVItem->iSubItem;
7133 lvItem.mask = LVIF_TEXT;
7134 lvItem.pszText = lpLVItem->pszText;
7135 lvItem.cchTextMax = lpLVItem->cchTextMax;
7137 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7139 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7144 * Set item index that marks the start of a multiple selection.
7147 * [I] infoPtr : valid pointer to the listview structure
7148 * [I] nIndex : index
7151 * Index number or -1 if there is no selection mark.
7153 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7155 INT nOldIndex = infoPtr->nSelectionMark;
7157 TRACE("(nIndex=%d)\n", nIndex);
7159 infoPtr->nSelectionMark = nIndex;
7166 * Sets the text background color.
7169 * [I] infoPtr : valid pointer to the listview structure
7170 * [I] clrTextBk : text background color
7176 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7178 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7180 if (infoPtr->clrTextBk != clrTextBk)
7182 infoPtr->clrTextBk = clrTextBk;
7183 LISTVIEW_InvalidateList(infoPtr);
7191 * Sets the text foreground color.
7194 * [I] infoPtr : valid pointer to the listview structure
7195 * [I] clrText : text color
7201 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7203 TRACE("(clrText=%lx)\n", clrText);
7205 if (infoPtr->clrText != clrText)
7207 infoPtr->clrText = clrText;
7208 LISTVIEW_InvalidateList(infoPtr);
7216 * Determines which listview item is located at the specified position.
7219 * [I] infoPtr : valid pointer to the listview structure
7220 * [I] hwndNewToolTip : handle to new ToolTip
7225 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7227 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7228 infoPtr->hwndToolTip = hwndNewToolTip;
7229 return hwndOldToolTip;
7232 /* LISTVIEW_SetUnicodeFormat */
7233 /* LISTVIEW_SetWorkAreas */
7237 * Callback internally used by LISTVIEW_SortItems()
7240 * [I] first : pointer to first ITEM_INFO to compare
7241 * [I] second : pointer to second ITEM_INFO to compare
7242 * [I] lParam : HWND of control
7245 * if first comes before second : negative
7246 * if first comes after second : positive
7247 * if first and second are equivalent : zero
7249 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7251 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7252 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7253 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7255 /* Forward the call to the client defined callback */
7256 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7261 * Sorts the listview items.
7264 * [I] infoPtr : valid pointer to the listview structure
7265 * [I] pfnCompare : application-defined value
7266 * [I] lParamSort : pointer to comparision callback
7272 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7274 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7277 LPVOID selectionMarkItem;
7281 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7283 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7285 if (!pfnCompare) return FALSE;
7286 if (!infoPtr->hdpaItems) return FALSE;
7288 /* if there are 0 or 1 items, there is no need to sort */
7289 if (infoPtr->nItemCount < 2) return TRUE;
7291 if (infoPtr->nFocusedItem >= 0)
7293 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7294 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7295 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7297 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7298 /* clear the lpItem->state for non-selected ones */
7299 /* remove the selection ranges */
7301 infoPtr->pfnCompare = pfnCompare;
7302 infoPtr->lParamSort = lParamSort;
7303 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7305 /* Adjust selections and indices so that they are the way they should
7306 * be after the sort (otherwise, the list items move around, but
7307 * whatever is at the item's previous original position will be
7310 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7311 for (i=0; i < infoPtr->nItemCount; i++)
7313 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7314 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7316 if (lpItem->state & LVIS_SELECTED)
7318 item.state = LVIS_SELECTED;
7319 item.stateMask = LVIS_SELECTED;
7320 LISTVIEW_SetItemState(infoPtr, i, &item);
7322 if (lpItem->state & LVIS_FOCUSED)
7324 infoPtr->nFocusedItem = i;
7325 lpItem->state &= ~LVIS_FOCUSED;
7328 if (selectionMarkItem != NULL)
7329 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7330 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7332 /* refresh the display */
7333 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7334 LISTVIEW_InvalidateList(infoPtr);
7341 * Updates an items or rearranges the listview control.
7344 * [I] infoPtr : valid pointer to the listview structure
7345 * [I] nItem : item index
7351 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7353 TRACE("(nItem=%d)\n", nItem);
7355 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7357 /* rearrange with default alignment style */
7358 if (is_autoarrange(infoPtr))
7359 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7361 LISTVIEW_InvalidateItem(infoPtr, nItem);
7369 * Creates the listview control.
7372 * [I] hwnd : window handle
7373 * [I] lpcs : the create parameters
7379 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7381 LISTVIEW_INFO *infoPtr;
7382 UINT uView = lpcs->style & LVS_TYPEMASK;
7385 TRACE("(lpcs=%p)\n", lpcs);
7387 /* initialize info pointer */
7388 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7389 if (!infoPtr) return -1;
7391 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7393 infoPtr->hwndSelf = hwnd;
7394 infoPtr->dwStyle = lpcs->style;
7395 /* determine the type of structures to use */
7396 infoPtr->hwndNotify = lpcs->hwndParent;
7397 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7398 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7400 /* initialize color information */
7401 infoPtr->clrBk = CLR_NONE;
7402 infoPtr->clrText = comctl32_color.clrWindowText;
7403 infoPtr->clrTextBk = CLR_DEFAULT;
7404 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7406 /* set default values */
7407 infoPtr->nFocusedItem = -1;
7408 infoPtr->nSelectionMark = -1;
7409 infoPtr->nHotItem = -1;
7410 infoPtr->bRedraw = TRUE;
7411 infoPtr->bNoItemMetrics = TRUE;
7412 infoPtr->bDoChangeNotify = TRUE;
7413 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7414 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7415 infoPtr->nEditLabelItem = -1;
7416 infoPtr->dwHoverTime = -1; /* default system hover time */
7418 /* get default font (icon title) */
7419 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7420 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7421 infoPtr->hFont = infoPtr->hDefaultFont;
7422 LISTVIEW_SaveTextMetrics(infoPtr);
7425 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7426 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7427 0, 0, 0, 0, hwnd, NULL,
7428 lpcs->hInstance, NULL);
7429 if (!infoPtr->hwndHeader) goto fail;
7431 /* set header unicode format */
7432 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7434 /* set header font */
7435 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7437 /* allocate memory for the data structure */
7438 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7439 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7440 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7441 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7442 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7444 /* initialize the icon sizes */
7445 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7446 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7448 /* init item size to avoid division by 0 */
7449 LISTVIEW_UpdateItemSize (infoPtr);
7451 if (uView == LVS_REPORT)
7453 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7455 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7459 /* set HDS_HIDDEN flag to hide the header bar */
7460 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7461 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7468 DestroyWindow(infoPtr->hwndHeader);
7469 ranges_destroy(infoPtr->selectionRanges);
7470 DPA_Destroy(infoPtr->hdpaItems);
7471 DPA_Destroy(infoPtr->hdpaPosX);
7472 DPA_Destroy(infoPtr->hdpaPosY);
7473 DPA_Destroy(infoPtr->hdpaColumns);
7480 * Erases the background of the listview control.
7483 * [I] infoPtr : valid pointer to the listview structure
7484 * [I] hdc : device context handle
7490 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7494 TRACE("(hdc=%p)\n", hdc);
7496 if (!GetClipBox(hdc, &rc)) return FALSE;
7498 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7504 * Helper function for LISTVIEW_[HV]Scroll *only*.
7505 * Performs vertical/horizontal scrolling by a give amount.
7508 * [I] infoPtr : valid pointer to the listview structure
7509 * [I] dx : amount of horizontal scroll
7510 * [I] dy : amount of vertical scroll
7512 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7514 /* now we can scroll the list */
7515 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7516 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7517 /* if we have focus, adjust rect */
7518 OffsetRect(&infoPtr->rcFocus, dx, dy);
7519 UpdateWindow(infoPtr->hwndSelf);
7524 * Performs vertical scrolling.
7527 * [I] infoPtr : valid pointer to the listview structure
7528 * [I] nScrollCode : scroll code
7529 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7530 * [I] hScrollWnd : scrollbar control window handle
7536 * SB_LINEUP/SB_LINEDOWN:
7537 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7538 * for LVS_REPORT is 1 line
7539 * for LVS_LIST cannot occur
7542 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7543 INT nScrollDiff, HWND hScrollWnd)
7545 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7546 INT nOldScrollPos, nNewScrollPos;
7547 SCROLLINFO scrollInfo;
7550 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7551 debugscrollcode(nScrollCode), nScrollDiff);
7553 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7555 scrollInfo.cbSize = sizeof(SCROLLINFO);
7556 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7558 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7560 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7562 nOldScrollPos = scrollInfo.nPos;
7563 switch (nScrollCode)
7569 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7573 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7577 nScrollDiff = -scrollInfo.nPage;
7581 nScrollDiff = scrollInfo.nPage;
7584 case SB_THUMBPOSITION:
7586 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7593 /* quit right away if pos isn't changing */
7594 if (nScrollDiff == 0) return 0;
7596 /* calculate new position, and handle overflows */
7597 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7598 if (nScrollDiff > 0) {
7599 if (nNewScrollPos < nOldScrollPos ||
7600 nNewScrollPos > scrollInfo.nMax)
7601 nNewScrollPos = scrollInfo.nMax;
7603 if (nNewScrollPos > nOldScrollPos ||
7604 nNewScrollPos < scrollInfo.nMin)
7605 nNewScrollPos = scrollInfo.nMin;
7608 /* set the new position, and reread in case it changed */
7609 scrollInfo.fMask = SIF_POS;
7610 scrollInfo.nPos = nNewScrollPos;
7611 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7613 /* carry on only if it really changed */
7614 if (nNewScrollPos == nOldScrollPos) return 0;
7616 /* now adjust to client coordinates */
7617 nScrollDiff = nOldScrollPos - nNewScrollPos;
7618 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7620 /* and scroll the window */
7621 scroll_list(infoPtr, 0, nScrollDiff);
7628 * Performs horizontal scrolling.
7631 * [I] infoPtr : valid pointer to the listview structure
7632 * [I] nScrollCode : scroll code
7633 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7634 * [I] hScrollWnd : scrollbar control window handle
7640 * SB_LINELEFT/SB_LINERIGHT:
7641 * for LVS_ICON, LVS_SMALLICON 1 pixel
7642 * for LVS_REPORT is 1 pixel
7643 * for LVS_LIST is 1 column --> which is a 1 because the
7644 * scroll is based on columns not pixels
7647 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7648 INT nScrollDiff, HWND hScrollWnd)
7650 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7651 INT nOldScrollPos, nNewScrollPos;
7652 SCROLLINFO scrollInfo;
7654 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7655 debugscrollcode(nScrollCode), nScrollDiff);
7657 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7659 scrollInfo.cbSize = sizeof(SCROLLINFO);
7660 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7662 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7664 nOldScrollPos = scrollInfo.nPos;
7666 switch (nScrollCode)
7680 nScrollDiff = -scrollInfo.nPage;
7684 nScrollDiff = scrollInfo.nPage;
7687 case SB_THUMBPOSITION:
7689 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7696 /* quit right away if pos isn't changing */
7697 if (nScrollDiff == 0) return 0;
7699 /* calculate new position, and handle overflows */
7700 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7701 if (nScrollDiff > 0) {
7702 if (nNewScrollPos < nOldScrollPos ||
7703 nNewScrollPos > scrollInfo.nMax)
7704 nNewScrollPos = scrollInfo.nMax;
7706 if (nNewScrollPos > nOldScrollPos ||
7707 nNewScrollPos < scrollInfo.nMin)
7708 nNewScrollPos = scrollInfo.nMin;
7711 /* set the new position, and reread in case it changed */
7712 scrollInfo.fMask = SIF_POS;
7713 scrollInfo.nPos = nNewScrollPos;
7714 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7716 /* carry on only if it really changed */
7717 if (nNewScrollPos == nOldScrollPos) return 0;
7719 if(uView == LVS_REPORT)
7720 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7722 /* now adjust to client coordinates */
7723 nScrollDiff = nOldScrollPos - nNewScrollPos;
7724 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7726 /* and scroll the window */
7727 scroll_list(infoPtr, nScrollDiff, 0);
7732 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7734 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7735 INT gcWheelDelta = 0;
7736 INT pulScrollLines = 3;
7737 SCROLLINFO scrollInfo;
7739 TRACE("(wheelDelta=%d)\n", wheelDelta);
7741 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7742 gcWheelDelta -= wheelDelta;
7744 scrollInfo.cbSize = sizeof(SCROLLINFO);
7745 scrollInfo.fMask = SIF_POS;
7752 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7753 * should be fixed in the future.
7755 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7756 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7760 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7762 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7763 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7764 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7769 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7780 * [I] infoPtr : valid pointer to the listview structure
7781 * [I] nVirtualKey : virtual key
7782 * [I] lKeyData : key data
7787 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7789 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7791 NMLVKEYDOWN nmKeyDown;
7793 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7795 /* send LVN_KEYDOWN notification */
7796 nmKeyDown.wVKey = nVirtualKey;
7797 nmKeyDown.flags = 0;
7798 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7800 switch (nVirtualKey)
7803 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7805 notify(infoPtr, NM_RETURN);
7806 notify(infoPtr, LVN_ITEMACTIVATE);
7811 if (infoPtr->nItemCount > 0)
7816 if (infoPtr->nItemCount > 0)
7817 nItem = infoPtr->nItemCount - 1;
7821 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7825 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7829 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7833 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7837 if (uView == LVS_REPORT)
7838 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7840 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7841 * LISTVIEW_GetCountPerRow(infoPtr);
7842 if(nItem < 0) nItem = 0;
7846 if (uView == LVS_REPORT)
7847 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7849 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7850 * LISTVIEW_GetCountPerRow(infoPtr);
7851 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7855 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7856 LISTVIEW_KeySelection(infoPtr, nItem);
7866 * [I] infoPtr : valid pointer to the listview structure
7871 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7875 /* if we did not have the focus, there's nothing to do */
7876 if (!infoPtr->bFocus) return 0;
7878 /* send NM_KILLFOCUS notification */
7879 notify(infoPtr, NM_KILLFOCUS);
7881 /* if we have a focus rectagle, get rid of it */
7882 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7884 /* set window focus flag */
7885 infoPtr->bFocus = FALSE;
7887 /* invalidate the selected items before reseting focus flag */
7888 LISTVIEW_InvalidateSelectedItems(infoPtr);
7896 * Track mouse/dragging
7899 * [I] infoPtr : valid pointer to the listview structure
7900 * [I] pt : mouse coordinate
7905 static LRESULT LISTVIEW_TrackMouse(LISTVIEW_INFO *infoPtr, POINT pt)
7907 INT cxDrag = GetSystemMetrics(SM_CXDRAG);
7908 INT cyDrag = GetSystemMetrics(SM_CYDRAG);
7914 r.top = pt.y - cyDrag;
7915 r.left = pt.x - cxDrag;
7916 r.bottom = pt.y + cyDrag;
7917 r.right = pt.x + cxDrag;
7919 SetCapture(infoPtr->hwndSelf);
7923 if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
7925 if (msg.message == WM_MOUSEMOVE)
7927 pt.x = (short)LOWORD(msg.lParam);
7928 pt.y = (short)HIWORD(msg.lParam);
7929 if (PtInRect(&r, pt))
7937 else if (msg.message >= WM_LBUTTONDOWN &&
7938 msg.message <= WM_RBUTTONDBLCLK)
7943 DispatchMessageW(&msg);
7946 if (GetCapture() != infoPtr->hwndSelf)
7957 * Processes double click messages (left mouse button).
7960 * [I] infoPtr : valid pointer to the listview structure
7961 * [I] wKey : key flag
7962 * [I] x,y : mouse coordinate
7967 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
7969 LVHITTESTINFO htInfo;
7971 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
7973 /* send NM_RELEASEDCAPTURE notification */
7974 notify(infoPtr, NM_RELEASEDCAPTURE);
7979 /* send NM_DBLCLK notification */
7980 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7981 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7983 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7984 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
7991 * Processes mouse down messages (left mouse button).
7994 * [I] infoPtr : valid pointer to the listview structure
7995 * [I] wKey : key flag
7996 * [I] x,y : mouse coordinate
8001 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8003 LVHITTESTINFO lvHitTestInfo;
8004 static BOOL bGroupSelect = TRUE;
8005 POINT pt = { x, y };
8008 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8010 /* send NM_RELEASEDCAPTURE notification */
8011 notify(infoPtr, NM_RELEASEDCAPTURE);
8013 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8015 /* set left button down flag */
8016 infoPtr->bLButtonDown = TRUE;
8018 lvHitTestInfo.pt.x = x;
8019 lvHitTestInfo.pt.y = y;
8021 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8022 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
8023 infoPtr->nEditLabelItem = -1;
8024 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8026 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8028 DWORD state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK) >> 12;
8029 if(state == 1 || state == 2)
8033 lvitem.state = state << 12;
8034 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8035 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8039 if (LISTVIEW_TrackMouse(infoPtr, lvHitTestInfo.pt))
8043 ZeroMemory(&nmlv, sizeof(nmlv));
8045 nmlv.ptAction.x = lvHitTestInfo.pt.x;
8046 nmlv.ptAction.y = lvHitTestInfo.pt.y;
8048 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
8053 if (infoPtr->dwStyle & LVS_SINGLESEL)
8055 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8056 infoPtr->nEditLabelItem = nItem;
8058 LISTVIEW_SetSelection(infoPtr, nItem);
8062 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8066 LISTVIEW_AddGroupSelection(infoPtr, nItem);
8067 LISTVIEW_SetItemFocus(infoPtr, nItem);
8068 infoPtr->nSelectionMark = nItem;
8074 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8075 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8077 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8078 infoPtr->nSelectionMark = nItem;
8081 else if (wKey & MK_CONTROL)
8085 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8087 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8088 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8089 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8090 infoPtr->nSelectionMark = nItem;
8092 else if (wKey & MK_SHIFT)
8094 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8098 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8099 infoPtr->nEditLabelItem = nItem;
8101 /* set selection (clears other pre-existing selections) */
8102 LISTVIEW_SetSelection(infoPtr, nItem);
8108 /* remove all selections */
8109 LISTVIEW_DeselectAll(infoPtr);
8118 * Processes mouse up messages (left mouse button).
8121 * [I] infoPtr : valid pointer to the listview structure
8122 * [I] wKey : key flag
8123 * [I] x,y : mouse coordinate
8128 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8130 LVHITTESTINFO lvHitTestInfo;
8132 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8134 if (!infoPtr->bLButtonDown) return 0;
8136 lvHitTestInfo.pt.x = x;
8137 lvHitTestInfo.pt.y = y;
8139 /* send NM_CLICK notification */
8140 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8141 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
8143 /* set left button flag */
8144 infoPtr->bLButtonDown = FALSE;
8146 /* if we clicked on a selected item, edit the label */
8147 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8148 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8155 * Destroys the listview control (called after WM_DESTROY).
8158 * [I] infoPtr : valid pointer to the listview structure
8163 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8167 /* delete all items */
8168 LISTVIEW_DeleteAllItems(infoPtr);
8170 /* destroy data structure */
8171 DPA_Destroy(infoPtr->hdpaItems);
8172 DPA_Destroy(infoPtr->hdpaPosX);
8173 DPA_Destroy(infoPtr->hdpaPosY);
8174 DPA_Destroy(infoPtr->hdpaColumns);
8175 ranges_destroy(infoPtr->selectionRanges);
8177 /* destroy image lists */
8178 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8180 if (infoPtr->himlNormal)
8181 ImageList_Destroy(infoPtr->himlNormal);
8182 if (infoPtr->himlSmall)
8183 ImageList_Destroy(infoPtr->himlSmall);
8184 if (infoPtr->himlState)
8185 ImageList_Destroy(infoPtr->himlState);
8188 /* destroy font, bkgnd brush */
8190 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8191 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8193 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8195 /* free listview info pointer*/
8203 * Handles notifications from header.
8206 * [I] infoPtr : valid pointer to the listview structure
8207 * [I] nCtrlId : control identifier
8208 * [I] lpnmh : notification information
8213 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8215 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8217 TRACE("(lpnmh=%p)\n", lpnmh);
8219 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8221 switch (lpnmh->hdr.code)
8225 case HDN_ITEMCHANGEDW:
8226 case HDN_ITEMCHANGEDA:
8228 COLUMN_INFO *lpColumnInfo;
8231 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8235 hdi.mask = HDI_WIDTH;
8236 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8240 cxy = lpnmh->pitem->cxy;
8242 /* determine how much we change since the last know position */
8243 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8244 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8247 RECT rcCol = lpColumnInfo->rcHeader;
8249 lpColumnInfo->rcHeader.right += dx;
8250 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8251 LISTVIEW_UpdateItemSize(infoPtr);
8252 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8254 /* this trick works for left aligned columns only */
8255 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8257 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
8258 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
8260 rcCol.top = infoPtr->rcList.top;
8261 rcCol.bottom = infoPtr->rcList.bottom;
8262 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8268 case HDN_ITEMCLICKW:
8269 case HDN_ITEMCLICKA:
8271 /* Handle sorting by Header Column */
8274 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8276 nmlv.iSubItem = lpnmh->iItem;
8277 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8287 * Determines the type of structure to use.
8290 * [I] infoPtr : valid pointer to the listview structureof the sender
8291 * [I] hwndFrom : listview window handle
8292 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8297 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8299 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8301 if (nCommand != NF_REQUERY) return 0;
8303 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8310 * Paints/Repaints the listview control.
8313 * [I] infoPtr : valid pointer to the listview structure
8314 * [I] hdc : device context handle
8319 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8321 TRACE("(hdc=%p)\n", hdc);
8323 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8325 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8327 infoPtr->bNoItemMetrics = FALSE;
8328 LISTVIEW_UpdateItemSize(infoPtr);
8329 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8330 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8331 LISTVIEW_UpdateScroll(infoPtr);
8334 LISTVIEW_Refresh(infoPtr, hdc);
8339 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8341 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8342 LISTVIEW_Refresh(infoPtr, hdc);
8343 EndPaint(infoPtr->hwndSelf, &ps);
8352 * Paints/Repaints the listview control.
8355 * [I] infoPtr : valid pointer to the listview structure
8356 * [I] hdc : device context handle
8357 * [I] options : drawing options
8362 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8364 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8366 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8369 if (options & PRF_ERASEBKGND)
8370 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8372 if (options & PRF_CLIENT)
8373 LISTVIEW_Paint(infoPtr, hdc);
8381 * Processes double click messages (right mouse button).
8384 * [I] infoPtr : valid pointer to the listview structure
8385 * [I] wKey : key flag
8386 * [I] x,y : mouse coordinate
8391 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8393 LVHITTESTINFO lvHitTestInfo;
8395 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8397 /* send NM_RELEASEDCAPTURE notification */
8398 notify(infoPtr, NM_RELEASEDCAPTURE);
8400 /* send NM_RDBLCLK notification */
8401 lvHitTestInfo.pt.x = x;
8402 lvHitTestInfo.pt.y = y;
8403 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8404 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8411 * Processes mouse down messages (right mouse button).
8414 * [I] infoPtr : valid pointer to the listview structure
8415 * [I] wKey : key flag
8416 * [I] x,y : mouse coordinate
8421 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8423 LVHITTESTINFO lvHitTestInfo;
8426 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8428 /* send NM_RELEASEDCAPTURE notification */
8429 notify(infoPtr, NM_RELEASEDCAPTURE);
8431 /* make sure the listview control window has the focus */
8432 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8434 /* set right button down flag */
8435 infoPtr->bRButtonDown = TRUE;
8437 /* determine the index of the selected item */
8438 lvHitTestInfo.pt.x = x;
8439 lvHitTestInfo.pt.y = y;
8440 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8442 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8444 LISTVIEW_SetItemFocus(infoPtr, nItem);
8445 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8446 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8447 LISTVIEW_SetSelection(infoPtr, nItem);
8451 LISTVIEW_DeselectAll(infoPtr);
8459 * Processes mouse up messages (right mouse button).
8462 * [I] infoPtr : valid pointer to the listview structure
8463 * [I] wKey : key flag
8464 * [I] x,y : mouse coordinate
8469 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8471 LVHITTESTINFO lvHitTestInfo;
8474 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8476 if (!infoPtr->bRButtonDown) return 0;
8478 /* set button flag */
8479 infoPtr->bRButtonDown = FALSE;
8481 /* Send NM_RClICK notification */
8482 lvHitTestInfo.pt.x = x;
8483 lvHitTestInfo.pt.y = y;
8484 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8485 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8487 /* Change to screen coordinate for WM_CONTEXTMENU */
8488 pt = lvHitTestInfo.pt;
8489 ClientToScreen(infoPtr->hwndSelf, &pt);
8491 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8492 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8493 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8504 * [I] infoPtr : valid pointer to the listview structure
8505 * [I] hwnd : window handle of window containing the cursor
8506 * [I] nHittest : hit-test code
8507 * [I] wMouseMsg : ideintifier of the mouse message
8510 * TRUE if cursor is set
8513 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8515 LVHITTESTINFO lvHitTestInfo;
8517 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8519 if(!infoPtr->hHotCursor) return FALSE;
8521 GetCursorPos(&lvHitTestInfo.pt);
8522 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8524 SetCursor(infoPtr->hHotCursor);
8534 * [I] infoPtr : valid pointer to the listview structure
8535 * [I] hwndLoseFocus : handle of previously focused window
8540 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8542 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8544 /* if we have the focus already, there's nothing to do */
8545 if (infoPtr->bFocus) return 0;
8547 /* send NM_SETFOCUS notification */
8548 notify(infoPtr, NM_SETFOCUS);
8550 /* set window focus flag */
8551 infoPtr->bFocus = TRUE;
8553 /* put the focus rect back on */
8554 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8556 /* redraw all visible selected items */
8557 LISTVIEW_InvalidateSelectedItems(infoPtr);
8567 * [I] infoPtr : valid pointer to the listview structure
8568 * [I] fRedraw : font handle
8569 * [I] fRedraw : redraw flag
8574 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8576 HFONT oldFont = infoPtr->hFont;
8578 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8580 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8581 if (infoPtr->hFont == oldFont) return 0;
8583 LISTVIEW_SaveTextMetrics(infoPtr);
8585 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8586 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8588 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8595 * Message handling for WM_SETREDRAW.
8596 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8599 * [I] infoPtr : valid pointer to the listview structure
8600 * [I] bRedraw: state of redraw flag
8603 * DefWinProc return value
8605 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8607 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8609 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8610 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8612 infoPtr->bRedraw = bRedraw;
8614 if(!bRedraw) return 0;
8616 if (is_autoarrange(infoPtr))
8617 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8618 LISTVIEW_UpdateScroll(infoPtr);
8620 /* despite what the WM_SETREDRAW docs says, apps expect us
8621 * to invalidate the listview here... stupid! */
8622 LISTVIEW_InvalidateList(infoPtr);
8629 * Resizes the listview control. This function processes WM_SIZE
8630 * messages. At this time, the width and height are not used.
8633 * [I] infoPtr : valid pointer to the listview structure
8634 * [I] Width : new width
8635 * [I] Height : new height
8640 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8642 RECT rcOld = infoPtr->rcList;
8644 TRACE("(width=%d, height=%d)\n", Width, Height);
8646 LISTVIEW_UpdateSize(infoPtr);
8647 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8649 /* do not bother with display related stuff if we're not redrawing */
8650 if (!is_redrawing(infoPtr)) return 0;
8652 if (is_autoarrange(infoPtr))
8653 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8655 LISTVIEW_UpdateScroll(infoPtr);
8657 /* refresh all only for lists whose height changed significantly */
8658 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8659 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8660 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8661 LISTVIEW_InvalidateList(infoPtr);
8668 * Sets the size information.
8671 * [I] infoPtr : valid pointer to the listview structure
8676 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8678 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8680 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8682 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8684 if (uView == LVS_LIST)
8686 /* Apparently the "LIST" style is supposed to have the same
8687 * number of items in a column even if there is no scroll bar.
8688 * Since if a scroll bar already exists then the bottom is already
8689 * reduced, only reduce if the scroll bar does not currently exist.
8690 * The "2" is there to mimic the native control. I think it may be
8691 * related to either padding or edges. (GLA 7/2002)
8693 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
8694 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8695 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8697 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8702 hl.prc = &infoPtr->rcList;
8704 Header_Layout(infoPtr->hwndHeader, &hl);
8706 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8708 infoPtr->rcList.top = max(wp.cy, 0);
8711 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8716 * Processes WM_STYLECHANGED messages.
8719 * [I] infoPtr : valid pointer to the listview structure
8720 * [I] wStyleType : window style type (normal or extended)
8721 * [I] lpss : window style information
8726 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8727 const STYLESTRUCT *lpss)
8729 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8730 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8732 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8733 wStyleType, lpss->styleOld, lpss->styleNew);
8735 if (wStyleType != GWL_STYLE) return 0;
8737 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8738 /* what if LVS_OWNERDATA changed? */
8739 /* or LVS_SINGLESEL */
8740 /* or LVS_SORT{AS,DES}CENDING */
8742 infoPtr->dwStyle = lpss->styleNew;
8744 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8745 ((lpss->styleNew & WS_HSCROLL) == 0))
8746 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8748 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8749 ((lpss->styleNew & WS_VSCROLL) == 0))
8750 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8752 if (uNewView != uOldView)
8754 SIZE oldIconSize = infoPtr->iconSize;
8757 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8758 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8760 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8761 SetRectEmpty(&infoPtr->rcFocus);
8763 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8764 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8766 if (uNewView == LVS_ICON)
8768 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8770 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8771 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8772 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8775 else if (uNewView == LVS_REPORT)
8780 hl.prc = &infoPtr->rcList;
8782 Header_Layout(infoPtr->hwndHeader, &hl);
8783 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8786 LISTVIEW_UpdateItemSize(infoPtr);
8789 if (uNewView == LVS_REPORT)
8790 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8792 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8793 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8794 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8796 /* update the size of the client area */
8797 LISTVIEW_UpdateSize(infoPtr);
8799 /* add scrollbars if needed */
8800 LISTVIEW_UpdateScroll(infoPtr);
8802 /* invalidate client area + erase background */
8803 LISTVIEW_InvalidateList(infoPtr);
8810 * Window procedure of the listview control.
8813 static LRESULT WINAPI
8814 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8816 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8818 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8820 if (!infoPtr && (uMsg != WM_CREATE))
8821 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8825 case LVM_APPROXIMATEVIEWRECT:
8826 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8827 LOWORD(lParam), HIWORD(lParam));
8829 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8831 /* case LVM_CANCELEDITLABEL: */
8833 case LVM_CREATEDRAGIMAGE:
8834 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
8836 case LVM_DELETEALLITEMS:
8837 return LISTVIEW_DeleteAllItems(infoPtr);
8839 case LVM_DELETECOLUMN:
8840 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8842 case LVM_DELETEITEM:
8843 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8845 case LVM_EDITLABELW:
8846 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8848 case LVM_EDITLABELA:
8849 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8851 /* case LVM_ENABLEGROUPVIEW: */
8853 case LVM_ENSUREVISIBLE:
8854 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8857 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8860 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8862 case LVM_GETBKCOLOR:
8863 return infoPtr->clrBk;
8865 /* case LVM_GETBKIMAGE: */
8867 case LVM_GETCALLBACKMASK:
8868 return infoPtr->uCallbackMask;
8870 case LVM_GETCOLUMNA:
8871 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8873 case LVM_GETCOLUMNW:
8874 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8876 case LVM_GETCOLUMNORDERARRAY:
8877 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8879 case LVM_GETCOLUMNWIDTH:
8880 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8882 case LVM_GETCOUNTPERPAGE:
8883 return LISTVIEW_GetCountPerPage(infoPtr);
8885 case LVM_GETEDITCONTROL:
8886 return (LRESULT)infoPtr->hwndEdit;
8888 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8889 return infoPtr->dwLvExStyle;
8891 /* case LVM_GETGROUPINFO: */
8893 /* case LVM_GETGROUPMETRICS: */
8896 return (LRESULT)infoPtr->hwndHeader;
8898 case LVM_GETHOTCURSOR:
8899 return (LRESULT)infoPtr->hHotCursor;
8901 case LVM_GETHOTITEM:
8902 return infoPtr->nHotItem;
8904 case LVM_GETHOVERTIME:
8905 return infoPtr->dwHoverTime;
8907 case LVM_GETIMAGELIST:
8908 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8910 /* case LVM_GETINSERTMARK: */
8912 /* case LVM_GETINSERTMARKCOLOR: */
8914 /* case LVM_GETINSERTMARKRECT: */
8916 case LVM_GETISEARCHSTRINGA:
8917 case LVM_GETISEARCHSTRINGW:
8918 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8922 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8925 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8927 case LVM_GETITEMCOUNT:
8928 return infoPtr->nItemCount;
8930 case LVM_GETITEMPOSITION:
8931 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8933 case LVM_GETITEMRECT:
8934 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8936 case LVM_GETITEMSPACING:
8937 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8939 case LVM_GETITEMSTATE:
8940 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8942 case LVM_GETITEMTEXTA:
8943 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8945 case LVM_GETITEMTEXTW:
8946 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8948 case LVM_GETNEXTITEM:
8949 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8951 case LVM_GETNUMBEROFWORKAREAS:
8952 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8956 if (!lParam) return FALSE;
8957 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8960 /* case LVM_GETOUTLINECOLOR: */
8962 /* case LVM_GETSELECTEDCOLUMN: */
8964 case LVM_GETSELECTEDCOUNT:
8965 return LISTVIEW_GetSelectedCount(infoPtr);
8967 case LVM_GETSELECTIONMARK:
8968 return infoPtr->nSelectionMark;
8970 case LVM_GETSTRINGWIDTHA:
8971 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8973 case LVM_GETSTRINGWIDTHW:
8974 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8976 case LVM_GETSUBITEMRECT:
8977 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8979 case LVM_GETTEXTBKCOLOR:
8980 return infoPtr->clrTextBk;
8982 case LVM_GETTEXTCOLOR:
8983 return infoPtr->clrText;
8985 /* case LVM_GETTILEINFO: */
8987 /* case LVM_GETTILEVIEWINFO: */
8989 case LVM_GETTOOLTIPS:
8990 if( !infoPtr->hwndToolTip )
8991 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
8992 return (LRESULT)infoPtr->hwndToolTip;
8994 case LVM_GETTOPINDEX:
8995 return LISTVIEW_GetTopIndex(infoPtr);
8997 /*case LVM_GETUNICODEFORMAT:
8998 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9001 /* case LVM_GETVIEW: */
9003 case LVM_GETVIEWRECT:
9004 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9006 case LVM_GETWORKAREAS:
9007 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9010 /* case LVM_HASGROUP: */
9013 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9015 case LVM_INSERTCOLUMNA:
9016 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9018 case LVM_INSERTCOLUMNW:
9019 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9021 /* case LVM_INSERTGROUP: */
9023 /* case LVM_INSERTGROUPSORTED: */
9025 case LVM_INSERTITEMA:
9026 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9028 case LVM_INSERTITEMW:
9029 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9031 /* case LVM_INSERTMARKHITTEST: */
9033 /* case LVM_ISGROUPVIEWENABLED: */
9035 /* case LVM_MAPIDTOINDEX: */
9037 /* case LVM_MAPINDEXTOID: */
9039 /* case LVM_MOVEGROUP: */
9041 /* case LVM_MOVEITEMTOGROUP: */
9043 case LVM_REDRAWITEMS:
9044 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9046 /* case LVM_REMOVEALLGROUPS: */
9048 /* case LVM_REMOVEGROUP: */
9051 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9053 case LVM_SETBKCOLOR:
9054 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9056 /* case LVM_SETBKIMAGE: */
9058 case LVM_SETCALLBACKMASK:
9059 infoPtr->uCallbackMask = (UINT)wParam;
9062 case LVM_SETCOLUMNA:
9063 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9065 case LVM_SETCOLUMNW:
9066 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9068 case LVM_SETCOLUMNORDERARRAY:
9069 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9071 case LVM_SETCOLUMNWIDTH:
9072 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9074 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9075 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9077 /* case LVM_SETGROUPINFO: */
9079 /* case LVM_SETGROUPMETRICS: */
9081 case LVM_SETHOTCURSOR:
9082 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9084 case LVM_SETHOTITEM:
9085 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9087 case LVM_SETHOVERTIME:
9088 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9090 case LVM_SETICONSPACING:
9091 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9093 case LVM_SETIMAGELIST:
9094 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9096 /* case LVM_SETINFOTIP: */
9098 /* case LVM_SETINSERTMARK: */
9100 /* case LVM_SETINSERTMARKCOLOR: */
9103 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9106 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9108 case LVM_SETITEMCOUNT:
9109 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9111 case LVM_SETITEMPOSITION:
9114 pt.x = (short)LOWORD(lParam);
9115 pt.y = (short)HIWORD(lParam);
9116 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9119 case LVM_SETITEMPOSITION32:
9120 if (lParam == 0) return FALSE;
9121 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9123 case LVM_SETITEMSTATE:
9124 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9126 case LVM_SETITEMTEXTA:
9127 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9129 case LVM_SETITEMTEXTW:
9130 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9132 /* case LVM_SETOUTLINECOLOR: */
9134 /* case LVM_SETSELECTEDCOLUMN: */
9136 case LVM_SETSELECTIONMARK:
9137 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9139 case LVM_SETTEXTBKCOLOR:
9140 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9142 case LVM_SETTEXTCOLOR:
9143 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9145 /* case LVM_SETTILEINFO: */
9147 /* case LVM_SETTILEVIEWINFO: */
9149 /* case LVM_SETTILEWIDTH: */
9151 case LVM_SETTOOLTIPS:
9152 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9154 /* case LVM_SETUNICODEFORMAT: */
9156 /* case LVM_SETVIEW: */
9158 /* case LVM_SETWORKAREAS: */
9160 /* case LVM_SORTGROUPS: */
9163 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9165 /* LVM_SORTITEMSEX: */
9167 case LVM_SUBITEMHITTEST:
9168 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9171 return LISTVIEW_Update(infoPtr, (INT)wParam);
9174 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9177 return LISTVIEW_Command(infoPtr, wParam, lParam);
9180 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9183 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9186 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9189 return (LRESULT)infoPtr->hFont;
9192 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9195 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9198 return LISTVIEW_KillFocus(infoPtr);
9200 case WM_LBUTTONDBLCLK:
9201 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9203 case WM_LBUTTONDOWN:
9204 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9207 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9210 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9213 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9216 return LISTVIEW_NCDestroy(infoPtr);
9219 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9220 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9223 case WM_NOTIFYFORMAT:
9224 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9226 case WM_PRINTCLIENT:
9227 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9230 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9232 case WM_RBUTTONDBLCLK:
9233 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9235 case WM_RBUTTONDOWN:
9236 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9239 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9242 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9247 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9250 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9253 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9256 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9258 case WM_STYLECHANGED:
9259 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9261 case WM_SYSCOLORCHANGE:
9262 COMCTL32_RefreshSysColors();
9265 /* case WM_TIMER: */
9268 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9271 if (wParam & (MK_SHIFT | MK_CONTROL))
9272 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9273 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9275 case WM_WINDOWPOSCHANGED:
9276 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9278 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9279 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9280 LISTVIEW_UpdateSize(infoPtr);
9281 LISTVIEW_UpdateScroll(infoPtr);
9283 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9285 /* case WM_WININICHANGE: */
9288 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9289 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9292 /* call default window procedure */
9293 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9300 * Registers the window class.
9308 void LISTVIEW_Register(void)
9312 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9313 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9314 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9315 wndClass.cbClsExtra = 0;
9316 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9317 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9318 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9319 wndClass.lpszClassName = WC_LISTVIEWW;
9320 RegisterClassW(&wndClass);
9325 * Unregisters the window class.
9333 void LISTVIEW_Unregister(void)
9335 UnregisterClassW(WC_LISTVIEWW, NULL);
9340 * Handle any WM_COMMAND messages
9343 * [I] infoPtr : valid pointer to the listview structure
9344 * [I] wParam : the first message parameter
9345 * [I] lParam : the second message parameter
9350 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9352 switch (HIWORD(wParam))
9357 * Adjust the edit window size
9360 HDC hdc = GetDC(infoPtr->hwndEdit);
9361 HFONT hFont, hOldFont = 0;
9366 if (!infoPtr->hwndEdit || !hdc) return 0;
9367 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9368 GetWindowRect(infoPtr->hwndEdit, &rect);
9370 /* Select font to get the right dimension of the string */
9371 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9374 hOldFont = SelectObject(hdc, hFont);
9377 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9379 TEXTMETRICW textMetric;
9381 /* Add Extra spacing for the next character */
9382 GetTextMetricsW(hdc, &textMetric);
9383 sz.cx += (textMetric.tmMaxCharWidth * 2);
9391 rect.bottom - rect.top,
9392 SWP_DRAWFRAME|SWP_NOMOVE);
9395 SelectObject(hdc, hOldFont);
9397 ReleaseDC(infoPtr->hwndSelf, hdc);
9403 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9412 * Subclassed edit control windproc function
9415 * [I] hwnd : the edit window handle
9416 * [I] uMsg : the message that is to be processed
9417 * [I] wParam : first message parameter
9418 * [I] lParam : second message parameter
9419 * [I] isW : TRUE if input is Unicode
9424 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9426 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9427 BOOL cancel = FALSE;
9429 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9430 hwnd, uMsg, wParam, lParam, isW);
9435 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9442 WNDPROC editProc = infoPtr->EditWndProc;
9443 infoPtr->EditWndProc = 0;
9444 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9445 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9449 if (VK_ESCAPE == (INT)wParam)
9454 else if (VK_RETURN == (INT)wParam)
9458 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9462 if (infoPtr->hwndEdit)
9464 LPWSTR buffer = NULL;
9466 infoPtr->hwndEdit = 0;
9469 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9473 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9475 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9476 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9480 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9482 if (buffer) Free(buffer);
9486 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9492 * Subclassed edit control Unicode windproc function
9495 * [I] hwnd : the edit window handle
9496 * [I] uMsg : the message that is to be processed
9497 * [I] wParam : first message parameter
9498 * [I] lParam : second message parameter
9502 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9504 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9509 * Subclassed edit control ANSI windproc function
9512 * [I] hwnd : the edit window handle
9513 * [I] uMsg : the message that is to be processed
9514 * [I] wParam : first message parameter
9515 * [I] lParam : second message parameter
9519 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9521 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9526 * Creates a subclassed edit cotrol
9529 * [I] infoPtr : valid pointer to the listview structure
9530 * [I] text : initial text for the edit
9531 * [I] style : the window style
9532 * [I] isW : TRUE if input is Unicode
9536 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9537 INT x, INT y, INT width, INT height, BOOL isW)
9539 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9544 TEXTMETRICW textMetric;
9545 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9547 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9549 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9550 hdc = GetDC(infoPtr->hwndSelf);
9552 /* Select the font to get appropriate metric dimensions */
9553 if(infoPtr->hFont != 0)
9554 hOldFont = SelectObject(hdc, infoPtr->hFont);
9556 /*Get String Length in pixels */
9557 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9559 /*Add Extra spacing for the next character */
9560 GetTextMetricsW(hdc, &textMetric);
9561 sz.cx += (textMetric.tmMaxCharWidth * 2);
9563 if(infoPtr->hFont != 0)
9564 SelectObject(hdc, hOldFont);
9566 ReleaseDC(infoPtr->hwndSelf, hdc);
9568 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9570 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9572 if (!hedit) return 0;
9574 infoPtr->EditWndProc = (WNDPROC)
9575 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9576 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9578 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);