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 CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
44 * -- LVA_SNAPTOGRID not implemented
45 * -- LISTVIEW_ApproximateViewRect partially implemented
46 * -- LISTVIEW_[GS]etColumnOrderArray stubs
47 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
48 * -- LISTVIEW_SetIconSpacing is incomplete
49 * -- LISTVIEW_SortItems is broken
50 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
53 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
54 * linear in the number of items in the list, and this is
55 * unacceptable for large lists.
56 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
57 * instead of inserting in the right spot
58 * -- we should keep an ordered array of coordinates in iconic mode
59 * this would allow to frame items (iterator_frameditems),
60 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
68 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
75 * -- LVS_NOSCROLL (see Q137520)
76 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
79 * -- LVS_EX_BORDERSELECT
82 * -- LVS_EX_HEADERDRAGDROP
85 * -- LVS_EX_MULTIWORKAREAS
86 * -- LVS_EX_ONECLICKACTIVATE
88 * -- LVS_EX_SIMPLESELECT
89 * -- LVS_EX_TRACKSELECT
90 * -- LVS_EX_TWOCLICKACTIVATE
91 * -- LVS_EX_UNDERLINECOLD
92 * -- LVS_EX_UNDERLINEHOT
95 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
104 * -- LVM_CANCELEDITLABEL
105 * -- LVM_ENABLEGROUPVIEW
106 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
107 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
108 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
109 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
110 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
111 * -- LVM_GETINSERTMARKRECT
112 * -- LVM_GETNUMBEROFWORKAREAS
113 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
114 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
115 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
116 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
117 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
118 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
119 * -- LVM_GETVIEW, LVM_SETVIEW
120 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
121 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
122 * -- LVM_INSERTGROUPSORTED
123 * -- LVM_INSERTMARKHITTEST
124 * -- LVM_ISGROUPVIEWENABLED
125 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
127 * -- LVM_MOVEITEMTOGROUP
129 * -- LVM_SETTILEWIDTH
133 * Known differences in message stream from native control (not known if
134 * these differences cause problems):
135 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
136 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
137 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
138 * processing for "USEDOUBLECLICKTIME".
142 #include "wine/port.h"
157 #include "commctrl.h"
158 #include "comctl32.h"
160 #include "wine/debug.h"
161 #include "wine/unicode.h"
163 WINE_DEFAULT_DEBUG_CHANNEL(listview);
165 /* make sure you set this to 0 for production use! */
166 #define DEBUG_RANGES 1
168 typedef struct tagCOLUMN_INFO
170 RECT rcHeader; /* tracks the header's rectangle */
171 int fmt; /* same as LVCOLUMN.fmt */
174 typedef struct tagITEMHDR
178 } ITEMHDR, *LPITEMHDR;
180 typedef struct tagSUBITEM_INFO
186 typedef struct tagITEM_INFO
194 typedef struct tagRANGE
200 typedef struct tagRANGES
205 typedef struct tagITERATOR
214 typedef struct tagLISTVIEW_INFO
221 COLORREF clrTextBkDefault;
222 HIMAGELIST himlNormal;
223 HIMAGELIST himlSmall;
224 HIMAGELIST himlState;
227 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
230 RANGES selectionRanges;
235 RECT rcList; /* This rectangle is really the window
236 * client rectangle possibly reduced by the
237 * horizontal scroll bar and/or header - see
238 * LISTVIEW_UpdateSize. This rectangle offset
239 * by the LISTVIEW_GetOrigin value is in
240 * client coordinates */
249 INT ntmHeight; /* Some cached metrics of the font used */
250 INT ntmAveCharWidth; /* by the listview to draw items */
251 BOOL bRedraw; /* Turns on/off repaints & invalidations */
252 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
254 BOOL bDoChangeNotify; /* send change notification messages? */
257 DWORD dwStyle; /* the cached window GWL_STYLE */
258 DWORD dwLvExStyle; /* extended listview style */
259 INT nItemCount; /* the number of items in the list */
260 HDPA hdpaItems; /* array ITEM_INFO pointers */
261 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
262 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
263 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
264 POINT currIconPos; /* this is the position next icon will be placed */
265 PFNLVCOMPARE pfnCompare;
273 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
275 DWORD lastKeyPressTimestamp;
277 INT nSearchParamLength;
278 WCHAR szSearchParam[ MAX_PATH ];
285 /* How many we debug buffer to allocate */
286 #define DEBUG_BUFFERS 20
287 /* The size of a single debug bbuffer */
288 #define DEBUG_BUFFER_SIZE 256
290 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
291 #define SB_INTERNAL -1
293 /* maximum size of a label */
294 #define DISP_TEXT_SIZE 512
296 /* padding for items in list and small icon display modes */
297 #define WIDTH_PADDING 12
299 /* padding for items in list, report and small icon display modes */
300 #define HEIGHT_PADDING 1
302 /* offset of items in report display mode */
303 #define REPORT_MARGINX 2
305 /* padding for icon in large icon display mode
306 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
307 * that HITTEST will see.
308 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
309 * ICON_TOP_PADDING - sum of the two above.
310 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
311 * LABEL_HOR_PADDING - between text and sides of box
312 * LABEL_VERT_PADDING - between bottom of text and end of box
314 * ICON_LR_PADDING - additional width above icon size.
315 * ICON_LR_HALF - half of the above value
317 #define ICON_TOP_PADDING_NOTHITABLE 2
318 #define ICON_TOP_PADDING_HITABLE 2
319 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
320 #define ICON_BOTTOM_PADDING 4
321 #define LABEL_HOR_PADDING 5
322 #define LABEL_VERT_PADDING 7
323 #define ICON_LR_PADDING 16
324 #define ICON_LR_HALF (ICON_LR_PADDING/2)
326 /* default label width for items in list and small icon display modes */
327 #define DEFAULT_LABEL_WIDTH 40
329 /* default column width for items in list display mode */
330 #define DEFAULT_COLUMN_WIDTH 128
332 /* Size of "line" scroll for V & H scrolls */
333 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
335 /* Padding betwen image and label */
336 #define IMAGE_PADDING 2
338 /* Padding behind the label */
339 #define TRAILING_LABEL_PADDING 12
340 #define TRAILING_HEADER_PADDING 11
342 /* Border for the icon caption */
343 #define CAPTION_BORDER 2
345 /* Standard DrawText flags */
346 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
347 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
348 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
350 /* The time in milliseconds to reset the search in the list */
351 #define KEY_DELAY 450
353 /* Dump the LISTVIEW_INFO structure to the debug channel */
354 #define LISTVIEW_DUMP(iP) do { \
355 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
356 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
357 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
358 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
359 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
360 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
361 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
362 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
363 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
364 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
368 * forward declarations
370 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
371 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
372 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
373 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
374 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
375 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
376 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
377 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
378 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
379 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
380 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
381 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
382 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
383 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
384 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
385 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
386 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
387 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
388 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
389 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
390 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
391 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
392 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
393 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
394 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
395 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
397 /******** Text handling functions *************************************/
399 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
400 * text string. The string may be ANSI or Unicode, in which case
401 * the boolean isW tells us the type of the string.
403 * The name of the function tell what type of strings it expects:
404 * W: Unicode, T: ANSI/Unicode - function of isW
407 static inline BOOL is_textW(LPCWSTR text)
409 return text != NULL && text != LPSTR_TEXTCALLBACKW;
412 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
414 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
415 return is_textW(text);
418 static inline int textlenT(LPCWSTR text, BOOL isW)
420 return !is_textT(text, isW) ? 0 :
421 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
424 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
427 if (isSrcW) lstrcpynW(dest, src, max);
428 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
430 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
431 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
434 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
436 LPWSTR wstr = (LPWSTR)text;
438 if (!isW && is_textT(text, isW))
440 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
441 wstr = Alloc(len * sizeof(WCHAR));
442 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
444 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
448 static inline void textfreeT(LPWSTR wstr, BOOL isW)
450 if (!isW && is_textT(wstr, isW)) Free (wstr);
454 * dest is a pointer to a Unicode string
455 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
457 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
461 if (src == LPSTR_TEXTCALLBACKW)
463 if (is_textW(*dest)) Free(*dest);
464 *dest = LPSTR_TEXTCALLBACKW;
468 LPWSTR pszText = textdupTtoW(src, isW);
469 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
470 bResult = Str_SetPtrW(dest, pszText);
471 textfreeT(pszText, isW);
477 * compares a Unicode to a Unicode/ANSI text string
479 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
481 if (!aw) return bt ? -1 : 0;
482 if (!bt) return aw ? 1 : 0;
483 if (aw == LPSTR_TEXTCALLBACKW)
484 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
485 if (bt != LPSTR_TEXTCALLBACKW)
487 LPWSTR bw = textdupTtoW(bt, isW);
488 int r = bw ? lstrcmpW(aw, bw) : 1;
496 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
500 n = min(min(n, strlenW(s1)), strlenW(s2));
501 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
502 return res ? res - sizeof(WCHAR) : res;
505 /******** Debugging functions *****************************************/
507 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
509 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
510 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
513 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
515 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
516 n = min(textlenT(text, isW), n);
517 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
520 static char* debug_getbuf()
522 static int index = 0;
523 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
524 return buffers[index++ % DEBUG_BUFFERS];
527 static inline const char* debugrange(const RANGE *lprng)
531 char* buf = debug_getbuf();
532 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
534 } else return "(null)";
537 static inline const char* debugpoint(const POINT *lppt)
541 char* buf = debug_getbuf();
542 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
544 } else return "(null)";
547 static inline const char* debugrect(const RECT *rect)
551 char* buf = debug_getbuf();
552 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
553 rect->left, rect->top, rect->right, rect->bottom);
555 } else return "(null)";
558 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
560 char* buf = debug_getbuf(), *text = buf;
561 int len, size = DEBUG_BUFFER_SIZE;
563 if (pScrollInfo == NULL) return "(null)";
564 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
565 if (len == -1) goto end; buf += len; size -= len;
566 if (pScrollInfo->fMask & SIF_RANGE)
567 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
569 if (len == -1) goto end; buf += len; size -= len;
570 if (pScrollInfo->fMask & SIF_PAGE)
571 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
573 if (len == -1) goto end; buf += len; size -= len;
574 if (pScrollInfo->fMask & SIF_POS)
575 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
577 if (len == -1) goto end; buf += len; size -= len;
578 if (pScrollInfo->fMask & SIF_TRACKPOS)
579 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
581 if (len == -1) goto end; buf += len; size -= len;
584 buf = text + strlen(text);
586 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
590 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
594 char* buf = debug_getbuf();
595 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
596 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
597 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
598 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
600 } else return "(null)";
603 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
605 char* buf = debug_getbuf(), *text = buf;
606 int len, size = DEBUG_BUFFER_SIZE;
608 if (lpLVItem == NULL) return "(null)";
609 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
610 if (len == -1) goto end; buf += len; size -= len;
611 if (lpLVItem->mask & LVIF_STATE)
612 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
614 if (len == -1) goto end; buf += len; size -= len;
615 if (lpLVItem->mask & LVIF_TEXT)
616 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
618 if (len == -1) goto end; buf += len; size -= len;
619 if (lpLVItem->mask & LVIF_IMAGE)
620 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
622 if (len == -1) goto end; buf += len; size -= len;
623 if (lpLVItem->mask & LVIF_PARAM)
624 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
626 if (len == -1) goto end; buf += len; size -= len;
627 if (lpLVItem->mask & LVIF_INDENT)
628 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
630 if (len == -1) goto end; buf += len; size -= len;
633 buf = text + strlen(text);
635 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
639 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
641 char* buf = debug_getbuf(), *text = buf;
642 int len, size = DEBUG_BUFFER_SIZE;
644 if (lpColumn == NULL) return "(null)";
645 len = snprintf(buf, size, "{");
646 if (len == -1) goto end; buf += len; size -= len;
647 if (lpColumn->mask & LVCF_SUBITEM)
648 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
650 if (len == -1) goto end; buf += len; size -= len;
651 if (lpColumn->mask & LVCF_FMT)
652 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
654 if (len == -1) goto end; buf += len; size -= len;
655 if (lpColumn->mask & LVCF_WIDTH)
656 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
658 if (len == -1) goto end; buf += len; size -= len;
659 if (lpColumn->mask & LVCF_TEXT)
660 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
662 if (len == -1) goto end; buf += len; size -= len;
663 if (lpColumn->mask & LVCF_IMAGE)
664 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
666 if (len == -1) goto end; buf += len; size -= len;
667 if (lpColumn->mask & LVCF_ORDER)
668 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
670 if (len == -1) goto end; buf += len; size -= len;
673 buf = text + strlen(text);
675 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
679 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
683 char* buf = debug_getbuf();
684 snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
685 debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
687 } else return "(null)";
690 /* Return the corresponding text for a given scroll value */
691 static inline LPCSTR debugscrollcode(int nScrollCode)
695 case SB_LINELEFT: return "SB_LINELEFT";
696 case SB_LINERIGHT: return "SB_LINERIGHT";
697 case SB_PAGELEFT: return "SB_PAGELEFT";
698 case SB_PAGERIGHT: return "SB_PAGERIGHT";
699 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
700 case SB_THUMBTRACK: return "SB_THUMBTRACK";
701 case SB_ENDSCROLL: return "SB_ENDSCROLL";
702 case SB_INTERNAL: return "SB_INTERNAL";
703 default: return "unknown";
708 /******** Notification functions i************************************/
710 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
714 TRACE("(code=%d)\n", code);
716 pnmh->hwndFrom = infoPtr->hwndSelf;
717 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
719 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
720 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
722 TRACE(" <= %ld\n", result);
727 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
730 return notify_hdr(infoPtr, code, &nmh);
733 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
744 item.mask = LVIF_PARAM|LVIF_STATE;
745 item.iItem = htInfo->iItem;
747 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
748 nmia.lParam = item.lParam;
749 nmia.uOldState = item.state;
750 nmia.uNewState = item.state | LVIS_ACTIVATING;
751 nmia.uChanged = LVIF_STATE;
754 nmia.iItem = htInfo->iItem;
755 nmia.iSubItem = htInfo->iSubItem;
756 nmia.ptAction = htInfo->pt;
758 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
759 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
760 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
762 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
765 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
767 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
768 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
771 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
776 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
777 ZeroMemory(&nmlv, sizeof(nmlv));
778 nmlv.iItem = lvht->iItem;
779 nmlv.iSubItem = lvht->iSubItem;
780 nmlv.ptAction = lvht->pt;
781 item.mask = LVIF_PARAM;
782 item.iItem = lvht->iItem;
784 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
785 return notify_listview(infoPtr, code, &nmlv);
788 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
793 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
795 item.mask = LVIF_PARAM;
798 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
799 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
802 static int get_ansi_notification(INT unicodeNotificationCode)
804 switch (unicodeNotificationCode)
806 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
807 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
808 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
809 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
810 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
811 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
813 ERR("unknown notification %x\n", unicodeNotificationCode);
819 Send notification. depends on dispinfoW having same
820 structure as dispinfoA.
821 infoPtr : listview struct
822 notificationCode : *Unicode* notification code
823 pdi : dispinfo structure (can be unicode or ansi)
824 isW : TRUE if dispinfo is Unicode
826 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
828 BOOL bResult = FALSE;
829 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
830 INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
831 LPWSTR pszTempBuf = NULL, savPszText = NULL;
833 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
835 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
836 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
839 if (convertToAnsi || convertToUnicode)
841 if (notificationCode != LVN_GETDISPINFOW)
843 cchTempBufMax = convertToUnicode ?
844 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
845 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
849 cchTempBufMax = pdi->item.cchTextMax;
850 *pdi->item.pszText = 0; /* make sure we don't process garbage */
853 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
854 if (!pszTempBuf) return FALSE;
856 if (convertToUnicode)
857 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
858 pszTempBuf, cchTempBufMax);
860 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
861 cchTempBufMax, NULL, NULL);
863 savCchTextMax = pdi->item.cchTextMax;
864 savPszText = pdi->item.pszText;
865 pdi->item.pszText = pszTempBuf;
866 pdi->item.cchTextMax = cchTempBufMax;
869 if (infoPtr->notifyFormat == NFR_ANSI)
870 realNotifCode = get_ansi_notification(notificationCode);
872 realNotifCode = notificationCode;
873 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
874 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
876 if (convertToUnicode || convertToAnsi)
878 if (convertToUnicode) /* note : pointer can be changed by app ! */
879 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
880 savCchTextMax, NULL, NULL);
882 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
883 savPszText, savCchTextMax);
884 pdi->item.pszText = savPszText; /* restores our buffer */
885 pdi->item.cchTextMax = savCchTextMax;
891 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
892 const RECT *rcBounds, const LVITEMW *lplvItem)
894 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
895 lpnmlvcd->nmcd.hdc = hdc;
896 lpnmlvcd->nmcd.rc = *rcBounds;
897 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
898 lpnmlvcd->clrText = infoPtr->clrText;
899 if (!lplvItem) return;
900 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
901 lpnmlvcd->iSubItem = lplvItem->iSubItem;
902 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
903 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
904 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
905 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
908 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
910 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
913 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
914 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
915 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
916 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
917 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
918 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
922 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
924 /* apprently, for selected items, we have to override the returned values */
925 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
929 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
930 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
932 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
934 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
935 lpnmlvcd->clrText = comctl32_color.clrBtnText;
939 /* Set the text attributes */
940 if (lpnmlvcd->clrTextBk != CLR_NONE)
942 SetBkMode(hdc, OPAQUE);
943 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
944 SetBkColor(hdc, infoPtr->clrTextBkDefault);
946 SetBkColor(hdc,lpnmlvcd->clrTextBk);
949 SetBkMode(hdc, TRANSPARENT);
950 SetTextColor(hdc, lpnmlvcd->clrText);
953 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
955 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
958 /******** Item iterator functions **********************************/
960 static RANGES ranges_create(int count);
961 static void ranges_destroy(RANGES ranges);
962 static BOOL ranges_add(RANGES ranges, RANGE range);
963 static BOOL ranges_del(RANGES ranges, RANGE range);
964 static void ranges_dump(RANGES ranges);
966 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
968 RANGE range = { nItem, nItem + 1 };
970 return ranges_add(ranges, range);
973 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
975 RANGE range = { nItem, nItem + 1 };
977 return ranges_del(ranges, range);
981 * ITERATOR DOCUMENTATION
983 * The iterator functions allow for easy, and convenient iteration
984 * over items of iterest in the list. Typically, you create a
985 * iterator, use it, and destroy it, as such:
988 * iterator_xxxitems(&i, ...);
989 * while (iterator_{prev,next}(&i)
991 * //code which uses i.nItem
993 * iterator_destroy(&i);
995 * where xxx is either: framed, or visible.
996 * Note that it is important that the code destroys the iterator
997 * after it's done with it, as the creation of the iterator may
998 * allocate memory, which thus needs to be freed.
1000 * You can iterate both forwards, and backwards through the list,
1001 * by using iterator_next or iterator_prev respectively.
1003 * Lower numbered items are draw on top of higher number items in
1004 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1005 * items may overlap). So, to test items, you should use
1007 * which lists the items top to bottom (in Z-order).
1008 * For drawing items, you should use
1010 * which lists the items bottom to top (in Z-order).
1011 * If you keep iterating over the items after the end-of-items
1012 * marker (-1) is returned, the iterator will start from the
1013 * beginning. Typically, you don't need to test for -1,
1014 * because iterator_{next,prev} will return TRUE if more items
1015 * are to be iterated over, or FALSE otherwise.
1017 * Note: the iterator is defined to be bidirectional. That is,
1018 * any number of prev followed by any number of next, or
1019 * five versa, should leave the iterator at the same item:
1020 * prev * n, next * n = next * n, prev * n
1022 * The iterator has a notion of an out-of-order, special item,
1023 * which sits at the start of the list. This is used in
1024 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1025 * which needs to be first, as it may overlap other items.
1027 * The code is a bit messy because we have:
1028 * - a special item to deal with
1029 * - simple range, or composite range
1031 * If you find bugs, or want to add features, please make sure you
1032 * always check/modify *both* iterator_prev, and iterator_next.
1036 * This function iterates through the items in increasing order,
1037 * but prefixed by the special item, then -1. That is:
1038 * special, 1, 2, 3, ..., n, -1.
1039 * Each item is listed only once.
1041 static inline BOOL iterator_next(ITERATOR* i)
1045 i->nItem = i->nSpecial;
1046 if (i->nItem != -1) return TRUE;
1048 if (i->nItem == i->nSpecial)
1050 if (i->ranges) i->index = 0;
1056 if (i->nItem == i->nSpecial) i->nItem++;
1057 if (i->nItem < i->range.upper) return TRUE;
1062 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1063 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1066 else if (i->nItem >= i->range.upper) goto end;
1068 i->nItem = i->range.lower;
1069 if (i->nItem >= 0) goto testitem;
1076 * This function iterates through the items in decreasing order,
1077 * followed by the special item, then -1. That is:
1078 * n, n-1, ..., 3, 2, 1, special, -1.
1079 * Each item is listed only once.
1081 static inline BOOL iterator_prev(ITERATOR* i)
1088 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1091 if (i->nItem == i->nSpecial)
1099 if (i->nItem == i->nSpecial) i->nItem--;
1100 if (i->nItem >= i->range.lower) return TRUE;
1106 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1109 else if (!start && i->nItem < i->range.lower) goto end;
1111 i->nItem = i->range.upper;
1112 if (i->nItem > 0) goto testitem;
1114 return (i->nItem = i->nSpecial) != -1;
1117 static RANGE iterator_range(ITERATOR* i)
1121 if (!i->ranges) return i->range;
1123 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1125 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1126 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1128 else range.lower = range.upper = 0;
1134 * Releases resources associated with this ierator.
1136 static inline void iterator_destroy(ITERATOR* i)
1138 ranges_destroy(i->ranges);
1142 * Create an empty iterator.
1144 static inline BOOL iterator_empty(ITERATOR* i)
1146 ZeroMemory(i, sizeof(*i));
1147 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1152 * Create an iterator over a range.
1154 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1162 * Create an iterator over a bunch of ranges.
1163 * Please note that the iterator will take ownership of the ranges,
1164 * and will free them upon destruction.
1166 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1174 * Creates an iterator over the items which intersect lprc.
1176 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1178 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1179 RECT frame = *lprc, rcItem, rcTemp;
1182 /* in case we fail, we want to return an empty iterator */
1183 if (!iterator_empty(i)) return FALSE;
1185 LISTVIEW_GetOrigin(infoPtr, &Origin);
1187 TRACE("(lprc=%s)\n", debugrect(lprc));
1188 OffsetRect(&frame, -Origin.x, -Origin.y);
1190 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1194 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1196 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1197 if (IntersectRect(&rcTemp, &rcItem, lprc))
1198 i->nSpecial = infoPtr->nFocusedItem;
1200 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1201 /* to do better here, we need to have PosX, and PosY sorted */
1202 TRACE("building icon ranges:\n");
1203 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1205 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1206 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1207 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1208 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1209 if (IntersectRect(&rcTemp, &rcItem, &frame))
1210 ranges_additem(i->ranges, nItem);
1214 else if (uView == LVS_REPORT)
1218 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1219 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1221 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1222 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1223 if (range.upper <= range.lower) return TRUE;
1224 if (!iterator_rangeitems(i, range)) return FALSE;
1225 TRACE(" report=%s\n", debugrange(&i->range));
1229 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1230 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1231 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1232 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1233 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1234 INT lower = nFirstCol * nPerCol + nFirstRow;
1238 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1239 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1241 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1243 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1244 TRACE("building list ranges:\n");
1245 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1247 item_range.lower = nCol * nPerCol + nFirstRow;
1248 if(item_range.lower >= infoPtr->nItemCount) break;
1249 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1250 TRACE(" list=%s\n", debugrange(&item_range));
1251 ranges_add(i->ranges, item_range);
1259 * Creates an iterator over the items which intersect the visible region of hdc.
1261 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1263 POINT Origin, Position;
1264 RECT rcItem, rcClip;
1267 rgntype = GetClipBox(hdc, &rcClip);
1268 if (rgntype == NULLREGION) return iterator_empty(i);
1269 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1270 if (rgntype == SIMPLEREGION) return TRUE;
1272 /* first deal with the special item */
1273 if (i->nSpecial != -1)
1275 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1276 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1279 /* if we can't deal with the region, we'll just go with the simple range */
1280 LISTVIEW_GetOrigin(infoPtr, &Origin);
1281 TRACE("building visible range:\n");
1282 if (!i->ranges && i->range.lower < i->range.upper)
1284 if (!(i->ranges = ranges_create(50))) return TRUE;
1285 if (!ranges_add(i->ranges, i->range))
1287 ranges_destroy(i->ranges);
1293 /* now delete the invisible items from the list */
1294 while(iterator_next(i))
1296 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1297 rcItem.left = Position.x + Origin.x;
1298 rcItem.top = Position.y + Origin.y;
1299 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1300 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1301 if (!RectVisible(hdc, &rcItem))
1302 ranges_delitem(i->ranges, i->nItem);
1304 /* the iterator should restart on the next iterator_next */
1310 /******** Misc helper functions ************************************/
1312 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1313 WPARAM wParam, LPARAM lParam, BOOL isW)
1315 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1316 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1319 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1321 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1323 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1324 (uView == LVS_ICON || uView == LVS_SMALLICON);
1327 /******** Internal API functions ************************************/
1329 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1331 static COLUMN_INFO mainItem;
1333 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1334 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1335 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1338 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1340 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1343 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1345 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1348 /* Listview invalidation functions: use _only_ these functions to invalidate */
1350 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1352 return infoPtr->bRedraw;
1355 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1357 if(!is_redrawing(infoPtr)) return;
1358 TRACE(" invalidating rect=%s\n", debugrect(rect));
1359 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1362 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1366 if(!is_redrawing(infoPtr)) return;
1367 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1368 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1371 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1373 POINT Origin, Position;
1376 if(!is_redrawing(infoPtr)) return;
1377 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1378 LISTVIEW_GetOrigin(infoPtr, &Origin);
1379 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1380 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1382 rcBox.bottom = infoPtr->nItemHeight;
1383 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1384 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1387 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1389 LISTVIEW_InvalidateRect(infoPtr, NULL);
1392 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1396 if(!is_redrawing(infoPtr)) return;
1397 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1398 rcCol.top = infoPtr->rcList.top;
1399 rcCol.bottom = infoPtr->rcList.bottom;
1400 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1405 * Retrieves the number of items that can fit vertically in the client area.
1408 * [I] infoPtr : valid pointer to the listview structure
1411 * Number of items per row.
1413 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1415 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1417 return max(nListWidth/infoPtr->nItemWidth, 1);
1422 * Retrieves the number of items that can fit horizontally in the client
1426 * [I] infoPtr : valid pointer to the listview structure
1429 * Number of items per column.
1431 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1433 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1435 return max(nListHeight / infoPtr->nItemHeight, 1);
1439 /*************************************************************************
1440 * LISTVIEW_ProcessLetterKeys
1442 * Processes keyboard messages generated by pressing the letter keys
1444 * What this does is perform a case insensitive search from the
1445 * current position with the following quirks:
1446 * - If two chars or more are pressed in quick succession we search
1447 * for the corresponding string (e.g. 'abc').
1448 * - If there is a delay we wipe away the current search string and
1449 * restart with just that char.
1450 * - If the user keeps pressing the same character, whether slowly or
1451 * fast, so that the search string is entirely composed of this
1452 * character ('aaaaa' for instance), then we search for first item
1453 * that starting with that character.
1454 * - If the user types the above character in quick succession, then
1455 * we must also search for the corresponding string ('aaaaa'), and
1456 * go to that string if there is a match.
1459 * [I] hwnd : handle to the window
1460 * [I] charCode : the character code, the actual character
1461 * [I] keyData : key data
1469 * - The current implementation has a list of characters it will
1470 * accept and it ignores averything else. In particular it will
1471 * ignore accentuated characters which seems to match what
1472 * Windows does. But I'm not sure it makes sense to follow
1474 * - We don't sound a beep when the search fails.
1478 * TREEVIEW_ProcessLetterKeys
1480 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1485 WCHAR buffer[MAX_PATH];
1486 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1488 /* simple parameter checking */
1489 if (!charCode || !keyData) return 0;
1491 /* only allow the valid WM_CHARs through */
1492 if (!isalnum(charCode) &&
1493 charCode != '.' && charCode != '`' && charCode != '!' &&
1494 charCode != '@' && charCode != '#' && 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 != '~')
1504 /* if there's one item or less, there is no where to go */
1505 if (infoPtr->nItemCount <= 1) return 0;
1507 /* update the search parameters */
1508 infoPtr->lastKeyPressTimestamp = GetTickCount();
1509 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1510 if (infoPtr->nSearchParamLength < MAX_PATH)
1511 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1512 if (infoPtr->charCode != charCode)
1513 infoPtr->charCode = charCode = 0;
1515 infoPtr->charCode=charCode;
1516 infoPtr->szSearchParam[0]=charCode;
1517 infoPtr->nSearchParamLength=1;
1518 /* Redundant with the 1 char string */
1522 /* and search from the current position */
1524 if (infoPtr->nFocusedItem >= 0) {
1525 endidx=infoPtr->nFocusedItem;
1527 /* if looking for single character match,
1528 * then we must always move forward
1530 if (infoPtr->nSearchParamLength == 1)
1533 endidx=infoPtr->nItemCount;
1537 if (idx == infoPtr->nItemCount) {
1538 if (endidx == infoPtr->nItemCount || endidx == 0)
1544 item.mask = LVIF_TEXT;
1547 item.pszText = buffer;
1548 item.cchTextMax = MAX_PATH;
1549 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1551 /* check for a match */
1552 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1555 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1556 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1557 /* This would work but we must keep looking for a longer match */
1561 } while (idx != endidx);
1564 LISTVIEW_KeySelection(infoPtr, nItem);
1569 /*************************************************************************
1570 * LISTVIEW_UpdateHeaderSize [Internal]
1572 * Function to resize the header control
1575 * [I] hwnd : handle to a window
1576 * [I] nNewScrollPos : scroll pos to set
1581 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1586 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1588 GetWindowRect(infoPtr->hwndHeader, &winRect);
1589 point[0].x = winRect.left;
1590 point[0].y = winRect.top;
1591 point[1].x = winRect.right;
1592 point[1].y = winRect.bottom;
1594 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1595 point[0].x = -nNewScrollPos;
1596 point[1].x += nNewScrollPos;
1598 SetWindowPos(infoPtr->hwndHeader,0,
1599 point[0].x,point[0].y,point[1].x,point[1].y,
1600 SWP_NOZORDER | SWP_NOACTIVATE);
1605 * Update the scrollbars. This functions should be called whenever
1606 * the content, size or view changes.
1609 * [I] infoPtr : valid pointer to the listview structure
1614 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1616 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1617 SCROLLINFO horzInfo, vertInfo;
1619 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1621 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1622 horzInfo.cbSize = sizeof(SCROLLINFO);
1623 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1625 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1626 if (uView == LVS_LIST)
1628 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1629 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1631 /* scroll by at least one column per page */
1632 if(horzInfo.nPage < infoPtr->nItemWidth)
1633 horzInfo.nPage = infoPtr->nItemWidth;
1635 horzInfo.nPage /= infoPtr->nItemWidth;
1637 else if (uView == LVS_REPORT)
1639 horzInfo.nMax = infoPtr->nItemWidth;
1641 else /* LVS_ICON, or LVS_SMALLICON */
1645 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1648 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1649 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1650 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1651 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1653 /* Setting the horizontal scroll can change the listview size
1654 * (and potentially everything else) so we need to recompute
1655 * everything again for the vertical scroll
1658 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1659 vertInfo.cbSize = sizeof(SCROLLINFO);
1660 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1662 if (uView == LVS_REPORT)
1664 vertInfo.nMax = infoPtr->nItemCount;
1666 /* scroll by at least one page */
1667 if(vertInfo.nPage < infoPtr->nItemHeight)
1668 vertInfo.nPage = infoPtr->nItemHeight;
1670 vertInfo.nPage /= infoPtr->nItemHeight;
1672 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1676 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1679 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1680 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1681 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1682 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1684 /* Update the Header Control */
1685 if (uView == LVS_REPORT)
1687 horzInfo.fMask = SIF_POS;
1688 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1689 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1696 * Shows/hides the focus rectangle.
1699 * [I] infoPtr : valid pointer to the listview structure
1700 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1705 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1707 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1710 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1712 if (infoPtr->nFocusedItem < 0) return;
1714 /* we need some gymnastics in ICON mode to handle large items */
1715 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1719 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1720 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1722 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1727 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1729 /* for some reason, owner draw should work only in report mode */
1730 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1735 item.iItem = infoPtr->nFocusedItem;
1737 item.mask = LVIF_PARAM;
1738 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1740 ZeroMemory(&dis, sizeof(dis));
1741 dis.CtlType = ODT_LISTVIEW;
1742 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1743 dis.itemID = item.iItem;
1744 dis.itemAction = ODA_FOCUS;
1745 if (fShow) dis.itemState |= ODS_FOCUS;
1746 dis.hwndItem = infoPtr->hwndSelf;
1748 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1749 dis.itemData = item.lParam;
1751 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1755 DrawFocusRect(hdc, &infoPtr->rcFocus);
1758 ReleaseDC(infoPtr->hwndSelf, hdc);
1762 * Invalidates all visible selected items.
1764 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1768 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1769 while(iterator_next(&i))
1771 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1772 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1774 iterator_destroy(&i);
1779 * DESCRIPTION: [INTERNAL]
1780 * Computes an item's (left,top) corner, relative to rcView.
1781 * That is, the position has NOT been made relative to the Origin.
1782 * This is deliberate, to avoid computing the Origin over, and
1783 * over again, when this function is call in a loop. Instead,
1784 * one ca factor the computation of the Origin before the loop,
1785 * and offset the value retured by this function, on every iteration.
1788 * [I] infoPtr : valid pointer to the listview structure
1789 * [I] nItem : item number
1790 * [O] lpptOrig : item top, left corner
1795 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1797 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1799 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1801 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1803 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1804 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1806 else if (uView == LVS_LIST)
1808 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1809 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1810 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1812 else /* LVS_REPORT */
1814 lpptPosition->x = 0;
1815 lpptPosition->y = nItem * infoPtr->nItemHeight;
1820 * DESCRIPTION: [INTERNAL]
1821 * Compute the rectangles of an item. This is to localize all
1822 * the computations in one place. If you are not interested in some
1823 * of these values, simply pass in a NULL -- the fucntion is smart
1824 * enough to compute only what's necessary. The function computes
1825 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1826 * one, the BOX rectangle. This rectangle is very cheap to compute,
1827 * and is guaranteed to contain all the other rectangles. Computing
1828 * the ICON rect is also cheap, but all the others are potentaily
1829 * expensive. This gives an easy and effective optimization when
1830 * searching (like point inclusion, or rectangle intersection):
1831 * first test against the BOX, and if TRUE, test agains the desired
1833 * If the function does not have all the necessary information
1834 * to computed the requested rectangles, will crash with a
1835 * failed assertion. This is done so we catch all programming
1836 * errors, given that the function is called only from our code.
1838 * We have the following 'special' meanings for a few fields:
1839 * * If LVIS_FOCUSED is set, we assume the item has the focus
1840 * This is important in ICON mode, where it might get a larger
1841 * then usual rectange
1843 * Please note that subitem support works only in REPORT mode.
1846 * [I] infoPtr : valid pointer to the listview structure
1847 * [I] lpLVItem : item to compute the measures for
1848 * [O] lprcBox : ptr to Box rectangle
1849 * The internal LVIR_BOX rectangle
1850 * [0] lprcState : ptr to State icon rectangle
1851 * The internal LVIR_STATE rectangle
1852 * [O] lprcIcon : ptr to Icon rectangle
1853 * Same as LVM_GETITEMRECT with LVIR_ICON
1854 * [O] lprcLabel : ptr to Label rectangle
1855 * Same as LVM_GETITEMRECT with LVIR_LABEL
1860 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1861 LPRECT lprcBox, LPRECT lprcState,
1862 LPRECT lprcIcon, LPRECT lprcLabel)
1864 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1865 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1866 RECT Box, State, Icon, Label;
1867 COLUMN_INFO *lpColumnInfo = NULL;
1869 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1871 /* Be smart and try to figure out the minimum we have to do */
1872 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1873 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1875 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1876 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1878 if (lprcLabel) doLabel = TRUE;
1879 if (doLabel || lprcIcon) doIcon = TRUE;
1880 if (doIcon || lprcState) doState = TRUE;
1882 /************************************************************/
1883 /* compute the box rectangle (it should be cheap to do) */
1884 /************************************************************/
1885 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1886 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1888 if (lpLVItem->iSubItem)
1890 Box = lpColumnInfo->rcHeader;
1895 Box.right = infoPtr->nItemWidth;
1898 Box.bottom = infoPtr->nItemHeight;
1900 /************************************************************/
1901 /* compute STATEICON bounding box */
1902 /************************************************************/
1905 if (uView == LVS_ICON)
1907 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1908 if (infoPtr->himlNormal)
1909 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1910 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1914 /* we need the ident in report mode, if we don't have it, we fail */
1915 State.left = Box.left;
1916 if (uView == LVS_REPORT)
1918 if (lpLVItem->iSubItem == 0)
1920 State.left += REPORT_MARGINX;
1921 assert(lpLVItem->mask & LVIF_INDENT);
1922 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1925 State.top = Box.top;
1927 State.right = State.left;
1928 State.bottom = State.top;
1929 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1931 State.right += infoPtr->iconStateSize.cx;
1932 State.bottom += infoPtr->iconStateSize.cy;
1934 if (lprcState) *lprcState = State;
1935 TRACE(" - state=%s\n", debugrect(&State));
1938 /************************************************************/
1939 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1940 /************************************************************/
1943 if (uView == LVS_ICON)
1945 Icon.left = Box.left;
1946 if (infoPtr->himlNormal)
1947 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1948 Icon.top = Box.top + ICON_TOP_PADDING;
1949 Icon.right = Icon.left;
1950 Icon.bottom = Icon.top;
1951 if (infoPtr->himlNormal)
1953 Icon.right += infoPtr->iconSize.cx;
1954 Icon.bottom += infoPtr->iconSize.cy;
1957 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1959 Icon.left = State.right;
1961 Icon.right = Icon.left;
1962 if (infoPtr->himlSmall &&
1963 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1964 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1965 Icon.right += infoPtr->iconSize.cx;
1966 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1968 if(lprcIcon) *lprcIcon = Icon;
1969 TRACE(" - icon=%s\n", debugrect(&Icon));
1972 /************************************************************/
1973 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1974 /************************************************************/
1977 SIZE labelSize = { 0, 0 };
1979 /* calculate how far to the right can the label strech */
1980 Label.right = Box.right;
1981 if (uView == LVS_REPORT)
1983 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1986 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1988 labelSize.cx = infoPtr->nItemWidth;
1989 labelSize.cy = infoPtr->nItemHeight;
1993 /* we need the text in non owner draw mode */
1994 assert(lpLVItem->mask & LVIF_TEXT);
1995 if (is_textT(lpLVItem->pszText, TRUE))
1997 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1998 HDC hdc = GetDC(infoPtr->hwndSelf);
1999 HFONT hOldFont = SelectObject(hdc, hFont);
2003 /* compute rough rectangle where the label will go */
2004 SetRectEmpty(&rcText);
2005 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2006 rcText.bottom = infoPtr->nItemHeight;
2007 if (uView == LVS_ICON)
2008 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2010 /* now figure out the flags */
2011 if (uView == LVS_ICON)
2012 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2014 uFormat = LV_SL_DT_FLAGS;
2016 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2018 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2019 labelSize.cy = rcText.bottom - rcText.top;
2021 SelectObject(hdc, hOldFont);
2022 ReleaseDC(infoPtr->hwndSelf, hdc);
2026 if (uView == LVS_ICON)
2028 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2029 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2030 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2031 Label.right = Label.left + labelSize.cx;
2032 Label.bottom = Label.top + infoPtr->nItemHeight;
2033 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2035 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2036 labelSize.cy /= infoPtr->ntmHeight;
2037 labelSize.cy = max(labelSize.cy, 1);
2038 labelSize.cy *= infoPtr->ntmHeight;
2040 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2042 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2044 Label.left = Icon.right;
2045 Label.top = Box.top;
2046 Label.right = min(Label.left + labelSize.cx, Label.right);
2047 Label.bottom = Label.top + infoPtr->nItemHeight;
2050 if (lprcLabel) *lprcLabel = Label;
2051 TRACE(" - label=%s\n", debugrect(&Label));
2054 /* Fix the Box if necessary */
2057 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2058 else *lprcBox = Box;
2060 TRACE(" - box=%s\n", debugrect(&Box));
2064 * DESCRIPTION: [INTERNAL]
2067 * [I] infoPtr : valid pointer to the listview structure
2068 * [I] nItem : item number
2069 * [O] lprcBox : ptr to Box rectangle
2074 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2076 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2077 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2078 POINT Position, Origin;
2081 LISTVIEW_GetOrigin(infoPtr, &Origin);
2082 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2084 /* Be smart and try to figure out the minimum we have to do */
2086 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2087 lvItem.mask |= LVIF_TEXT;
2088 lvItem.iItem = nItem;
2089 lvItem.iSubItem = 0;
2090 lvItem.pszText = szDispText;
2091 lvItem.cchTextMax = DISP_TEXT_SIZE;
2092 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2093 if (uView == LVS_ICON)
2095 lvItem.mask |= LVIF_STATE;
2096 lvItem.stateMask = LVIS_FOCUSED;
2097 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2099 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2101 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2107 * Returns the current icon position, and advances it along the top.
2108 * The returned position is not offset by Origin.
2111 * [I] infoPtr : valid pointer to the listview structure
2112 * [O] lpPos : will get the current icon position
2117 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2119 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2121 *lpPos = infoPtr->currIconPos;
2123 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2124 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2126 infoPtr->currIconPos.x = 0;
2127 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2133 * Returns the current icon position, and advances it down the left edge.
2134 * The returned position is not offset by Origin.
2137 * [I] infoPtr : valid pointer to the listview structure
2138 * [O] lpPos : will get the current icon position
2143 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2145 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2147 *lpPos = infoPtr->currIconPos;
2149 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2150 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2152 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2153 infoPtr->currIconPos.y = 0;
2159 * Moves an icon to the specified position.
2160 * It takes care of invalidating the item, etc.
2163 * [I] infoPtr : valid pointer to the listview structure
2164 * [I] nItem : the item to move
2165 * [I] lpPos : the new icon position
2166 * [I] isNew : flags the item as being new
2172 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2178 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2179 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2181 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2182 LISTVIEW_InvalidateItem(infoPtr, nItem);
2185 /* Allocating a POINTER for every item is too resource intensive,
2186 * so we'll keep the (x,y) in different arrays */
2187 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2188 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2190 LISTVIEW_InvalidateItem(infoPtr, nItem);
2197 * Arranges listview items in icon display mode.
2200 * [I] infoPtr : valid pointer to the listview structure
2201 * [I] nAlignCode : alignment code
2207 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2209 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2210 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2214 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2216 TRACE("nAlignCode=%d\n", nAlignCode);
2218 if (nAlignCode == LVA_DEFAULT)
2220 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2221 else nAlignCode = LVA_ALIGNTOP;
2226 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2227 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2228 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2229 default: return FALSE;
2232 infoPtr->bAutoarrange = TRUE;
2233 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2234 for (i = 0; i < infoPtr->nItemCount; i++)
2236 next_pos(infoPtr, &pos);
2237 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2245 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2248 * [I] infoPtr : valid pointer to the listview structure
2249 * [O] lprcView : bounding rectangle
2255 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2259 SetRectEmpty(lprcView);
2261 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2265 for (i = 0; i < infoPtr->nItemCount; i++)
2267 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2268 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2269 lprcView->right = max(lprcView->right, x);
2270 lprcView->bottom = max(lprcView->bottom, y);
2272 if (infoPtr->nItemCount > 0)
2274 lprcView->right += infoPtr->nItemWidth;
2275 lprcView->bottom += infoPtr->nItemHeight;
2280 y = LISTVIEW_GetCountPerColumn(infoPtr);
2281 x = infoPtr->nItemCount / y;
2282 if (infoPtr->nItemCount % y) x++;
2283 lprcView->right = x * infoPtr->nItemWidth;
2284 lprcView->bottom = y * infoPtr->nItemHeight;
2288 lprcView->right = infoPtr->nItemWidth;
2289 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2296 * Retrieves the bounding rectangle of all the items.
2299 * [I] infoPtr : valid pointer to the listview structure
2300 * [O] lprcView : bounding rectangle
2306 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2310 TRACE("(lprcView=%p)\n", lprcView);
2312 if (!lprcView) return FALSE;
2314 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2315 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2316 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2318 TRACE("lprcView=%s\n", debugrect(lprcView));
2325 * Retrieves the subitem pointer associated with the subitem index.
2328 * [I] hdpaSubItems : DPA handle for a specific item
2329 * [I] nSubItem : index of subitem
2332 * SUCCESS : subitem pointer
2335 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2337 SUBITEM_INFO *lpSubItem;
2340 /* we should binary search here if need be */
2341 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2343 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2344 if (lpSubItem->iSubItem == nSubItem)
2354 * Caclulates the desired item width.
2357 * [I] infoPtr : valid pointer to the listview structure
2360 * The desired item width.
2362 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2364 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2367 TRACE("uView=%d\n", uView);
2369 if (uView == LVS_ICON)
2370 nItemWidth = infoPtr->iconSpacing.cx;
2371 else if (uView == LVS_REPORT)
2375 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2377 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2378 nItemWidth = rcHeader.right;
2381 else /* LVS_SMALLICON, or LVS_LIST */
2385 for (i = 0; i < infoPtr->nItemCount; i++)
2386 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2388 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2389 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2391 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2394 return max(nItemWidth, 1);
2399 * Caclulates the desired item height.
2402 * [I] infoPtr : valid pointer to the listview structure
2405 * The desired item height.
2407 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2409 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2412 TRACE("uView=%d\n", uView);
2414 if (uView == LVS_ICON)
2415 nItemHeight = infoPtr->iconSpacing.cy;
2418 nItemHeight = infoPtr->ntmHeight;
2419 if (infoPtr->himlState)
2420 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2421 if (infoPtr->himlSmall)
2422 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2423 if (infoPtr->himlState || infoPtr->himlSmall)
2424 nItemHeight += HEIGHT_PADDING;
2427 return max(nItemHeight, 1);
2432 * Updates the width, and height of an item.
2435 * [I] infoPtr : valid pointer to the listview structure
2440 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2442 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2443 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2449 * Retrieves and saves important text metrics info for the current
2453 * [I] infoPtr : valid pointer to the listview structure
2456 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2458 HDC hdc = GetDC(infoPtr->hwndSelf);
2459 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2460 HFONT hOldFont = SelectObject(hdc, hFont);
2463 if (GetTextMetricsW(hdc, &tm))
2465 infoPtr->ntmHeight = tm.tmHeight;
2466 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2468 SelectObject(hdc, hOldFont);
2469 ReleaseDC(infoPtr->hwndSelf, hdc);
2471 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2476 * A compare function for ranges
2479 * [I] range1 : pointer to range 1;
2480 * [I] range2 : pointer to range 2;
2484 * > 0 : if range 1 > range 2
2485 * < 0 : if range 2 > range 1
2486 * = 0 : if range intersects range 2
2488 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2492 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2494 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2499 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2505 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2507 #define ranges_check(ranges, desc) do { } while(0)
2510 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2515 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2517 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2518 ranges_dump(ranges);
2519 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2520 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2521 assert (prev->lower >= 0 && prev->lower < prev->upper);
2522 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2524 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2525 assert (prev->upper <= curr->lower);
2526 assert (curr->lower < curr->upper);
2529 TRACE("--- Done checking---\n");
2532 static RANGES ranges_create(int count)
2534 RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2535 if (!ranges) return NULL;
2536 ranges->hdpa = DPA_Create(count);
2537 if (ranges->hdpa) return ranges;
2542 static void ranges_clear(RANGES ranges)
2546 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2547 Free(DPA_GetPtr(ranges->hdpa, i));
2548 DPA_DeleteAllPtrs(ranges->hdpa);
2552 static void ranges_destroy(RANGES ranges)
2554 if (!ranges) return;
2555 ranges_clear(ranges);
2556 DPA_Destroy(ranges->hdpa);
2560 static RANGES ranges_clone(RANGES ranges)
2565 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2567 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2569 RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2570 if (!newrng) goto fail;
2571 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2572 DPA_SetPtr(clone->hdpa, i, newrng);
2577 TRACE ("clone failed\n");
2578 ranges_destroy(clone);
2582 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2586 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2587 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2592 static void ranges_dump(RANGES ranges)
2596 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2597 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2600 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2602 RANGE srchrng = { nItem, nItem + 1 };
2604 TRACE("(nItem=%d)\n", nItem);
2605 ranges_check(ranges, "before contain");
2606 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2609 static INT ranges_itemcount(RANGES ranges)
2613 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2615 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2616 count += sel->upper - sel->lower;
2622 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2624 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2627 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2628 if (index == -1) return TRUE;
2630 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2632 chkrng = DPA_GetPtr(ranges->hdpa, index);
2633 if (chkrng->lower >= nItem)
2634 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2635 if (chkrng->upper > nItem)
2636 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2641 static BOOL ranges_add(RANGES ranges, RANGE range)
2646 TRACE("(%s)\n", debugrange(&range));
2647 ranges_check(ranges, "before add");
2649 /* try find overlapping regions first */
2650 srchrgn.lower = range.lower - 1;
2651 srchrgn.upper = range.upper + 1;
2652 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2658 TRACE("Adding new range\n");
2660 /* create the brand new range to insert */
2661 newrgn = (RANGE *)Alloc(sizeof(RANGE));
2662 if(!newrgn) goto fail;
2665 /* figure out where to insert it */
2666 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2667 TRACE("index=%d\n", index);
2668 if (index == -1) index = 0;
2670 /* and get it over with */
2671 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2679 RANGE *chkrgn, *mrgrgn;
2680 INT fromindex, mergeindex;
2682 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2683 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2685 chkrgn->lower = min(range.lower, chkrgn->lower);
2686 chkrgn->upper = max(range.upper, chkrgn->upper);
2688 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2690 /* merge now common anges */
2692 srchrgn.lower = chkrgn->lower - 1;
2693 srchrgn.upper = chkrgn->upper + 1;
2697 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2698 if (mergeindex == -1) break;
2699 if (mergeindex == index)
2701 fromindex = index + 1;
2705 TRACE("Merge with index %i\n", mergeindex);
2707 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2708 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2709 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2711 DPA_DeletePtr(ranges->hdpa, mergeindex);
2712 if (mergeindex < index) index --;
2716 ranges_check(ranges, "after add");
2720 ranges_check(ranges, "failed add");
2724 static BOOL ranges_del(RANGES ranges, RANGE range)
2729 TRACE("(%s)\n", debugrange(&range));
2730 ranges_check(ranges, "before del");
2732 /* we don't use DPAS_SORTED here, since we need *
2733 * to find the first overlapping range */
2734 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2737 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2739 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2741 /* case 1: Same range */
2742 if ( (chkrgn->upper == range.upper) &&
2743 (chkrgn->lower == range.lower) )
2745 DPA_DeletePtr(ranges->hdpa, index);
2748 /* case 2: engulf */
2749 else if ( (chkrgn->upper <= range.upper) &&
2750 (chkrgn->lower >= range.lower) )
2752 DPA_DeletePtr(ranges->hdpa, index);
2754 /* case 3: overlap upper */
2755 else if ( (chkrgn->upper <= range.upper) &&
2756 (chkrgn->lower < range.lower) )
2758 chkrgn->upper = range.lower;
2760 /* case 4: overlap lower */
2761 else if ( (chkrgn->upper > range.upper) &&
2762 (chkrgn->lower >= range.lower) )
2764 chkrgn->lower = range.upper;
2767 /* case 5: fully internal */
2770 RANGE tmprgn = *chkrgn, *newrgn;
2772 if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2773 newrgn->lower = chkrgn->lower;
2774 newrgn->upper = range.lower;
2775 chkrgn->lower = range.upper;
2776 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2785 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2788 ranges_check(ranges, "after del");
2792 ranges_check(ranges, "failed del");
2798 * Removes all selection ranges
2801 * [I] infoPtr : valid pointer to the listview structure
2802 * [I] toSkip : item range to skip removing the selection
2808 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2817 lvItem.stateMask = LVIS_SELECTED;
2819 /* need to clone the DPA because callbacks can change it */
2820 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2821 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2822 while(iterator_next(&i))
2823 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2824 /* note that the iterator destructor will free the cloned range */
2825 iterator_destroy(&i);
2830 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2834 if (!(toSkip = ranges_create(1))) return FALSE;
2835 if (nItem != -1) ranges_additem(toSkip, nItem);
2836 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2837 ranges_destroy(toSkip);
2841 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2843 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2848 * Retrieves the number of items that are marked as selected.
2851 * [I] infoPtr : valid pointer to the listview structure
2854 * Number of items selected.
2856 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2858 INT nSelectedCount = 0;
2860 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2863 for (i = 0; i < infoPtr->nItemCount; i++)
2865 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2870 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2872 TRACE("nSelectedCount=%d\n", nSelectedCount);
2873 return nSelectedCount;
2878 * Manages the item focus.
2881 * [I] infoPtr : valid pointer to the listview structure
2882 * [I] nItem : item index
2885 * TRUE : focused item changed
2886 * FALSE : focused item has NOT changed
2888 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2890 INT oldFocus = infoPtr->nFocusedItem;
2893 if (nItem == infoPtr->nFocusedItem) return FALSE;
2895 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2896 lvItem.stateMask = LVIS_FOCUSED;
2897 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2899 return oldFocus != infoPtr->nFocusedItem;
2902 /* Helper function for LISTVIEW_ShiftIndices *only* */
2903 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2905 if (nShiftItem < nItem) return nShiftItem;
2907 if (nShiftItem > nItem) return nShiftItem + direction;
2909 if (direction > 0) return nShiftItem + direction;
2911 return min(nShiftItem, infoPtr->nItemCount - 1);
2916 * Updates the various indices after an item has been inserted or deleted.
2919 * [I] infoPtr : valid pointer to the listview structure
2920 * [I] nItem : item index
2921 * [I] direction : Direction of shift, +1 or -1.
2926 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2931 /* temporarily disable change notification while shifting items */
2932 bOldChange = infoPtr->bDoChangeNotify;
2933 infoPtr->bDoChangeNotify = FALSE;
2935 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2937 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2939 assert(abs(direction) == 1);
2941 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2943 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2944 if (nNewFocus != infoPtr->nFocusedItem)
2945 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2947 /* But we are not supposed to modify nHotItem! */
2949 infoPtr->bDoChangeNotify = bOldChange;
2955 * Adds a block of selections.
2958 * [I] infoPtr : valid pointer to the listview structure
2959 * [I] nItem : item index
2964 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2966 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2967 INT nLast = max(infoPtr->nSelectionMark, nItem);
2968 NMLVODSTATECHANGE nmlv;
2973 /* Temporarily disable change notification
2974 * If the control is LVS_OWNERDATA, we need to send
2975 * only one LVN_ODSTATECHANGED notification.
2976 * See MSDN documentation for LVN_ITEMCHANGED.
2978 bOldChange = infoPtr->bDoChangeNotify;
2979 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
2981 if (nFirst == -1) nFirst = nItem;
2983 item.state = LVIS_SELECTED;
2984 item.stateMask = LVIS_SELECTED;
2986 for (i = nFirst; i <= nLast; i++)
2987 LISTVIEW_SetItemState(infoPtr,i,&item);
2989 ZeroMemory(&nmlv, sizeof(nmlv));
2990 nmlv.iFrom = nFirst;
2993 nmlv.uOldState = item.state;
2995 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
2996 infoPtr->bDoChangeNotify = bOldChange;
3002 * Sets a single group selection.
3005 * [I] infoPtr : valid pointer to the listview structure
3006 * [I] nItem : item index
3011 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3013 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3018 if (!(selection = ranges_create(100))) return;
3020 item.state = LVIS_SELECTED;
3021 item.stateMask = LVIS_SELECTED;
3023 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3025 if (infoPtr->nSelectionMark == -1)
3027 infoPtr->nSelectionMark = nItem;
3028 ranges_additem(selection, nItem);
3034 sel.lower = min(infoPtr->nSelectionMark, nItem);
3035 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3036 ranges_add(selection, sel);
3041 RECT rcItem, rcSel, rcSelMark;
3044 rcItem.left = LVIR_BOUNDS;
3045 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3046 rcSelMark.left = LVIR_BOUNDS;
3047 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3048 UnionRect(&rcSel, &rcItem, &rcSelMark);
3049 iterator_frameditems(&i, infoPtr, &rcSel);
3050 while(iterator_next(&i))
3052 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3053 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3055 iterator_destroy(&i);
3058 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3059 iterator_rangesitems(&i, selection);
3060 while(iterator_next(&i))
3061 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3062 /* this will also destroy the selection */
3063 iterator_destroy(&i);
3065 LISTVIEW_SetItemFocus(infoPtr, nItem);
3070 * Sets a single selection.
3073 * [I] infoPtr : valid pointer to the listview structure
3074 * [I] nItem : item index
3079 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3083 TRACE("nItem=%d\n", nItem);
3085 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3087 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3088 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3089 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3091 infoPtr->nSelectionMark = nItem;
3096 * Set selection(s) with keyboard.
3099 * [I] infoPtr : valid pointer to the listview structure
3100 * [I] nItem : item index
3103 * SUCCESS : TRUE (needs to be repainted)
3104 * FAILURE : FALSE (nothing has changed)
3106 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3108 /* FIXME: pass in the state */
3109 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3110 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3111 BOOL bResult = FALSE;
3113 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3115 if (infoPtr->dwStyle & LVS_SINGLESEL)
3118 LISTVIEW_SetSelection(infoPtr, nItem);
3125 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3129 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3134 LISTVIEW_SetSelection(infoPtr, nItem);
3137 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3140 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3147 * Called when the mouse is being actively tracked and has hovered for a specified
3151 * [I] infoPtr : valid pointer to the listview structure
3152 * [I] fwKeys : key indicator
3153 * [I] x,y : mouse position
3156 * 0 if the message was processed, non-zero if there was an error
3159 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3160 * over the item for a certain period of time.
3163 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, INT x, INT y)
3165 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3166 /* FIXME: select the item!!! */
3167 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3174 * Called whenever WM_MOUSEMOVE is received.
3177 * [I] infoPtr : valid pointer to the listview structure
3178 * [I] fwKeys : key indicator
3179 * [I] x,y : mouse position
3182 * 0 if the message is processed, non-zero if there was an error
3184 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3186 TRACKMOUSEEVENT trackinfo;
3188 /* see if we are supposed to be tracking mouse hovering */
3189 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3190 /* fill in the trackinfo struct */
3191 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3192 trackinfo.dwFlags = TME_QUERY;
3193 trackinfo.hwndTrack = infoPtr->hwndSelf;
3194 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3196 /* see if we are already tracking this hwnd */
3197 _TrackMouseEvent(&trackinfo);
3199 if(!(trackinfo.dwFlags & TME_HOVER)) {
3200 trackinfo.dwFlags = TME_HOVER;
3202 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3203 _TrackMouseEvent(&trackinfo);
3212 * Tests wheather the item is assignable to a list with style lStyle
3214 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3216 if ( (lpLVItem->mask & LVIF_TEXT) &&
3217 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3218 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3226 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3229 * [I] infoPtr : valid pointer to the listview structure
3230 * [I] lpLVItem : valid pointer to new item atttributes
3231 * [I] isNew : the item being set is being inserted
3232 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3233 * [O] bChanged : will be set to TRUE if the item really changed
3239 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3241 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3249 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3251 if (lpLVItem->mask == 0) return TRUE;
3253 if (infoPtr->dwStyle & LVS_OWNERDATA)
3255 /* a virtual listview we stores only selection and focus */
3256 if (lpLVItem->mask & ~LVIF_STATE)
3262 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3263 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3267 /* we need to get the lParam and state of the item */
3268 item.iItem = lpLVItem->iItem;
3269 item.iSubItem = lpLVItem->iSubItem;
3270 item.mask = LVIF_STATE | LVIF_PARAM;
3271 item.stateMask = ~0;
3274 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3276 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3277 /* determine what fields will change */
3278 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3279 uChanged |= LVIF_STATE;
3281 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3282 uChanged |= LVIF_IMAGE;
3284 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3285 uChanged |= LVIF_PARAM;
3287 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3288 uChanged |= LVIF_INDENT;
3290 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3291 uChanged |= LVIF_TEXT;
3293 TRACE("uChanged=0x%x\n", uChanged);
3294 if (!uChanged) return TRUE;
3297 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3298 nmlv.iItem = lpLVItem->iItem;
3299 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3300 nmlv.uOldState = item.state;
3301 nmlv.uChanged = uChanged;
3302 nmlv.lParam = item.lParam;
3304 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3305 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3307 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3308 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3311 /* copy information */
3312 if (lpLVItem->mask & LVIF_TEXT)
3313 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3315 if (lpLVItem->mask & LVIF_IMAGE)
3316 lpItem->hdr.iImage = lpLVItem->iImage;
3318 if (lpLVItem->mask & LVIF_PARAM)
3319 lpItem->lParam = lpLVItem->lParam;
3321 if (lpLVItem->mask & LVIF_INDENT)
3322 lpItem->iIndent = lpLVItem->iIndent;
3324 if (uChanged & LVIF_STATE)
3326 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3328 lpItem->state &= ~lpLVItem->stateMask;
3329 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3331 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3333 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3334 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3336 else if (lpLVItem->stateMask & LVIS_SELECTED)
3337 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3339 /* if we are asked to change focus, and we manage it, do it */
3340 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3342 if (lpLVItem->state & LVIS_FOCUSED)
3344 LISTVIEW_SetItemFocus(infoPtr, -1);
3345 infoPtr->nFocusedItem = lpLVItem->iItem;
3346 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3348 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3349 infoPtr->nFocusedItem = -1;
3353 /* if we're inserting the item, we're done */
3354 if (isNew) return TRUE;
3356 /* send LVN_ITEMCHANGED notification */
3357 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3358 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3365 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3368 * [I] infoPtr : valid pointer to the listview structure
3369 * [I] lpLVItem : valid pointer to new subitem atttributes
3370 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3371 * [O] bChanged : will be set to TRUE if the item really changed
3377 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3380 SUBITEM_INFO *lpSubItem;
3382 /* we do not support subitems for virtual listviews */
3383 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3385 /* set subitem only if column is present */
3386 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3388 /* First do some sanity checks */
3389 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3390 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3392 /* get the subitem structure, and create it if not there */
3393 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3394 assert (hdpaSubItems);
3396 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3399 SUBITEM_INFO *tmpSubItem;
3402 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3403 if (!lpSubItem) return FALSE;
3404 /* we could binary search here, if need be...*/
3405 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3407 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3408 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3410 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3415 lpSubItem->iSubItem = lpLVItem->iSubItem;
3416 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3420 if (lpLVItem->mask & LVIF_IMAGE)
3421 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3423 lpSubItem->hdr.iImage = lpLVItem->iImage;
3427 if (lpLVItem->mask & LVIF_TEXT)
3428 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3430 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3439 * Sets item attributes.
3442 * [I] infoPtr : valid pointer to the listview structure
3443 * [I] lpLVItem : new item atttributes
3444 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3450 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3452 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3453 LPWSTR pszText = NULL;
3454 BOOL bResult, bChanged = FALSE;
3456 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3458 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3461 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3462 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3464 pszText = lpLVItem->pszText;
3465 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3468 /* actually set the fields */
3469 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3471 if (lpLVItem->iSubItem)
3472 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3474 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3476 /* redraw item, if necessary */
3477 if (bChanged && !infoPtr->bIsDrawing)
3479 /* this little optimization eliminates some nasty flicker */
3480 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3481 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3482 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3484 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3489 textfreeT(lpLVItem->pszText, isW);
3490 ((LVITEMW *)lpLVItem)->pszText = pszText;
3498 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3501 * [I] infoPtr : valid pointer to the listview structure
3506 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3508 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3510 SCROLLINFO scrollInfo;
3512 scrollInfo.cbSize = sizeof(SCROLLINFO);
3513 scrollInfo.fMask = SIF_POS;
3515 if (uView == LVS_LIST)
3517 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3518 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3520 else if (uView == LVS_REPORT)
3522 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3523 nItem = scrollInfo.nPos;
3527 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3528 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3531 TRACE("nItem=%d\n", nItem);
3539 * Erases the background of the given rectangle
3542 * [I] infoPtr : valid pointer to the listview structure
3543 * [I] hdc : device context handle
3544 * [I] lprcBox : clipping rectangle
3550 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3552 if (!infoPtr->hBkBrush) return FALSE;
3554 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3556 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3564 * [I] infoPtr : valid pointer to the listview structure
3565 * [I] hdc : device context handle
3566 * [I] nItem : item index
3567 * [I] nSubItem : subitem index
3568 * [I] pos : item position in client coordinates
3569 * [I] cdmode : custom draw mode
3575 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3577 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3578 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3579 static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3580 DWORD cdsubitemmode = CDRF_DODEFAULT;
3581 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3582 NMLVCUSTOMDRAW nmlvcd;
3586 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3588 /* get information needed for drawing the item */
3589 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3590 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3591 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3592 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3593 lvItem.iItem = nItem;
3594 lvItem.iSubItem = nSubItem;
3597 lvItem.cchTextMax = DISP_TEXT_SIZE;
3598 lvItem.pszText = szDispText;
3599 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3600 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3601 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3602 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3603 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3605 /* now check if we need to update the focus rectangle */
3606 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3608 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3609 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3610 OffsetRect(&rcBox, pos.x, pos.y);
3611 OffsetRect(&rcState, pos.x, pos.y);
3612 OffsetRect(&rcIcon, pos.x, pos.y);
3613 OffsetRect(&rcLabel, pos.x, pos.y);
3614 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3615 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3617 /* fill in the custom draw structure */
3618 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3620 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3621 if (cdmode & CDRF_NOTIFYITEMDRAW)
3622 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3623 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3624 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3625 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3626 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3628 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3629 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3631 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3632 prepaint_setup(infoPtr, hdc, &nmlvcd);
3634 /* in full row select, subitems, will just use main item's colors */
3635 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3636 nmlvcd.clrTextBk = CLR_NONE;
3639 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3641 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3644 TRACE("uStateImage=%d\n", uStateImage);
3645 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3650 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3651 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3653 TRACE("iImage=%d\n", lvItem.iImage);
3654 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3655 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3658 /* Don't bother painting item being edited */
3659 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3661 /* draw the selection background, if we're drawing the main item */
3665 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3666 rcSelect.right = rcBox.right;
3668 if (nmlvcd.clrTextBk != CLR_NONE)
3669 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3670 if(lprcFocus) *lprcFocus = rcSelect;
3673 /* figure out the text drawing flags */
3674 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3675 if (uView == LVS_ICON)
3676 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3679 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3681 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3682 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3683 default: uFormat |= DT_LEFT;
3686 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3688 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3689 else rcLabel.left += LABEL_HOR_PADDING;
3691 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3692 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3695 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3696 notify_postpaint(infoPtr, &nmlvcd);
3702 * Draws listview items when in owner draw mode.
3705 * [I] infoPtr : valid pointer to the listview structure
3706 * [I] hdc : device context handle
3711 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3713 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3714 DWORD cditemmode = CDRF_DODEFAULT;
3715 NMLVCUSTOMDRAW nmlvcd;
3716 POINT Origin, Position;
3722 ZeroMemory(&dis, sizeof(dis));
3724 /* Get scroll info once before loop */
3725 LISTVIEW_GetOrigin(infoPtr, &Origin);
3727 /* iterate through the invalidated rows */
3728 while(iterator_next(i))
3730 item.iItem = i->nItem;
3732 item.mask = LVIF_PARAM | LVIF_STATE;
3733 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3734 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3736 dis.CtlType = ODT_LISTVIEW;
3738 dis.itemID = item.iItem;
3739 dis.itemAction = ODA_DRAWENTIRE;
3741 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3742 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3743 dis.hwndItem = infoPtr->hwndSelf;
3745 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3746 dis.rcItem.left = Position.x + Origin.x;
3747 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3748 dis.rcItem.top = Position.y + Origin.y;
3749 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3750 dis.itemData = item.lParam;
3752 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3755 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3756 * structure for the rest. of the paint cycle
3758 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3759 if (cdmode & CDRF_NOTIFYITEMDRAW)
3760 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3762 if (!(cditemmode & CDRF_SKIPDEFAULT))
3764 prepaint_setup (infoPtr, hdc, &nmlvcd);
3765 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3768 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3769 notify_postpaint(infoPtr, &nmlvcd);
3775 * Draws listview items when in report display mode.
3778 * [I] infoPtr : valid pointer to the listview structure
3779 * [I] hdc : device context handle
3780 * [I] cdmode : custom draw mode
3785 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3788 RECT rcClip, rcItem;
3789 POINT Origin, Position;
3795 /* figure out what to draw */
3796 rgntype = GetClipBox(hdc, &rcClip);
3797 if (rgntype == NULLREGION) return;
3799 /* Get scroll info once before loop */
3800 LISTVIEW_GetOrigin(infoPtr, &Origin);
3802 /* narrow down the columns we need to paint */
3803 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3805 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3806 if (rcItem.right + Origin.x >= rcClip.left) break;
3808 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3810 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3811 if (rcItem.left + Origin.x < rcClip.right) break;
3813 iterator_rangeitems(&j, colRange);
3815 /* in full row select, we _have_ to draw the main item */
3816 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3819 /* iterate through the invalidated rows */
3820 while(iterator_next(i))
3822 /* iterate through the invalidated columns */
3823 while(iterator_next(&j))
3825 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3826 Position.x += Origin.x;
3827 Position.y += Origin.y;
3829 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3831 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3833 rcItem.bottom = infoPtr->nItemHeight;
3834 OffsetRect(&rcItem, Position.x, Position.y);
3835 if (!RectVisible(hdc, &rcItem)) continue;
3838 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3841 iterator_destroy(&j);
3846 * Draws listview items when in list display mode.
3849 * [I] infoPtr : valid pointer to the listview structure
3850 * [I] hdc : device context handle
3851 * [I] cdmode : custom draw mode
3856 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3858 POINT Origin, Position;
3860 /* Get scroll info once before loop */
3861 LISTVIEW_GetOrigin(infoPtr, &Origin);
3863 while(iterator_prev(i))
3865 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3866 Position.x += Origin.x;
3867 Position.y += Origin.y;
3869 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3876 * Draws listview items.
3879 * [I] infoPtr : valid pointer to the listview structure
3880 * [I] hdc : device context handle
3885 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3887 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3888 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3889 NMLVCUSTOMDRAW nmlvcd;
3896 LISTVIEW_DUMP(infoPtr);
3898 infoPtr->bIsDrawing = TRUE;
3900 /* save dc values we're gonna trash while drawing */
3901 hOldFont = SelectObject(hdc, infoPtr->hFont);
3902 oldBkMode = GetBkMode(hdc);
3903 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3904 oldTextColor = GetTextColor(hdc);
3906 oldClrTextBk = infoPtr->clrTextBk;
3907 oldClrText = infoPtr->clrText;
3909 infoPtr->cditemmode = CDRF_DODEFAULT;
3911 GetClientRect(infoPtr->hwndSelf, &rcClient);
3912 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3913 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3914 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3915 prepaint_setup(infoPtr, hdc, &nmlvcd);
3917 /* Use these colors to draw the items */
3918 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3919 infoPtr->clrText = nmlvcd.clrText;
3921 /* nothing to draw */
3922 if(infoPtr->nItemCount == 0) goto enddraw;
3924 /* figure out what we need to draw */
3925 iterator_visibleitems(&i, infoPtr, hdc);
3927 /* send cache hint notification */
3928 if (infoPtr->dwStyle & LVS_OWNERDATA)
3930 RANGE range = iterator_range(&i);
3933 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3934 nmlv.iFrom = range.lower;
3935 nmlv.iTo = range.upper - 1;
3936 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3939 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3940 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3943 if (uView == LVS_REPORT)
3944 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3945 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3946 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3948 /* if we have a focus rect, draw it */
3949 if (infoPtr->bFocus)
3950 DrawFocusRect(hdc, &infoPtr->rcFocus);
3952 iterator_destroy(&i);
3955 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3956 notify_postpaint(infoPtr, &nmlvcd);
3958 infoPtr->clrTextBk = oldClrTextBk;
3959 infoPtr->clrText = oldClrText;
3961 SelectObject(hdc, hOldFont);
3962 SetBkMode(hdc, oldBkMode);
3963 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3964 SetTextColor(hdc, oldTextColor);
3965 infoPtr->bIsDrawing = FALSE;
3971 * Calculates the approximate width and height of a given number of items.
3974 * [I] infoPtr : valid pointer to the listview structure
3975 * [I] nItemCount : number of items
3976 * [I] wWidth : width
3977 * [I] wHeight : height
3980 * Returns a DWORD. The width in the low word and the height in high word.
3982 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3983 WORD wWidth, WORD wHeight)
3985 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3986 INT nItemCountPerColumn = 1;
3987 INT nColumnCount = 0;
3988 DWORD dwViewRect = 0;
3990 if (nItemCount == -1)
3991 nItemCount = infoPtr->nItemCount;
3993 if (uView == LVS_LIST)
3995 if (wHeight == 0xFFFF)
3997 /* use current height */
3998 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4001 if (wHeight < infoPtr->nItemHeight)
4002 wHeight = infoPtr->nItemHeight;
4006 if (infoPtr->nItemHeight > 0)
4008 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4009 if (nItemCountPerColumn == 0)
4010 nItemCountPerColumn = 1;
4012 if (nItemCount % nItemCountPerColumn != 0)
4013 nColumnCount = nItemCount / nItemCountPerColumn;
4015 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4019 /* Microsoft padding magic */
4020 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4021 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4023 dwViewRect = MAKELONG(wWidth, wHeight);
4025 else if (uView == LVS_REPORT)
4029 if (infoPtr->nItemCount > 0)
4031 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4032 wWidth = rcBox.right - rcBox.left;
4033 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4037 /* use current height and width */
4038 if (wHeight == 0xffff)
4039 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4040 if (wWidth == 0xffff)
4041 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4044 dwViewRect = MAKELONG(wWidth, wHeight);
4046 else if (uView == LVS_SMALLICON)
4047 FIXME("uView == LVS_SMALLICON: not implemented\n");
4048 else if (uView == LVS_ICON)
4049 FIXME("uView == LVS_ICON: not implemented\n");
4057 * Create a drag image list for the specified item.
4060 * [I] infoPtr : valid pointer to the listview structure
4061 * [I] iItem : index of item
4062 * [O] lppt : Upperr-left corner of the image
4065 * Returns a handle to the image list if successful, NULL otherwise.
4067 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4073 HBITMAP hbmp, hOldbmp;
4074 HIMAGELIST dragList = 0;
4075 TRACE("iItem=%d Count=%d \n", iItem, infoPtr->nItemCount);
4077 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4080 rcItem.left = LVIR_BOUNDS;
4081 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4084 lppt->x = rcItem.left;
4085 lppt->y = rcItem.top;
4087 size.cx = rcItem.right - rcItem.left;
4088 size.cy = rcItem.bottom - rcItem.top;
4090 hdcOrig = GetDC(infoPtr->hwndSelf);
4091 hdc = CreateCompatibleDC(hdcOrig);
4092 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4093 hOldbmp = SelectObject(hdc, hbmp);
4095 rcItem.left = rcItem.top = 0;
4096 rcItem.right = size.cx;
4097 rcItem.bottom = size.cy;
4098 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4101 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4103 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4104 SelectObject(hdc, hOldbmp);
4105 ImageList_Add(dragList, hbmp, 0);
4108 SelectObject(hdc, hOldbmp);
4112 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4114 TRACE("ret=%p\n", dragList);
4122 * Removes all listview items and subitems.
4125 * [I] infoPtr : valid pointer to the listview structure
4131 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4134 HDPA hdpaSubItems = NULL;
4141 /* we do it directly, to avoid notifications */
4142 ranges_clear(infoPtr->selectionRanges);
4143 infoPtr->nSelectionMark = -1;
4144 infoPtr->nFocusedItem = -1;
4145 SetRectEmpty(&infoPtr->rcFocus);
4146 /* But we are supposed to leave nHotItem as is! */
4149 /* send LVN_DELETEALLITEMS notification */
4150 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4152 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4154 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4156 /* send LVN_DELETEITEM notification, if not suppressed */
4157 if (!bSuppress) notify_deleteitem(infoPtr, i);
4158 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4160 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4161 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4163 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4164 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4167 DPA_Destroy(hdpaSubItems);
4168 DPA_DeletePtr(infoPtr->hdpaItems, i);
4170 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4171 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4172 infoPtr->nItemCount --;
4175 LISTVIEW_UpdateScroll(infoPtr);
4177 LISTVIEW_InvalidateList(infoPtr);
4184 * Scrolls, and updates the columns, when a column is changing width.
4187 * [I] infoPtr : valid pointer to the listview structure
4188 * [I] nColumn : column to scroll
4189 * [I] dx : amount of scroll, in pixels
4194 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4196 COLUMN_INFO *lpColumnInfo;
4200 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4201 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4202 rcCol = lpColumnInfo->rcHeader;
4203 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4204 rcCol.left = rcCol.right;
4206 /* ajust the other columns */
4207 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4209 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4210 lpColumnInfo->rcHeader.left += dx;
4211 lpColumnInfo->rcHeader.right += dx;
4214 /* do not update screen if not in report mode */
4215 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4217 /* if we have a focus, must first erase the focus rect */
4218 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4220 /* Need to reset the item width when inserting a new column */
4221 infoPtr->nItemWidth += dx;
4223 LISTVIEW_UpdateScroll(infoPtr);
4225 /* scroll to cover the deleted column, and invalidate for redraw */
4226 rcOld = infoPtr->rcList;
4227 rcOld.left = rcCol.left;
4228 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4230 /* we can restore focus now */
4231 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4236 * Removes a column from the listview control.
4239 * [I] infoPtr : valid pointer to the listview structure
4240 * [I] nColumn : column index
4246 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4250 TRACE("nColumn=%d\n", nColumn);
4252 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4253 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4255 /* While the MSDN specifically says that column zero should not be deleted,
4256 what actually happens is that the column itself is deleted but no items or subitems
4260 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4262 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4265 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4266 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4268 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4270 SUBITEM_INFO *lpSubItem, *lpDelItem;
4272 INT nItem, nSubItem, i;
4274 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4276 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4279 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4281 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4282 if (lpSubItem->iSubItem == nColumn)
4285 lpDelItem = lpSubItem;
4287 else if (lpSubItem->iSubItem > nColumn)
4289 lpSubItem->iSubItem--;
4293 /* if we found our subitem, zapp it */
4297 if (is_textW(lpDelItem->hdr.pszText))
4298 Free(lpDelItem->hdr.pszText);
4303 /* free dpa memory */
4304 DPA_DeletePtr(hdpaSubItems, nSubItem);
4309 /* update the other column info */
4310 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4311 LISTVIEW_InvalidateList(infoPtr);
4313 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4320 * Invalidates the listview after an item's insertion or deletion.
4323 * [I] infoPtr : valid pointer to the listview structure
4324 * [I] nItem : item index
4325 * [I] dir : -1 if deleting, 1 if inserting
4330 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4332 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4333 INT nPerCol, nItemCol, nItemRow;
4337 /* if we don't refresh, what's the point of scrolling? */
4338 if (!is_redrawing(infoPtr)) return;
4340 assert (abs(dir) == 1);
4342 /* arrange icons if autoarrange is on */
4343 if (is_autoarrange(infoPtr))
4345 BOOL arrange = TRUE;
4346 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4347 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4348 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4351 /* scrollbars need updating */
4352 LISTVIEW_UpdateScroll(infoPtr);
4354 /* figure out the item's position */
4355 if (uView == LVS_REPORT)
4356 nPerCol = infoPtr->nItemCount + 1;
4357 else if (uView == LVS_LIST)
4358 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4359 else /* LVS_ICON, or LVS_SMALLICON */
4362 nItemCol = nItem / nPerCol;
4363 nItemRow = nItem % nPerCol;
4364 LISTVIEW_GetOrigin(infoPtr, &Origin);
4366 /* move the items below up a slot */
4367 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4368 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4369 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4370 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4371 OffsetRect(&rcScroll, Origin.x, Origin.y);
4372 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4373 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4375 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4376 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4377 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4380 /* report has only that column, so we're done */
4381 if (uView == LVS_REPORT) return;
4383 /* now for LISTs, we have to deal with the columns to the right */
4384 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4386 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4387 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4388 OffsetRect(&rcScroll, Origin.x, Origin.y);
4389 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4390 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4391 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4396 * Removes an item from the listview control.
4399 * [I] infoPtr : valid pointer to the listview structure
4400 * [I] nItem : item index
4406 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4408 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4411 TRACE("(nItem=%d)\n", nItem);
4413 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4415 /* remove selection, and focus */
4417 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4418 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4420 /* send LVN_DELETEITEM notification. */
4421 notify_deleteitem(infoPtr, nItem);
4423 /* we need to do this here, because we'll be deleting stuff */
4424 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4425 LISTVIEW_InvalidateItem(infoPtr, nItem);
4427 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4433 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4434 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4436 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4437 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4440 DPA_Destroy(hdpaSubItems);
4443 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4445 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4446 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4449 infoPtr->nItemCount--;
4450 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4452 /* now is the invalidation fun */
4453 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4460 * Callback implementation for editlabel control
4463 * [I] infoPtr : valid pointer to the listview structure
4464 * [I] pszText : modified text
4465 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4471 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4473 NMLVDISPINFOW dispInfo;
4475 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4477 ZeroMemory(&dispInfo, sizeof(dispInfo));
4478 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4479 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4480 dispInfo.item.iSubItem = 0;
4481 dispInfo.item.stateMask = ~0;
4482 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4483 /* add the text from the edit in */
4484 dispInfo.item.mask |= LVIF_TEXT;
4485 dispInfo.item.pszText = pszText;
4486 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4488 /* Do we need to update the Item Text */
4489 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4490 if (!pszText) return TRUE;
4492 ZeroMemory(&dispInfo, sizeof(dispInfo));
4493 dispInfo.item.mask = LVIF_TEXT;
4494 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4495 dispInfo.item.iSubItem = 0;
4496 dispInfo.item.pszText = pszText;
4497 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4498 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4503 * Begin in place editing of specified list view item
4506 * [I] infoPtr : valid pointer to the listview structure
4507 * [I] nItem : item index
4508 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4514 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4516 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4517 NMLVDISPINFOW dispInfo;
4520 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4522 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4523 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4525 infoPtr->nEditLabelItem = nItem;
4527 /* Is the EditBox still there, if so remove it */
4528 if(infoPtr->hwndEdit != 0)
4530 SetFocus(infoPtr->hwndSelf);
4531 infoPtr->hwndEdit = 0;
4534 LISTVIEW_SetSelection(infoPtr, nItem);
4535 LISTVIEW_SetItemFocus(infoPtr, nItem);
4536 LISTVIEW_InvalidateItem(infoPtr, nItem);
4538 rect.left = LVIR_LABEL;
4539 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4541 ZeroMemory(&dispInfo, sizeof(dispInfo));
4542 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4543 dispInfo.item.iItem = nItem;
4544 dispInfo.item.iSubItem = 0;
4545 dispInfo.item.stateMask = ~0;
4546 dispInfo.item.pszText = szDispText;
4547 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4548 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4550 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4551 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4552 if (!infoPtr->hwndEdit) return 0;
4554 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4556 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4557 infoPtr->hwndEdit = 0;
4561 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4562 SetFocus(infoPtr->hwndEdit);
4563 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4564 return infoPtr->hwndEdit;
4570 * Ensures the specified item is visible, scrolling into view if necessary.
4573 * [I] infoPtr : valid pointer to the listview structure
4574 * [I] nItem : item index
4575 * [I] bPartial : partially or entirely visible
4581 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4583 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4584 INT nScrollPosHeight = 0;
4585 INT nScrollPosWidth = 0;
4586 INT nHorzAdjust = 0;
4587 INT nVertAdjust = 0;
4590 RECT rcItem, rcTemp;
4592 rcItem.left = LVIR_BOUNDS;
4593 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4595 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4597 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4599 /* scroll left/right, but in LVS_REPORT mode */
4600 if (uView == LVS_LIST)
4601 nScrollPosWidth = infoPtr->nItemWidth;
4602 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4603 nScrollPosWidth = 1;
4605 if (rcItem.left < infoPtr->rcList.left)
4608 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4613 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4617 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4619 /* scroll up/down, but not in LVS_LIST mode */
4620 if (uView == LVS_REPORT)
4621 nScrollPosHeight = infoPtr->nItemHeight;
4622 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4623 nScrollPosHeight = 1;
4625 if (rcItem.top < infoPtr->rcList.top)
4628 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4633 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4637 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4639 if (nScrollPosWidth)
4641 INT diff = nHorzDiff / nScrollPosWidth;
4642 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4643 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4646 if (nScrollPosHeight)
4648 INT diff = nVertDiff / nScrollPosHeight;
4649 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4650 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4658 * Searches for an item with specific characteristics.
4661 * [I] hwnd : window handle
4662 * [I] nStart : base item index
4663 * [I] lpFindInfo : item information to look for
4666 * SUCCESS : index of item
4669 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4670 const LVFINDINFOW *lpFindInfo)
4672 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4673 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4674 BOOL bWrap = FALSE, bNearest = FALSE;
4675 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4676 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4677 POINT Position, Destination;
4680 if (!lpFindInfo || nItem < 0) return -1;
4683 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4685 lvItem.mask |= LVIF_TEXT;
4686 lvItem.pszText = szDispText;
4687 lvItem.cchTextMax = DISP_TEXT_SIZE;
4690 if (lpFindInfo->flags & LVFI_WRAP)
4693 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4694 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4699 LISTVIEW_GetOrigin(infoPtr, &Origin);
4700 Destination.x = lpFindInfo->pt.x - Origin.x;
4701 Destination.y = lpFindInfo->pt.y - Origin.y;
4702 switch(lpFindInfo->vkDirection)
4704 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4705 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4706 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4707 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4708 case VK_HOME: Destination.x = Destination.y = 0; break;
4709 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4710 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4712 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4713 Destination.x = rcArea.right;
4714 Destination.y = rcArea.bottom;
4716 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4721 /* if LVFI_PARAM is specified, all other flags are ignored */
4722 if (lpFindInfo->flags & LVFI_PARAM)
4724 lvItem.mask |= LVIF_PARAM;
4726 lvItem.mask &= ~LVIF_TEXT;
4730 for (; nItem < nLast; nItem++)
4732 lvItem.iItem = nItem;
4733 lvItem.iSubItem = 0;
4734 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4736 if (lvItem.mask & LVIF_PARAM)
4738 if (lpFindInfo->lParam == lvItem.lParam)
4744 if (lvItem.mask & LVIF_TEXT)
4746 if (lpFindInfo->flags & LVFI_PARTIAL)
4748 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4752 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4756 if (!bNearest) return nItem;
4758 /* This is very inefficient. To do a good job here,
4759 * we need a sorted array of (x,y) item positions */
4760 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4762 /* compute the distance^2 to the destination */
4763 xdist = Destination.x - Position.x;
4764 ydist = Destination.y - Position.y;
4765 dist = xdist * xdist + ydist * ydist;
4767 /* remember the distance, and item if it's closer */
4771 nNearestItem = nItem;
4778 nLast = min(nStart + 1, infoPtr->nItemCount);
4783 return nNearestItem;
4788 * Searches for an item with specific characteristics.
4791 * [I] hwnd : window handle
4792 * [I] nStart : base item index
4793 * [I] lpFindInfo : item information to look for
4796 * SUCCESS : index of item
4799 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4800 const LVFINDINFOA *lpFindInfo)
4802 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4806 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4807 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4808 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4809 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4815 * Retrieves the background image of the listview control.
4818 * [I] infoPtr : valid pointer to the listview structure
4819 * [O] lpBkImage : background image attributes
4825 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4827 /* FIXME (listview, "empty stub!\n"); */
4833 * Retrieves column attributes.
4836 * [I] infoPtr : valid pointer to the listview structure
4837 * [I] nColumn : column index
4838 * [IO] lpColumn : column information
4839 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4840 * otherwise it is in fact a LPLVCOLUMNA
4846 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4848 COLUMN_INFO *lpColumnInfo;
4851 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4852 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4854 /* initialize memory */
4855 ZeroMemory(&hdi, sizeof(hdi));
4857 if (lpColumn->mask & LVCF_TEXT)
4859 hdi.mask |= HDI_TEXT;
4860 hdi.pszText = lpColumn->pszText;
4861 hdi.cchTextMax = lpColumn->cchTextMax;
4864 if (lpColumn->mask & LVCF_IMAGE)
4865 hdi.mask |= HDI_IMAGE;
4867 if (lpColumn->mask & LVCF_ORDER)
4868 hdi.mask |= HDI_ORDER;
4870 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4872 if (lpColumn->mask & LVCF_FMT)
4873 lpColumn->fmt = lpColumnInfo->fmt;
4875 if (lpColumn->mask & LVCF_WIDTH)
4876 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4878 if (lpColumn->mask & LVCF_IMAGE)
4879 lpColumn->iImage = hdi.iImage;
4881 if (lpColumn->mask & LVCF_ORDER)
4882 lpColumn->iOrder = hdi.iOrder;
4888 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4895 /* FIXME: little hack */
4896 for (i = 0; i < iCount; i++)
4904 * Retrieves the column width.
4907 * [I] infoPtr : valid pointer to the listview structure
4908 * [I] int : column index
4911 * SUCCESS : column width
4914 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4916 INT nColumnWidth = 0;
4919 TRACE("nColumn=%d\n", nColumn);
4921 /* we have a 'column' in LIST and REPORT mode only */
4922 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4925 nColumnWidth = infoPtr->nItemWidth;
4928 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
4929 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4930 nColumnWidth = rcHeader.right - rcHeader.left;
4934 TRACE("nColumnWidth=%d\n", nColumnWidth);
4935 return nColumnWidth;
4940 * In list or report display mode, retrieves the number of items that can fit
4941 * vertically in the visible area. In icon or small icon display mode,
4942 * retrieves the total number of visible items.
4945 * [I] infoPtr : valid pointer to the listview structure
4948 * Number of fully visible items.
4950 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4952 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4956 return infoPtr->nItemCount;
4958 return LISTVIEW_GetCountPerColumn(infoPtr);
4960 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4968 * Retrieves an image list handle.
4971 * [I] infoPtr : valid pointer to the listview structure
4972 * [I] nImageList : image list identifier
4975 * SUCCESS : image list handle
4978 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4982 case LVSIL_NORMAL: return infoPtr->himlNormal;
4983 case LVSIL_SMALL: return infoPtr->himlSmall;
4984 case LVSIL_STATE: return infoPtr->himlState;
4989 /* LISTVIEW_GetISearchString */
4993 * Retrieves item attributes.
4996 * [I] hwnd : window handle
4997 * [IO] lpLVItem : item info
4998 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4999 * if FALSE, the lpLVItem is a LPLVITEMA.
5002 * This is the internal 'GetItem' interface -- it tries to
5003 * be smart, and avoids text copies, if possible, by modifing
5004 * lpLVItem->pszText to point to the text string. Please note
5005 * that this is not always possible (e.g. OWNERDATA), so on
5006 * entry you *must* supply valid values for pszText, and cchTextMax.
5007 * The only difference to the documented interface is that upon
5008 * return, you should use *only* the lpLVItem->pszText, rather than
5009 * the buffer pointer you provided on input. Most code already does
5010 * that, so it's not a problem.
5011 * For the two cases when the text must be copied (that is,
5012 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5018 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5020 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5021 NMLVDISPINFOW dispInfo;
5027 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5029 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5032 if (lpLVItem->mask == 0) return TRUE;
5034 /* make a local copy */
5035 isubitem = lpLVItem->iSubItem;
5037 /* a quick optimization if all we're asked is the focus state
5038 * these queries are worth optimising since they are common,
5039 * and can be answered in constant time, without the heavy accesses */
5040 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5041 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5043 lpLVItem->state = 0;
5044 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5045 lpLVItem->state |= LVIS_FOCUSED;
5049 ZeroMemory(&dispInfo, sizeof(dispInfo));
5051 /* if the app stores all the data, handle it separately */
5052 if (infoPtr->dwStyle & LVS_OWNERDATA)
5054 dispInfo.item.state = 0;
5056 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5057 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5059 /* NOTE: copy only fields which we _know_ are initialized, some apps
5060 * depend on the uninitialized fields being 0 */
5061 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5062 dispInfo.item.iItem = lpLVItem->iItem;
5063 dispInfo.item.iSubItem = isubitem;
5064 if (lpLVItem->mask & LVIF_TEXT)
5066 dispInfo.item.pszText = lpLVItem->pszText;
5067 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5069 if (lpLVItem->mask & LVIF_STATE)
5070 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5071 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5072 dispInfo.item.stateMask = lpLVItem->stateMask;
5073 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5075 /* full size structure expected - _WIN32IE >= 0x560 */
5076 *lpLVItem = dispInfo.item;
5078 else if (lpLVItem->mask & LVIF_INDENT)
5080 /* indent member expected - _WIN32IE >= 0x300 */
5081 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5085 /* minimal structure expected */
5086 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5088 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5091 /* make sure lParam is zeroed out */
5092 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5094 /* we store only a little state, so if we're not asked, we're done */
5095 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5097 /* if focus is handled by us, report it */
5098 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5100 lpLVItem->state &= ~LVIS_FOCUSED;
5101 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5102 lpLVItem->state |= LVIS_FOCUSED;
5105 /* and do the same for selection, if we handle it */
5106 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5108 lpLVItem->state &= ~LVIS_SELECTED;
5109 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5110 lpLVItem->state |= LVIS_SELECTED;
5116 /* find the item and subitem structures before we proceed */
5117 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5118 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5123 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5124 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5127 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5132 pItemHdr = &lpItem->hdr;
5134 /* Do we need to query the state from the app? */
5135 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5137 dispInfo.item.mask |= LVIF_STATE;
5138 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5141 /* Do we need to enquire about the image? */
5142 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5143 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5145 dispInfo.item.mask |= LVIF_IMAGE;
5146 dispInfo.item.iImage = I_IMAGECALLBACK;
5149 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5150 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5152 dispInfo.item.mask |= LVIF_TEXT;
5153 dispInfo.item.pszText = lpLVItem->pszText;
5154 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5155 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5156 *dispInfo.item.pszText = '\0';
5159 /* If we don't have all the requested info, query the application */
5160 if (dispInfo.item.mask != 0)
5162 dispInfo.item.iItem = lpLVItem->iItem;
5163 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5164 dispInfo.item.lParam = lpItem->lParam;
5165 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5166 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5169 /* we should not store values for subitems */
5170 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5172 /* Now, handle the iImage field */
5173 if (dispInfo.item.mask & LVIF_IMAGE)
5175 lpLVItem->iImage = dispInfo.item.iImage;
5176 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5177 pItemHdr->iImage = dispInfo.item.iImage;
5179 else if (lpLVItem->mask & LVIF_IMAGE)
5181 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5182 lpLVItem->iImage = pItemHdr->iImage;
5184 lpLVItem->iImage = 0;
5187 /* The pszText field */
5188 if (dispInfo.item.mask & LVIF_TEXT)
5190 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5191 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5193 lpLVItem->pszText = dispInfo.item.pszText;
5195 else if (lpLVItem->mask & LVIF_TEXT)
5197 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5198 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5201 /* if this is a subitem, we're done */
5202 if (isubitem) return TRUE;
5204 /* Next is the lParam field */
5205 if (dispInfo.item.mask & LVIF_PARAM)
5207 lpLVItem->lParam = dispInfo.item.lParam;
5208 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5209 lpItem->lParam = dispInfo.item.lParam;
5211 else if (lpLVItem->mask & LVIF_PARAM)
5212 lpLVItem->lParam = lpItem->lParam;
5214 /* ... the state field (this one is different due to uCallbackmask) */
5215 if (lpLVItem->mask & LVIF_STATE)
5217 lpLVItem->state = lpItem->state;
5218 if (dispInfo.item.mask & LVIF_STATE)
5220 lpLVItem->state &= ~dispInfo.item.stateMask;
5221 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5223 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5225 lpLVItem->state &= ~LVIS_FOCUSED;
5226 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5227 lpLVItem->state |= LVIS_FOCUSED;
5229 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5231 lpLVItem->state &= ~LVIS_SELECTED;
5232 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5233 lpLVItem->state |= LVIS_SELECTED;
5237 /* and last, but not least, the indent field */
5238 if (lpLVItem->mask & LVIF_INDENT)
5239 lpLVItem->iIndent = lpItem->iIndent;
5246 * Retrieves item attributes.
5249 * [I] hwnd : window handle
5250 * [IO] lpLVItem : item info
5251 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5252 * if FALSE, the lpLVItem is a LPLVITEMA.
5255 * This is the external 'GetItem' interface -- it properly copies
5256 * the text in the provided buffer.
5262 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5267 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5270 pszText = lpLVItem->pszText;
5271 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5272 if (bResult && lpLVItem->pszText != pszText)
5273 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5274 lpLVItem->pszText = pszText;
5282 * Retrieves the position (upper-left) of the listview control item.
5283 * Note that for LVS_ICON style, the upper-left is that of the icon
5284 * and not the bounding box.
5287 * [I] infoPtr : valid pointer to the listview structure
5288 * [I] nItem : item index
5289 * [O] lpptPosition : coordinate information
5295 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5297 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5300 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5302 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5304 LISTVIEW_GetOrigin(infoPtr, &Origin);
5305 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5307 if (uView == LVS_ICON)
5309 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5310 lpptPosition->y += ICON_TOP_PADDING;
5312 lpptPosition->x += Origin.x;
5313 lpptPosition->y += Origin.y;
5315 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5322 * Retrieves the bounding rectangle for a listview control item.
5325 * [I] infoPtr : valid pointer to the listview structure
5326 * [I] nItem : item index
5327 * [IO] lprc : bounding rectangle coordinates
5328 * lprc->left specifies the portion of the item for which the bounding
5329 * rectangle will be retrieved.
5331 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5332 * including the icon and label.
5335 * * Experiment shows that native control returns:
5336 * * width = min (48, length of text line)
5337 * * .left = position.x - (width - iconsize.cx)/2
5338 * * .right = .left + width
5339 * * height = #lines of text * ntmHeight + icon height + 8
5340 * * .top = position.y - 2
5341 * * .bottom = .top + height
5342 * * separation between items .y = itemSpacing.cy - height
5343 * * .x = itemSpacing.cx - width
5344 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5347 * * Experiment shows that native control returns:
5348 * * width = iconSize.cx + 16
5349 * * .left = position.x - (width - iconsize.cx)/2
5350 * * .right = .left + width
5351 * * height = iconSize.cy + 4
5352 * * .top = position.y - 2
5353 * * .bottom = .top + height
5354 * * separation between items .y = itemSpacing.cy - height
5355 * * .x = itemSpacing.cx - width
5356 * LVIR_LABEL Returns the bounding rectangle of the item text.
5359 * * Experiment shows that native control returns:
5360 * * width = text length
5361 * * .left = position.x - width/2
5362 * * .right = .left + width
5363 * * height = ntmH * linecount + 2
5364 * * .top = position.y + iconSize.cy + 6
5365 * * .bottom = .top + height
5366 * * separation between items .y = itemSpacing.cy - height
5367 * * .x = itemSpacing.cx - width
5368 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5369 * rectangles, but excludes columns in report view.
5376 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5377 * upon whether the window has the focus currently and on whether the item
5378 * is the one with the focus. Ensure that the control's record of which
5379 * item has the focus agrees with the items' records.
5381 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5383 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5384 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5385 BOOL doLabel = TRUE, oversizedBox = FALSE;
5386 POINT Position, Origin;
5390 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5392 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5394 LISTVIEW_GetOrigin(infoPtr, &Origin);
5395 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5397 /* Be smart and try to figure out the minimum we have to do */
5398 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5399 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5400 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5401 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5402 oversizedBox = TRUE;
5404 /* get what we need from the item before hand, so we make
5405 * only one request. This can speed up things, if data
5406 * is stored on the app side */
5408 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5409 if (doLabel) lvItem.mask |= LVIF_TEXT;
5410 lvItem.iItem = nItem;
5411 lvItem.iSubItem = 0;
5412 lvItem.pszText = szDispText;
5413 lvItem.cchTextMax = DISP_TEXT_SIZE;
5414 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5415 /* we got the state already up, simulate it here, to avoid a reget */
5416 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5418 lvItem.mask |= LVIF_STATE;
5419 lvItem.stateMask = LVIS_FOCUSED;
5420 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5423 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5424 lprc->left = LVIR_BOUNDS;
5428 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5432 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5436 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5439 case LVIR_SELECTBOUNDS:
5440 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5441 UnionRect(lprc, lprc, &label_rect);
5445 WARN("Unknown value: %ld\n", lprc->left);
5449 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5451 TRACE(" rect=%s\n", debugrect(lprc));
5458 * Retrieves the spacing between listview control items.
5461 * [I] infoPtr : valid pointer to the listview structure
5462 * [IO] lprc : rectangle to receive the output
5463 * on input, lprc->top = nSubItem
5464 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5466 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5467 * not only those of the first column.
5468 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5474 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5479 if (!lprc) return FALSE;
5481 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5482 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5484 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5486 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5488 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5491 lvItem.iItem = nItem;
5492 lvItem.iSubItem = lprc->top;
5494 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5498 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5503 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5507 ERR("Unknown bounds=%ld\n", lprc->left);
5511 OffsetRect(lprc, Position.x, Position.y);
5518 * Retrieves the width of a label.
5521 * [I] infoPtr : valid pointer to the listview structure
5524 * SUCCESS : string width (in pixels)
5527 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5529 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5532 TRACE("(nItem=%d)\n", nItem);
5534 lvItem.mask = LVIF_TEXT;
5535 lvItem.iItem = nItem;
5536 lvItem.iSubItem = 0;
5537 lvItem.pszText = szDispText;
5538 lvItem.cchTextMax = DISP_TEXT_SIZE;
5539 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5541 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5546 * Retrieves the spacing between listview control items.
5549 * [I] infoPtr : valid pointer to the listview structure
5550 * [I] bSmall : flag for small or large icon
5553 * Horizontal + vertical spacing
5555 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5561 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5565 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5566 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5568 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5575 * Retrieves the state of a listview control item.
5578 * [I] infoPtr : valid pointer to the listview structure
5579 * [I] nItem : item index
5580 * [I] uMask : state mask
5583 * State specified by the mask.
5585 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5589 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5591 lvItem.iItem = nItem;
5592 lvItem.iSubItem = 0;
5593 lvItem.mask = LVIF_STATE;
5594 lvItem.stateMask = uMask;
5595 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5597 return lvItem.state & uMask;
5602 * Retrieves the text of a listview control item or subitem.
5605 * [I] hwnd : window handle
5606 * [I] nItem : item index
5607 * [IO] lpLVItem : item information
5608 * [I] isW : TRUE if lpLVItem is Unicode
5611 * SUCCESS : string length
5614 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5616 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5618 lpLVItem->mask = LVIF_TEXT;
5619 lpLVItem->iItem = nItem;
5620 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5622 return textlenT(lpLVItem->pszText, isW);
5627 * Searches for an item based on properties + relationships.
5630 * [I] infoPtr : valid pointer to the listview structure
5631 * [I] nItem : item index
5632 * [I] uFlags : relationship flag
5635 * SUCCESS : item index
5638 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5640 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5642 LVFINDINFOW lvFindInfo;
5643 INT nCountPerColumn;
5647 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5648 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5650 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5652 if (uFlags & LVNI_CUT)
5655 if (uFlags & LVNI_DROPHILITED)
5656 uMask |= LVIS_DROPHILITED;
5658 if (uFlags & LVNI_FOCUSED)
5659 uMask |= LVIS_FOCUSED;
5661 if (uFlags & LVNI_SELECTED)
5662 uMask |= LVIS_SELECTED;
5664 /* if we're asked for the focused item, that's only one,
5665 * so it's worth optimizing */
5666 if (uFlags & LVNI_FOCUSED)
5668 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5669 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5672 if (uFlags & LVNI_ABOVE)
5674 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5679 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5685 /* Special case for autoarrange - move 'til the top of a list */
5686 if (is_autoarrange(infoPtr))
5688 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5689 while (nItem - nCountPerRow >= 0)
5691 nItem -= nCountPerRow;
5692 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5697 lvFindInfo.flags = LVFI_NEARESTXY;
5698 lvFindInfo.vkDirection = VK_UP;
5699 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5700 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5702 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5707 else if (uFlags & LVNI_BELOW)
5709 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5711 while (nItem < infoPtr->nItemCount)
5714 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5720 /* Special case for autoarrange - move 'til the bottom of a list */
5721 if (is_autoarrange(infoPtr))
5723 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5724 while (nItem + nCountPerRow < infoPtr->nItemCount )
5726 nItem += nCountPerRow;
5727 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5732 lvFindInfo.flags = LVFI_NEARESTXY;
5733 lvFindInfo.vkDirection = VK_DOWN;
5734 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5735 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5737 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5742 else if (uFlags & LVNI_TOLEFT)
5744 if (uView == LVS_LIST)
5746 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5747 while (nItem - nCountPerColumn >= 0)
5749 nItem -= nCountPerColumn;
5750 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5754 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5756 /* Special case for autoarrange - move 'ti the beginning of a row */
5757 if (is_autoarrange(infoPtr))
5759 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5760 while (nItem % nCountPerRow > 0)
5763 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5768 lvFindInfo.flags = LVFI_NEARESTXY;
5769 lvFindInfo.vkDirection = VK_LEFT;
5770 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5771 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5773 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5778 else if (uFlags & LVNI_TORIGHT)
5780 if (uView == LVS_LIST)
5782 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5783 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5785 nItem += nCountPerColumn;
5786 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5790 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5792 /* Special case for autoarrange - move 'til the end of a row */
5793 if (is_autoarrange(infoPtr))
5795 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5796 while (nItem % nCountPerRow < nCountPerRow - 1 )
5799 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5804 lvFindInfo.flags = LVFI_NEARESTXY;
5805 lvFindInfo.vkDirection = VK_RIGHT;
5806 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5807 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5809 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5818 /* search by index */
5819 for (i = nItem; i < infoPtr->nItemCount; i++)
5821 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5829 /* LISTVIEW_GetNumberOfWorkAreas */
5833 * Retrieves the origin coordinates when in icon or small icon display mode.
5836 * [I] infoPtr : valid pointer to the listview structure
5837 * [O] lpptOrigin : coordinate information
5842 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5844 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5845 INT nHorzPos = 0, nVertPos = 0;
5846 SCROLLINFO scrollInfo;
5848 scrollInfo.cbSize = sizeof(SCROLLINFO);
5849 scrollInfo.fMask = SIF_POS;
5851 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5852 nHorzPos = scrollInfo.nPos;
5853 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5854 nVertPos = scrollInfo.nPos;
5856 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5858 lpptOrigin->x = infoPtr->rcList.left;
5859 lpptOrigin->y = infoPtr->rcList.top;
5860 if (uView == LVS_LIST)
5861 nHorzPos *= infoPtr->nItemWidth;
5862 else if (uView == LVS_REPORT)
5863 nVertPos *= infoPtr->nItemHeight;
5865 lpptOrigin->x -= nHorzPos;
5866 lpptOrigin->y -= nVertPos;
5868 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5873 * Retrieves the width of a string.
5876 * [I] hwnd : window handle
5877 * [I] lpszText : text string to process
5878 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5881 * SUCCESS : string width (in pixels)
5884 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5889 if (is_textT(lpszText, isW))
5891 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5892 HDC hdc = GetDC(infoPtr->hwndSelf);
5893 HFONT hOldFont = SelectObject(hdc, hFont);
5896 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5898 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5899 SelectObject(hdc, hOldFont);
5900 ReleaseDC(infoPtr->hwndSelf, hdc);
5902 return stringSize.cx;
5907 * Determines which listview item is located at the specified position.
5910 * [I] infoPtr : valid pointer to the listview structure
5911 * [IO] lpht : hit test information
5912 * [I] subitem : fill out iSubItem.
5913 * [I] select : return the index only if the hit selects the item
5916 * (mm 20001022): We must not allow iSubItem to be touched, for
5917 * an app might pass only a structure with space up to iItem!
5918 * (MS Office 97 does that for instance in the file open dialog)
5921 * SUCCESS : item index
5924 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5926 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5927 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5928 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5929 POINT Origin, Position, opt;
5934 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5938 if (subitem) lpht->iSubItem = 0;
5940 if (infoPtr->rcList.left > lpht->pt.x)
5941 lpht->flags |= LVHT_TOLEFT;
5942 else if (infoPtr->rcList.right < lpht->pt.x)
5943 lpht->flags |= LVHT_TORIGHT;
5945 if (infoPtr->rcList.top > lpht->pt.y)
5946 lpht->flags |= LVHT_ABOVE;
5947 else if (infoPtr->rcList.bottom < lpht->pt.y)
5948 lpht->flags |= LVHT_BELOW;
5950 TRACE("lpht->flags=0x%x\n", lpht->flags);
5951 if (lpht->flags) return -1;
5953 lpht->flags |= LVHT_NOWHERE;
5955 LISTVIEW_GetOrigin(infoPtr, &Origin);
5957 /* first deal with the large items */
5958 rcSearch.left = lpht->pt.x;
5959 rcSearch.top = lpht->pt.y;
5960 rcSearch.right = rcSearch.left + 1;
5961 rcSearch.bottom = rcSearch.top + 1;
5963 iterator_frameditems(&i, infoPtr, &rcSearch);
5964 iterator_next(&i); /* go to first item in the sequence */
5966 iterator_destroy(&i);
5968 TRACE("lpht->iItem=%d\n", iItem);
5969 if (iItem == -1) return -1;
5971 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5972 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5973 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5974 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5975 lvItem.iItem = iItem;
5976 lvItem.iSubItem = 0;
5977 lvItem.pszText = szDispText;
5978 lvItem.cchTextMax = DISP_TEXT_SIZE;
5979 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5980 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5982 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5983 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
5984 opt.x = lpht->pt.x - Position.x - Origin.x;
5985 opt.y = lpht->pt.y - Position.y - Origin.y;
5987 if (uView == LVS_REPORT)
5990 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5991 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5992 if (!PtInRect(&rcBounds, opt)) return -1;
5994 if (PtInRect(&rcIcon, opt))
5995 lpht->flags |= LVHT_ONITEMICON;
5996 else if (PtInRect(&rcLabel, opt))
5997 lpht->flags |= LVHT_ONITEMLABEL;
5998 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5999 lpht->flags |= LVHT_ONITEMSTATEICON;
6000 if (lpht->flags & LVHT_ONITEM)
6001 lpht->flags &= ~LVHT_NOWHERE;
6003 TRACE("lpht->flags=0x%x\n", lpht->flags);
6004 if (uView == LVS_REPORT && subitem)
6008 rcBounds.right = rcBounds.left;
6009 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6011 rcBounds.left = rcBounds.right;
6012 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6013 if (PtInRect(&rcBounds, opt))
6021 if (select && !(uView == LVS_REPORT &&
6022 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6023 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6025 if (uView == LVS_REPORT)
6027 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6028 UnionRect(&rcBounds, &rcBounds, &rcState);
6030 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6032 return lpht->iItem = iItem;
6036 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6037 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6038 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6039 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6040 their own sort proc. when sending LVM_SORTITEMS.
6043 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6045 LVS_SORTXXX must be specified,
6046 LVS_OWNERDRAW is not set,
6047 <item>.pszText is not LPSTR_TEXTCALLBACK.
6049 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6050 are sorted based on item text..."
6052 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6054 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6055 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6056 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6058 /* if we're sorting descending, negate the return value */
6059 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6064 * Inserts a new item in the listview control.
6067 * [I] infoPtr : valid pointer to the listview structure
6068 * [I] lpLVItem : item information
6069 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6072 * SUCCESS : new item index
6075 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6077 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6082 BOOL is_sorted, has_changed;
6085 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6087 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6089 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6090 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6092 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6094 if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6096 /* insert item in listview control data structure */
6097 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6098 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6100 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6101 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6103 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6104 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6105 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6106 if (nItem == -1) goto fail;
6107 infoPtr->nItemCount++;
6109 /* shift indices first so they don't get tangled */
6110 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6112 /* set the item attributes */
6113 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6115 /* full size structure expected - _WIN32IE >= 0x560 */
6118 else if (lpLVItem->mask & LVIF_INDENT)
6120 /* indent member expected - _WIN32IE >= 0x300 */
6121 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6125 /* minimal structure expected */
6126 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6129 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6130 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6132 /* if we're sorted, sort the list, and update the index */
6135 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6136 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6137 assert(nItem != -1);
6140 /* make room for the position, if we are in the right mode */
6141 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6143 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6145 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6147 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6152 /* send LVN_INSERTITEM notification */
6153 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6155 nmlv.lParam = lpItem->lParam;
6156 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6158 /* align items (set position of each item) */
6159 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6163 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6164 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6166 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6168 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6171 /* now is the invalidation fun */
6172 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6176 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6177 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6178 infoPtr->nItemCount--;
6180 DPA_DeletePtr(hdpaSubItems, 0);
6181 DPA_Destroy (hdpaSubItems);
6188 * Redraws a range of items.
6191 * [I] infoPtr : valid pointer to the listview structure
6192 * [I] nFirst : first item
6193 * [I] nLast : last item
6199 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6203 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6204 max(nFirst, nLast) >= infoPtr->nItemCount)
6207 for (i = nFirst; i <= nLast; i++)
6208 LISTVIEW_InvalidateItem(infoPtr, i);
6215 * Scroll the content of a listview.
6218 * [I] infoPtr : valid pointer to the listview structure
6219 * [I] dx : horizontal scroll amount in pixels
6220 * [I] dy : vertical scroll amount in pixels
6227 * If the control is in report mode (LVS_REPORT) the control can
6228 * be scrolled only in line increments. "dy" will be rounded to the
6229 * nearest number of pixels that are a whole line. Ex: if line height
6230 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6231 * is passed the the scroll will be 0. (per MSDN 7/2002)
6233 * For: (per experimentaion with native control and CSpy ListView)
6234 * LVS_ICON dy=1 = 1 pixel (vertical only)
6236 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6238 * LVS_LIST dx=1 = 1 column (horizontal only)
6239 * but will only scroll 1 column per message
6240 * no matter what the value.
6241 * dy must be 0 or FALSE returned.
6242 * LVS_REPORT dx=1 = 1 pixel
6246 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6248 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6250 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6251 dy /= infoPtr->nItemHeight;
6254 if (dy != 0) return FALSE;
6261 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6262 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6269 * Sets the background color.
6272 * [I] infoPtr : valid pointer to the listview structure
6273 * [I] clrBk : background color
6279 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6281 TRACE("(clrBk=%lx)\n", clrBk);
6283 if(infoPtr->clrBk != clrBk) {
6284 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6285 infoPtr->clrBk = clrBk;
6286 if (clrBk == CLR_NONE)
6287 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6289 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6290 LISTVIEW_InvalidateList(infoPtr);
6296 /* LISTVIEW_SetBkImage */
6298 /*** Helper for {Insert,Set}ColumnT *only* */
6299 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6301 if (lpColumn->mask & LVCF_FMT)
6303 /* format member is valid */
6304 lphdi->mask |= HDI_FORMAT;
6306 /* set text alignment (leftmost column must be left-aligned) */
6307 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6308 lphdi->fmt |= HDF_LEFT;
6309 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6310 lphdi->fmt |= HDF_RIGHT;
6311 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6312 lphdi->fmt |= HDF_CENTER;
6314 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6315 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6317 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6319 lphdi->fmt |= HDF_IMAGE;
6320 lphdi->iImage = I_IMAGECALLBACK;
6324 if (lpColumn->mask & LVCF_WIDTH)
6326 lphdi->mask |= HDI_WIDTH;
6327 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6329 /* make it fill the remainder of the controls width */
6333 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6335 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6336 lphdi->cxy += rcHeader.right - rcHeader.left;
6339 /* retrieve the layout of the header */
6340 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6341 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6343 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6346 lphdi->cxy = lpColumn->cx;
6349 if (lpColumn->mask & LVCF_TEXT)
6351 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6352 lphdi->fmt |= HDF_STRING;
6353 lphdi->pszText = lpColumn->pszText;
6354 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6357 if (lpColumn->mask & LVCF_IMAGE)
6359 lphdi->mask |= HDI_IMAGE;
6360 lphdi->iImage = lpColumn->iImage;
6363 if (lpColumn->mask & LVCF_ORDER)
6365 lphdi->mask |= HDI_ORDER;
6366 lphdi->iOrder = lpColumn->iOrder;
6373 * Inserts a new column.
6376 * [I] infoPtr : valid pointer to the listview structure
6377 * [I] nColumn : column index
6378 * [I] lpColumn : column information
6379 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6382 * SUCCESS : new column index
6385 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6386 const LVCOLUMNW *lpColumn, BOOL isW)
6388 COLUMN_INFO *lpColumnInfo;
6392 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6394 if (!lpColumn || nColumn < 0) return -1;
6395 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6397 ZeroMemory(&hdi, sizeof(HDITEMW));
6398 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6400 /* insert item in header control */
6401 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6402 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6403 (WPARAM)nColumn, (LPARAM)&hdi);
6404 if (nNewColumn == -1) return -1;
6405 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6407 /* create our own column info */
6408 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6409 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6411 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6412 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6414 /* now we have to actually adjust the data */
6415 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6417 SUBITEM_INFO *lpSubItem;
6421 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6423 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6424 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6426 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6427 if (lpSubItem->iSubItem >= nNewColumn)
6428 lpSubItem->iSubItem++;
6433 /* make space for the new column */
6434 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6439 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6442 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6450 * Sets the attributes of a header item.
6453 * [I] infoPtr : valid pointer to the listview structure
6454 * [I] nColumn : column index
6455 * [I] lpColumn : column attributes
6456 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6462 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6463 const LVCOLUMNW *lpColumn, BOOL isW)
6465 HDITEMW hdi, hdiget;
6468 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6470 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6472 ZeroMemory(&hdi, sizeof(HDITEMW));
6473 if (lpColumn->mask & LVCF_FMT)
6475 hdi.mask |= HDI_FORMAT;
6476 hdiget.mask = HDI_FORMAT;
6477 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6478 hdi.fmt = hdiget.fmt & HDF_STRING;
6480 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6482 /* set header item attributes */
6483 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6484 if (!bResult) return FALSE;
6486 if (lpColumn->mask & LVCF_FMT)
6488 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6489 int oldFmt = lpColumnInfo->fmt;
6491 lpColumnInfo->fmt = lpColumn->fmt;
6492 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6494 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6495 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6504 * Sets the column order array
6507 * [I] infoPtr : valid pointer to the listview structure
6508 * [I] iCount : number of elements in column order array
6509 * [I] lpiArray : pointer to column order array
6515 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6517 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6528 * Sets the width of a column
6531 * [I] infoPtr : valid pointer to the listview structure
6532 * [I] nColumn : column index
6533 * [I] cx : column width
6539 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6541 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6542 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6546 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6548 /* set column width only if in report or list mode */
6549 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6551 /* take care of invalid cx values */
6552 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6553 else if (uView == LVS_LIST && cx < 1) return FALSE;
6555 /* resize all columns if in LVS_LIST mode */
6556 if(uView == LVS_LIST)
6558 infoPtr->nItemWidth = cx;
6559 LISTVIEW_InvalidateList(infoPtr);
6563 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6565 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6570 lvItem.mask = LVIF_TEXT;
6572 lvItem.iSubItem = nColumn;
6573 lvItem.pszText = szDispText;
6574 lvItem.cchTextMax = DISP_TEXT_SIZE;
6575 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6577 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6578 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6579 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6581 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6582 max_cx += infoPtr->iconSize.cx;
6583 max_cx += TRAILING_LABEL_PADDING;
6586 /* autosize based on listview items width */
6587 if(cx == LVSCW_AUTOSIZE)
6589 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6591 /* if iCol is the last column make it fill the remainder of the controls width */
6592 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6597 LISTVIEW_GetOrigin(infoPtr, &Origin);
6598 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6600 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6604 /* Despite what the MS docs say, if this is not the last
6605 column, then MS resizes the column to the width of the
6606 largest text string in the column, including headers
6607 and items. This is different from LVSCW_AUTOSIZE in that
6608 LVSCW_AUTOSIZE ignores the header string length. */
6611 /* retrieve header text */
6612 hdi.mask = HDI_TEXT;
6613 hdi.cchTextMax = DISP_TEXT_SIZE;
6614 hdi.pszText = szDispText;
6615 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6617 HDC hdc = GetDC(infoPtr->hwndSelf);
6618 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6621 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6622 cx = size.cx + TRAILING_HEADER_PADDING;
6623 /* FIXME: Take into account the header image, if one is present */
6624 SelectObject(hdc, old_font);
6625 ReleaseDC(infoPtr->hwndSelf, hdc);
6627 cx = max (cx, max_cx);
6631 if (cx < 0) return FALSE;
6633 /* call header to update the column change */
6634 hdi.mask = HDI_WIDTH;
6636 TRACE("hdi.cxy=%d\n", hdi.cxy);
6637 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6641 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6644 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6647 HBITMAP hbm_im, hbm_mask, hbm_orig;
6649 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6650 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6653 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6654 ILC_COLOR | ILC_MASK, 2, 2);
6655 hdc_wnd = GetDC(infoPtr->hwndSelf);
6656 hdc = CreateCompatibleDC(hdc_wnd);
6657 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6658 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6659 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6661 rc.left = rc.top = 0;
6662 rc.right = GetSystemMetrics(SM_CXSMICON);
6663 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6665 hbm_orig = SelectObject(hdc, hbm_mask);
6666 FillRect(hdc, &rc, hbr_white);
6667 InflateRect(&rc, -3, -3);
6668 FillRect(hdc, &rc, hbr_black);
6670 SelectObject(hdc, hbm_im);
6671 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6672 SelectObject(hdc, hbm_orig);
6673 ImageList_Add(himl, hbm_im, hbm_mask);
6675 SelectObject(hdc, hbm_im);
6676 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6677 SelectObject(hdc, hbm_orig);
6678 ImageList_Add(himl, hbm_im, hbm_mask);
6680 DeleteObject(hbm_mask);
6681 DeleteObject(hbm_im);
6689 * Sets the extended listview style.
6692 * [I] infoPtr : valid pointer to the listview structure
6694 * [I] dwStyle : style
6697 * SUCCESS : previous style
6700 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6702 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6706 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6708 infoPtr->dwLvExStyle = dwStyle;
6710 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6712 HIMAGELIST himl = 0;
6713 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6714 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6715 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6723 * Sets the new hot cursor used during hot tracking and hover selection.
6726 * [I] infoPtr : valid pointer to the listview structure
6727 * [I} hCurosr : the new hot cursor handle
6730 * Returns the previous hot cursor
6732 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6734 HCURSOR oldCursor = infoPtr->hHotCursor;
6736 infoPtr->hHotCursor = hCursor;
6744 * Sets the hot item index.
6747 * [I] infoPtr : valid pointer to the listview structure
6748 * [I] iIndex : index
6751 * SUCCESS : previous hot item index
6752 * FAILURE : -1 (no hot item)
6754 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6756 INT iOldIndex = infoPtr->nHotItem;
6758 infoPtr->nHotItem = iIndex;
6766 * Sets the amount of time the cursor must hover over an item before it is selected.
6769 * [I] infoPtr : valid pointer to the listview structure
6770 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6773 * Returns the previous hover time
6775 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6777 DWORD oldHoverTime = infoPtr->dwHoverTime;
6779 infoPtr->dwHoverTime = dwHoverTime;
6781 return oldHoverTime;
6786 * Sets spacing for icons of LVS_ICON style.
6789 * [I] infoPtr : valid pointer to the listview structure
6790 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6791 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6794 * MAKELONG(oldcx, oldcy)
6796 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6798 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6799 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6801 TRACE("requested=(%d,%d)\n", cx, cy);
6803 /* this is supported only for LVS_ICON style */
6804 if (uView != LVS_ICON) return oldspacing;
6806 /* set to defaults, if instructed to */
6807 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6808 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6810 /* if 0 then compute width
6811 * FIXME: Should scan each item and determine max width of
6812 * icon or label, then make that the width */
6814 cx = infoPtr->iconSpacing.cx;
6816 /* if 0 then compute height */
6818 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6819 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6822 infoPtr->iconSpacing.cx = cx;
6823 infoPtr->iconSpacing.cy = cy;
6825 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6826 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6827 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6828 infoPtr->ntmHeight);
6830 /* these depend on the iconSpacing */
6831 LISTVIEW_UpdateItemSize(infoPtr);
6836 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6840 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6847 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6848 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6857 * [I] infoPtr : valid pointer to the listview structure
6858 * [I] nType : image list type
6859 * [I] himl : image list handle
6862 * SUCCESS : old image list
6865 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6867 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6868 INT oldHeight = infoPtr->nItemHeight;
6869 HIMAGELIST himlOld = 0;
6871 TRACE("(nType=%d, himl=%p\n", nType, himl);
6876 himlOld = infoPtr->himlNormal;
6877 infoPtr->himlNormal = himl;
6878 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6879 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6883 himlOld = infoPtr->himlSmall;
6884 infoPtr->himlSmall = himl;
6885 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6889 himlOld = infoPtr->himlState;
6890 infoPtr->himlState = himl;
6891 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6892 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6896 ERR("Unknown icon type=%d\n", nType);
6900 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6901 if (infoPtr->nItemHeight != oldHeight)
6902 LISTVIEW_UpdateScroll(infoPtr);
6909 * Preallocates memory (does *not* set the actual count of items !)
6912 * [I] infoPtr : valid pointer to the listview structure
6913 * [I] nItems : item count (projected number of items to allocate)
6914 * [I] dwFlags : update flags
6920 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6922 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6924 if (infoPtr->dwStyle & LVS_OWNERDATA)
6926 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6927 INT nOldCount = infoPtr->nItemCount;
6929 if (nItems < nOldCount)
6931 RANGE range = { nItems, nOldCount };
6932 ranges_del(infoPtr->selectionRanges, range);
6933 if (infoPtr->nFocusedItem >= nItems)
6935 infoPtr->nFocusedItem = -1;
6936 SetRectEmpty(&infoPtr->rcFocus);
6940 infoPtr->nItemCount = nItems;
6941 LISTVIEW_UpdateScroll(infoPtr);
6943 /* the flags are valid only in ownerdata report and list modes */
6944 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6946 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6947 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6949 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6950 LISTVIEW_InvalidateList(infoPtr);
6957 LISTVIEW_GetOrigin(infoPtr, &Origin);
6958 nFrom = min(nOldCount, nItems);
6959 nTo = max(nOldCount, nItems);
6961 if (uView == LVS_REPORT)
6964 rcErase.top = nFrom * infoPtr->nItemHeight;
6965 rcErase.right = infoPtr->nItemWidth;
6966 rcErase.bottom = nTo * infoPtr->nItemHeight;
6967 OffsetRect(&rcErase, Origin.x, Origin.y);
6968 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6969 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6973 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6975 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6976 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6977 rcErase.right = rcErase.left + infoPtr->nItemWidth;
6978 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6979 OffsetRect(&rcErase, Origin.x, Origin.y);
6980 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6981 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6983 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6985 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
6986 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6987 OffsetRect(&rcErase, Origin.x, Origin.y);
6988 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6989 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6995 /* According to MSDN for non-LVS_OWNERDATA this is just
6996 * a performance issue. The control allocates its internal
6997 * data structures for the number of items specified. It
6998 * cuts down on the number of memory allocations. Therefore
6999 * we will just issue a WARN here
7001 WARN("for non-ownerdata performance option not implemented.\n");
7009 * Sets the position of an item.
7012 * [I] infoPtr : valid pointer to the listview structure
7013 * [I] nItem : item index
7014 * [I] pt : coordinate
7020 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7022 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7025 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
7027 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7028 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7030 LISTVIEW_GetOrigin(infoPtr, &Origin);
7032 /* This point value seems to be an undocumented feature.
7033 * The best guess is that it means either at the origin,
7034 * or at true beginning of the list. I will assume the origin. */
7035 if ((pt.x == -1) && (pt.y == -1))
7038 if (uView == LVS_ICON)
7040 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7041 pt.y -= ICON_TOP_PADDING;
7046 infoPtr->bAutoarrange = FALSE;
7048 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7053 * Sets the state of one or many items.
7056 * [I] infoPtr : valid pointer to the listview structure
7057 * [I] nItem : item index
7058 * [I] lpLVItem : item or subitem info
7064 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7066 BOOL bResult = TRUE;
7069 lvItem.iItem = nItem;
7070 lvItem.iSubItem = 0;
7071 lvItem.mask = LVIF_STATE;
7072 lvItem.state = lpLVItem->state;
7073 lvItem.stateMask = lpLVItem->stateMask;
7074 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7078 /* apply to all items */
7079 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7080 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7083 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7086 * Update selection mark
7088 * Investigation on windows 2k showed that selection mark was updated
7089 * whenever a new selection was made, but if the selected item was
7090 * unselected it was not updated.
7092 * we are probably still not 100% accurate, but this at least sets the
7093 * proper selection mark when it is needed
7096 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7097 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7100 infoPtr->nSelectionMark = -1;
7101 for (i = 0; i < infoPtr->nItemCount; i++)
7103 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7105 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7107 infoPtr->nSelectionMark = i;
7111 else if (ranges_contain(infoPtr->selectionRanges, i))
7113 infoPtr->nSelectionMark = i;
7124 * Sets the text of an item or subitem.
7127 * [I] hwnd : window handle
7128 * [I] nItem : item index
7129 * [I] lpLVItem : item or subitem info
7130 * [I] isW : TRUE if input is Unicode
7136 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7140 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7142 lvItem.iItem = nItem;
7143 lvItem.iSubItem = lpLVItem->iSubItem;
7144 lvItem.mask = LVIF_TEXT;
7145 lvItem.pszText = lpLVItem->pszText;
7146 lvItem.cchTextMax = lpLVItem->cchTextMax;
7148 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7150 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7155 * Set item index that marks the start of a multiple selection.
7158 * [I] infoPtr : valid pointer to the listview structure
7159 * [I] nIndex : index
7162 * Index number or -1 if there is no selection mark.
7164 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7166 INT nOldIndex = infoPtr->nSelectionMark;
7168 TRACE("(nIndex=%d)\n", nIndex);
7170 infoPtr->nSelectionMark = nIndex;
7177 * Sets the text background color.
7180 * [I] infoPtr : valid pointer to the listview structure
7181 * [I] clrTextBk : text background color
7187 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7189 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7191 if (infoPtr->clrTextBk != clrTextBk)
7193 infoPtr->clrTextBk = clrTextBk;
7194 LISTVIEW_InvalidateList(infoPtr);
7202 * Sets the text foreground color.
7205 * [I] infoPtr : valid pointer to the listview structure
7206 * [I] clrText : text color
7212 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7214 TRACE("(clrText=%lx)\n", clrText);
7216 if (infoPtr->clrText != clrText)
7218 infoPtr->clrText = clrText;
7219 LISTVIEW_InvalidateList(infoPtr);
7227 * Determines which listview item is located at the specified position.
7230 * [I] infoPtr : valid pointer to the listview structure
7231 * [I] hwndNewToolTip : handle to new ToolTip
7236 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7238 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7239 infoPtr->hwndToolTip = hwndNewToolTip;
7240 return hwndOldToolTip;
7243 /* LISTVIEW_SetUnicodeFormat */
7244 /* LISTVIEW_SetWorkAreas */
7248 * Callback internally used by LISTVIEW_SortItems()
7251 * [I] first : pointer to first ITEM_INFO to compare
7252 * [I] second : pointer to second ITEM_INFO to compare
7253 * [I] lParam : HWND of control
7256 * if first comes before second : negative
7257 * if first comes after second : positive
7258 * if first and second are equivalent : zero
7260 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7262 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7263 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7264 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7266 /* Forward the call to the client defined callback */
7267 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7272 * Sorts the listview items.
7275 * [I] infoPtr : valid pointer to the listview structure
7276 * [I] pfnCompare : application-defined value
7277 * [I] lParamSort : pointer to comparision callback
7283 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7285 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7288 LPVOID selectionMarkItem;
7292 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7294 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7296 if (!pfnCompare) return FALSE;
7297 if (!infoPtr->hdpaItems) return FALSE;
7299 /* if there are 0 or 1 items, there is no need to sort */
7300 if (infoPtr->nItemCount < 2) return TRUE;
7302 if (infoPtr->nFocusedItem >= 0)
7304 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7305 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7306 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7308 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7309 /* clear the lpItem->state for non-selected ones */
7310 /* remove the selection ranges */
7312 infoPtr->pfnCompare = pfnCompare;
7313 infoPtr->lParamSort = lParamSort;
7314 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7316 /* Adjust selections and indices so that they are the way they should
7317 * be after the sort (otherwise, the list items move around, but
7318 * whatever is at the item's previous original position will be
7321 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7322 for (i=0; i < infoPtr->nItemCount; i++)
7324 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7325 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7327 if (lpItem->state & LVIS_SELECTED)
7329 item.state = LVIS_SELECTED;
7330 item.stateMask = LVIS_SELECTED;
7331 LISTVIEW_SetItemState(infoPtr, i, &item);
7333 if (lpItem->state & LVIS_FOCUSED)
7335 infoPtr->nFocusedItem = i;
7336 lpItem->state &= ~LVIS_FOCUSED;
7339 if (selectionMarkItem != NULL)
7340 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7341 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7343 /* refresh the display */
7344 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7345 LISTVIEW_InvalidateList(infoPtr);
7352 * Updates an items or rearranges the listview control.
7355 * [I] infoPtr : valid pointer to the listview structure
7356 * [I] nItem : item index
7362 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7364 TRACE("(nItem=%d)\n", nItem);
7366 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7368 /* rearrange with default alignment style */
7369 if (is_autoarrange(infoPtr))
7370 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7372 LISTVIEW_InvalidateItem(infoPtr, nItem);
7380 * Creates the listview control.
7383 * [I] hwnd : window handle
7384 * [I] lpcs : the create parameters
7390 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7392 LISTVIEW_INFO *infoPtr;
7393 UINT uView = lpcs->style & LVS_TYPEMASK;
7396 TRACE("(lpcs=%p)\n", lpcs);
7398 /* initialize info pointer */
7399 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7400 if (!infoPtr) return -1;
7402 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7404 infoPtr->hwndSelf = hwnd;
7405 infoPtr->dwStyle = lpcs->style;
7406 /* determine the type of structures to use */
7407 infoPtr->hwndNotify = lpcs->hwndParent;
7408 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7409 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7411 /* initialize color information */
7412 infoPtr->clrBk = CLR_NONE;
7413 infoPtr->clrText = comctl32_color.clrWindowText;
7414 infoPtr->clrTextBk = CLR_DEFAULT;
7415 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7417 /* set default values */
7418 infoPtr->nFocusedItem = -1;
7419 infoPtr->nSelectionMark = -1;
7420 infoPtr->nHotItem = -1;
7421 infoPtr->bRedraw = TRUE;
7422 infoPtr->bNoItemMetrics = TRUE;
7423 infoPtr->bDoChangeNotify = TRUE;
7424 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7425 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7426 infoPtr->nEditLabelItem = -1;
7427 infoPtr->dwHoverTime = -1; /* default system hover time */
7429 /* get default font (icon title) */
7430 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7431 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7432 infoPtr->hFont = infoPtr->hDefaultFont;
7433 LISTVIEW_SaveTextMetrics(infoPtr);
7436 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7437 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7438 0, 0, 0, 0, hwnd, NULL,
7439 lpcs->hInstance, NULL);
7440 if (!infoPtr->hwndHeader) goto fail;
7442 /* set header unicode format */
7443 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7445 /* set header font */
7446 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7448 /* allocate memory for the data structure */
7449 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7450 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7451 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7452 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7453 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7455 /* initialize the icon sizes */
7456 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7457 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7459 /* init item size to avoid division by 0 */
7460 LISTVIEW_UpdateItemSize (infoPtr);
7462 if (uView == LVS_REPORT)
7464 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7466 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7470 /* set HDS_HIDDEN flag to hide the header bar */
7471 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7472 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7479 DestroyWindow(infoPtr->hwndHeader);
7480 ranges_destroy(infoPtr->selectionRanges);
7481 DPA_Destroy(infoPtr->hdpaItems);
7482 DPA_Destroy(infoPtr->hdpaPosX);
7483 DPA_Destroy(infoPtr->hdpaPosY);
7484 DPA_Destroy(infoPtr->hdpaColumns);
7491 * Erases the background of the listview control.
7494 * [I] infoPtr : valid pointer to the listview structure
7495 * [I] hdc : device context handle
7501 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7505 TRACE("(hdc=%p)\n", hdc);
7507 if (!GetClipBox(hdc, &rc)) return FALSE;
7509 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7515 * Helper function for LISTVIEW_[HV]Scroll *only*.
7516 * Performs vertical/horizontal scrolling by a give amount.
7519 * [I] infoPtr : valid pointer to the listview structure
7520 * [I] dx : amount of horizontal scroll
7521 * [I] dy : amount of vertical scroll
7523 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7525 /* now we can scroll the list */
7526 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7527 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7528 /* if we have focus, adjust rect */
7529 OffsetRect(&infoPtr->rcFocus, dx, dy);
7530 UpdateWindow(infoPtr->hwndSelf);
7535 * Performs vertical scrolling.
7538 * [I] infoPtr : valid pointer to the listview structure
7539 * [I] nScrollCode : scroll code
7540 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7541 * [I] hScrollWnd : scrollbar control window handle
7547 * SB_LINEUP/SB_LINEDOWN:
7548 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7549 * for LVS_REPORT is 1 line
7550 * for LVS_LIST cannot occur
7553 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7554 INT nScrollDiff, HWND hScrollWnd)
7556 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7557 INT nOldScrollPos, nNewScrollPos;
7558 SCROLLINFO scrollInfo;
7561 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7562 debugscrollcode(nScrollCode), nScrollDiff);
7564 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7566 scrollInfo.cbSize = sizeof(SCROLLINFO);
7567 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7569 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7571 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7573 nOldScrollPos = scrollInfo.nPos;
7574 switch (nScrollCode)
7580 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7584 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7588 nScrollDiff = -scrollInfo.nPage;
7592 nScrollDiff = scrollInfo.nPage;
7595 case SB_THUMBPOSITION:
7597 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7604 /* quit right away if pos isn't changing */
7605 if (nScrollDiff == 0) return 0;
7607 /* calculate new position, and handle overflows */
7608 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7609 if (nScrollDiff > 0) {
7610 if (nNewScrollPos < nOldScrollPos ||
7611 nNewScrollPos > scrollInfo.nMax)
7612 nNewScrollPos = scrollInfo.nMax;
7614 if (nNewScrollPos > nOldScrollPos ||
7615 nNewScrollPos < scrollInfo.nMin)
7616 nNewScrollPos = scrollInfo.nMin;
7619 /* set the new position, and reread in case it changed */
7620 scrollInfo.fMask = SIF_POS;
7621 scrollInfo.nPos = nNewScrollPos;
7622 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7624 /* carry on only if it really changed */
7625 if (nNewScrollPos == nOldScrollPos) return 0;
7627 /* now adjust to client coordinates */
7628 nScrollDiff = nOldScrollPos - nNewScrollPos;
7629 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7631 /* and scroll the window */
7632 scroll_list(infoPtr, 0, nScrollDiff);
7639 * Performs horizontal scrolling.
7642 * [I] infoPtr : valid pointer to the listview structure
7643 * [I] nScrollCode : scroll code
7644 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7645 * [I] hScrollWnd : scrollbar control window handle
7651 * SB_LINELEFT/SB_LINERIGHT:
7652 * for LVS_ICON, LVS_SMALLICON 1 pixel
7653 * for LVS_REPORT is 1 pixel
7654 * for LVS_LIST is 1 column --> which is a 1 because the
7655 * scroll is based on columns not pixels
7658 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7659 INT nScrollDiff, HWND hScrollWnd)
7661 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7662 INT nOldScrollPos, nNewScrollPos;
7663 SCROLLINFO scrollInfo;
7665 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7666 debugscrollcode(nScrollCode), nScrollDiff);
7668 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7670 scrollInfo.cbSize = sizeof(SCROLLINFO);
7671 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7673 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7675 nOldScrollPos = scrollInfo.nPos;
7677 switch (nScrollCode)
7691 nScrollDiff = -scrollInfo.nPage;
7695 nScrollDiff = scrollInfo.nPage;
7698 case SB_THUMBPOSITION:
7700 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7707 /* quit right away if pos isn't changing */
7708 if (nScrollDiff == 0) return 0;
7710 /* calculate new position, and handle overflows */
7711 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7712 if (nScrollDiff > 0) {
7713 if (nNewScrollPos < nOldScrollPos ||
7714 nNewScrollPos > scrollInfo.nMax)
7715 nNewScrollPos = scrollInfo.nMax;
7717 if (nNewScrollPos > nOldScrollPos ||
7718 nNewScrollPos < scrollInfo.nMin)
7719 nNewScrollPos = scrollInfo.nMin;
7722 /* set the new position, and reread in case it changed */
7723 scrollInfo.fMask = SIF_POS;
7724 scrollInfo.nPos = nNewScrollPos;
7725 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7727 /* carry on only if it really changed */
7728 if (nNewScrollPos == nOldScrollPos) return 0;
7730 if(uView == LVS_REPORT)
7731 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7733 /* now adjust to client coordinates */
7734 nScrollDiff = nOldScrollPos - nNewScrollPos;
7735 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7737 /* and scroll the window */
7738 scroll_list(infoPtr, nScrollDiff, 0);
7743 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7745 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7746 INT gcWheelDelta = 0;
7747 INT pulScrollLines = 3;
7748 SCROLLINFO scrollInfo;
7750 TRACE("(wheelDelta=%d)\n", wheelDelta);
7752 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7753 gcWheelDelta -= wheelDelta;
7755 scrollInfo.cbSize = sizeof(SCROLLINFO);
7756 scrollInfo.fMask = SIF_POS;
7763 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7764 * should be fixed in the future.
7766 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7767 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7771 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7773 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7774 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7775 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7780 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7791 * [I] infoPtr : valid pointer to the listview structure
7792 * [I] nVirtualKey : virtual key
7793 * [I] lKeyData : key data
7798 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7800 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7802 NMLVKEYDOWN nmKeyDown;
7804 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7806 /* send LVN_KEYDOWN notification */
7807 nmKeyDown.wVKey = nVirtualKey;
7808 nmKeyDown.flags = 0;
7809 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7811 switch (nVirtualKey)
7814 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7816 notify(infoPtr, NM_RETURN);
7817 notify(infoPtr, LVN_ITEMACTIVATE);
7822 if (infoPtr->nItemCount > 0)
7827 if (infoPtr->nItemCount > 0)
7828 nItem = infoPtr->nItemCount - 1;
7832 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7836 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7840 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7844 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7848 if (uView == LVS_REPORT)
7849 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7851 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7852 * LISTVIEW_GetCountPerRow(infoPtr);
7853 if(nItem < 0) nItem = 0;
7857 if (uView == LVS_REPORT)
7858 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7860 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7861 * LISTVIEW_GetCountPerRow(infoPtr);
7862 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7866 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7867 LISTVIEW_KeySelection(infoPtr, nItem);
7877 * [I] infoPtr : valid pointer to the listview structure
7882 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7886 /* if we did not have the focus, there's nothing to do */
7887 if (!infoPtr->bFocus) return 0;
7889 /* send NM_KILLFOCUS notification */
7890 notify(infoPtr, NM_KILLFOCUS);
7892 /* if we have a focus rectagle, get rid of it */
7893 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7895 /* set window focus flag */
7896 infoPtr->bFocus = FALSE;
7898 /* invalidate the selected items before reseting focus flag */
7899 LISTVIEW_InvalidateSelectedItems(infoPtr);
7907 * Track mouse/dragging
7910 * [I] infoPtr : valid pointer to the listview structure
7911 * [I] pt : mouse coordinate
7916 static LRESULT LISTVIEW_TrackMouse(LISTVIEW_INFO *infoPtr, POINT pt)
7918 INT cxDrag = GetSystemMetrics(SM_CXDRAG);
7919 INT cyDrag = GetSystemMetrics(SM_CYDRAG);
7925 r.top = pt.y - cyDrag;
7926 r.left = pt.x - cxDrag;
7927 r.bottom = pt.y + cyDrag;
7928 r.right = pt.x + cxDrag;
7930 SetCapture(infoPtr->hwndSelf);
7934 if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
7936 if (msg.message == WM_MOUSEMOVE)
7938 pt.x = (short)LOWORD(msg.lParam);
7939 pt.y = (short)HIWORD(msg.lParam);
7940 if (PtInRect(&r, pt))
7948 else if (msg.message >= WM_LBUTTONDOWN &&
7949 msg.message <= WM_RBUTTONDBLCLK)
7954 DispatchMessageW(&msg);
7957 if (GetCapture() != infoPtr->hwndSelf)
7968 * Processes double click messages (left mouse button).
7971 * [I] infoPtr : valid pointer to the listview structure
7972 * [I] wKey : key flag
7973 * [I] x,y : mouse coordinate
7978 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
7980 LVHITTESTINFO htInfo;
7982 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
7984 /* send NM_RELEASEDCAPTURE notification */
7985 notify(infoPtr, NM_RELEASEDCAPTURE);
7990 /* send NM_DBLCLK notification */
7991 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7992 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7994 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7995 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8002 * Processes mouse down messages (left mouse button).
8005 * [I] infoPtr : valid pointer to the listview structure
8006 * [I] wKey : key flag
8007 * [I] x,y : mouse coordinate
8012 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8014 LVHITTESTINFO lvHitTestInfo;
8015 static BOOL bGroupSelect = TRUE;
8016 POINT pt = { x, y };
8019 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8021 /* send NM_RELEASEDCAPTURE notification */
8022 notify(infoPtr, NM_RELEASEDCAPTURE);
8024 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8026 /* set left button down flag */
8027 infoPtr->bLButtonDown = TRUE;
8029 lvHitTestInfo.pt.x = x;
8030 lvHitTestInfo.pt.y = y;
8032 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8033 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
8034 infoPtr->nEditLabelItem = -1;
8035 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8037 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8039 DWORD state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK) >> 12;
8040 if(state == 1 || state == 2)
8044 lvitem.state = state << 12;
8045 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8046 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8050 if (LISTVIEW_TrackMouse(infoPtr, lvHitTestInfo.pt))
8054 ZeroMemory(&nmlv, sizeof(nmlv));
8056 nmlv.ptAction.x = lvHitTestInfo.pt.x;
8057 nmlv.ptAction.y = lvHitTestInfo.pt.y;
8059 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
8064 if (infoPtr->dwStyle & LVS_SINGLESEL)
8066 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8067 infoPtr->nEditLabelItem = nItem;
8069 LISTVIEW_SetSelection(infoPtr, nItem);
8073 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8077 LISTVIEW_AddGroupSelection(infoPtr, nItem);
8078 LISTVIEW_SetItemFocus(infoPtr, nItem);
8079 infoPtr->nSelectionMark = nItem;
8085 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8086 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8088 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8089 infoPtr->nSelectionMark = nItem;
8092 else if (wKey & MK_CONTROL)
8096 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8098 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8099 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8100 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8101 infoPtr->nSelectionMark = nItem;
8103 else if (wKey & MK_SHIFT)
8105 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8109 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8110 infoPtr->nEditLabelItem = nItem;
8112 /* set selection (clears other pre-existing selections) */
8113 LISTVIEW_SetSelection(infoPtr, nItem);
8119 /* remove all selections */
8120 LISTVIEW_DeselectAll(infoPtr);
8129 * Processes mouse up messages (left mouse button).
8132 * [I] infoPtr : valid pointer to the listview structure
8133 * [I] wKey : key flag
8134 * [I] x,y : mouse coordinate
8139 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8141 LVHITTESTINFO lvHitTestInfo;
8143 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8145 if (!infoPtr->bLButtonDown) return 0;
8147 lvHitTestInfo.pt.x = x;
8148 lvHitTestInfo.pt.y = y;
8150 /* send NM_CLICK notification */
8151 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8152 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
8154 /* set left button flag */
8155 infoPtr->bLButtonDown = FALSE;
8157 /* if we clicked on a selected item, edit the label */
8158 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8159 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8166 * Destroys the listview control (called after WM_DESTROY).
8169 * [I] infoPtr : valid pointer to the listview structure
8174 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8178 /* delete all items */
8179 LISTVIEW_DeleteAllItems(infoPtr);
8181 /* destroy data structure */
8182 DPA_Destroy(infoPtr->hdpaItems);
8183 DPA_Destroy(infoPtr->hdpaPosX);
8184 DPA_Destroy(infoPtr->hdpaPosY);
8185 DPA_Destroy(infoPtr->hdpaColumns);
8186 ranges_destroy(infoPtr->selectionRanges);
8188 /* destroy image lists */
8189 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8191 if (infoPtr->himlNormal)
8192 ImageList_Destroy(infoPtr->himlNormal);
8193 if (infoPtr->himlSmall)
8194 ImageList_Destroy(infoPtr->himlSmall);
8195 if (infoPtr->himlState)
8196 ImageList_Destroy(infoPtr->himlState);
8199 /* destroy font, bkgnd brush */
8201 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8202 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8204 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8206 /* free listview info pointer*/
8214 * Handles notifications from header.
8217 * [I] infoPtr : valid pointer to the listview structure
8218 * [I] nCtrlId : control identifier
8219 * [I] lpnmh : notification information
8224 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8226 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8228 TRACE("(lpnmh=%p)\n", lpnmh);
8230 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8232 switch (lpnmh->hdr.code)
8236 case HDN_ITEMCHANGEDW:
8237 case HDN_ITEMCHANGEDA:
8239 COLUMN_INFO *lpColumnInfo;
8242 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8246 hdi.mask = HDI_WIDTH;
8247 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8251 cxy = lpnmh->pitem->cxy;
8253 /* determine how much we change since the last know position */
8254 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8255 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8258 RECT rcCol = lpColumnInfo->rcHeader;
8260 lpColumnInfo->rcHeader.right += dx;
8261 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8262 LISTVIEW_UpdateItemSize(infoPtr);
8263 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8265 /* this trick works for left aligned columns only */
8266 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8268 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
8269 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
8271 rcCol.top = infoPtr->rcList.top;
8272 rcCol.bottom = infoPtr->rcList.bottom;
8273 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8279 case HDN_ITEMCLICKW:
8280 case HDN_ITEMCLICKA:
8282 /* Handle sorting by Header Column */
8285 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8287 nmlv.iSubItem = lpnmh->iItem;
8288 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8298 * Determines the type of structure to use.
8301 * [I] infoPtr : valid pointer to the listview structureof the sender
8302 * [I] hwndFrom : listview window handle
8303 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8308 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8310 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8312 if (nCommand != NF_REQUERY) return 0;
8314 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8321 * Paints/Repaints the listview control.
8324 * [I] infoPtr : valid pointer to the listview structure
8325 * [I] hdc : device context handle
8330 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8332 TRACE("(hdc=%p)\n", hdc);
8334 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8336 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8338 infoPtr->bNoItemMetrics = FALSE;
8339 LISTVIEW_UpdateItemSize(infoPtr);
8340 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8341 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8342 LISTVIEW_UpdateScroll(infoPtr);
8345 LISTVIEW_Refresh(infoPtr, hdc);
8350 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8352 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8353 LISTVIEW_Refresh(infoPtr, hdc);
8354 EndPaint(infoPtr->hwndSelf, &ps);
8363 * Paints/Repaints the listview control.
8366 * [I] infoPtr : valid pointer to the listview structure
8367 * [I] hdc : device context handle
8368 * [I] options : drawing options
8373 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8375 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8377 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8380 if (options & PRF_ERASEBKGND)
8381 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8383 if (options & PRF_CLIENT)
8384 LISTVIEW_Paint(infoPtr, hdc);
8392 * Processes double click messages (right mouse button).
8395 * [I] infoPtr : valid pointer to the listview structure
8396 * [I] wKey : key flag
8397 * [I] x,y : mouse coordinate
8402 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8404 LVHITTESTINFO lvHitTestInfo;
8406 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8408 /* send NM_RELEASEDCAPTURE notification */
8409 notify(infoPtr, NM_RELEASEDCAPTURE);
8411 /* send NM_RDBLCLK notification */
8412 lvHitTestInfo.pt.x = x;
8413 lvHitTestInfo.pt.y = y;
8414 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8415 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8422 * Processes mouse down messages (right mouse button).
8425 * [I] infoPtr : valid pointer to the listview structure
8426 * [I] wKey : key flag
8427 * [I] x,y : mouse coordinate
8432 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8434 LVHITTESTINFO lvHitTestInfo;
8437 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8439 /* send NM_RELEASEDCAPTURE notification */
8440 notify(infoPtr, NM_RELEASEDCAPTURE);
8442 /* make sure the listview control window has the focus */
8443 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8445 /* set right button down flag */
8446 infoPtr->bRButtonDown = TRUE;
8448 /* determine the index of the selected item */
8449 lvHitTestInfo.pt.x = x;
8450 lvHitTestInfo.pt.y = y;
8451 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8453 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8455 LISTVIEW_SetItemFocus(infoPtr, nItem);
8456 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8457 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8458 LISTVIEW_SetSelection(infoPtr, nItem);
8462 LISTVIEW_DeselectAll(infoPtr);
8470 * Processes mouse up messages (right mouse button).
8473 * [I] infoPtr : valid pointer to the listview structure
8474 * [I] wKey : key flag
8475 * [I] x,y : mouse coordinate
8480 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8482 LVHITTESTINFO lvHitTestInfo;
8485 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8487 if (!infoPtr->bRButtonDown) return 0;
8489 /* set button flag */
8490 infoPtr->bRButtonDown = FALSE;
8492 /* Send NM_RClICK notification */
8493 lvHitTestInfo.pt.x = x;
8494 lvHitTestInfo.pt.y = y;
8495 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8496 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8498 /* Change to screen coordinate for WM_CONTEXTMENU */
8499 pt = lvHitTestInfo.pt;
8500 ClientToScreen(infoPtr->hwndSelf, &pt);
8502 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8503 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8504 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8515 * [I] infoPtr : valid pointer to the listview structure
8516 * [I] hwnd : window handle of window containing the cursor
8517 * [I] nHittest : hit-test code
8518 * [I] wMouseMsg : ideintifier of the mouse message
8521 * TRUE if cursor is set
8524 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8526 LVHITTESTINFO lvHitTestInfo;
8528 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8530 if(!infoPtr->hHotCursor) return FALSE;
8532 GetCursorPos(&lvHitTestInfo.pt);
8533 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8535 SetCursor(infoPtr->hHotCursor);
8545 * [I] infoPtr : valid pointer to the listview structure
8546 * [I] hwndLoseFocus : handle of previously focused window
8551 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8553 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8555 /* if we have the focus already, there's nothing to do */
8556 if (infoPtr->bFocus) return 0;
8558 /* send NM_SETFOCUS notification */
8559 notify(infoPtr, NM_SETFOCUS);
8561 /* set window focus flag */
8562 infoPtr->bFocus = TRUE;
8564 /* put the focus rect back on */
8565 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8567 /* redraw all visible selected items */
8568 LISTVIEW_InvalidateSelectedItems(infoPtr);
8578 * [I] infoPtr : valid pointer to the listview structure
8579 * [I] fRedraw : font handle
8580 * [I] fRedraw : redraw flag
8585 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8587 HFONT oldFont = infoPtr->hFont;
8589 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8591 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8592 if (infoPtr->hFont == oldFont) return 0;
8594 LISTVIEW_SaveTextMetrics(infoPtr);
8596 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8597 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8599 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8606 * Message handling for WM_SETREDRAW.
8607 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8610 * [I] infoPtr : valid pointer to the listview structure
8611 * [I] bRedraw: state of redraw flag
8614 * DefWinProc return value
8616 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8618 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8620 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8621 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8623 infoPtr->bRedraw = bRedraw;
8625 if(!bRedraw) return 0;
8627 if (is_autoarrange(infoPtr))
8628 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8629 LISTVIEW_UpdateScroll(infoPtr);
8631 /* despite what the WM_SETREDRAW docs says, apps expect us
8632 * to invalidate the listview here... stupid! */
8633 LISTVIEW_InvalidateList(infoPtr);
8640 * Resizes the listview control. This function processes WM_SIZE
8641 * messages. At this time, the width and height are not used.
8644 * [I] infoPtr : valid pointer to the listview structure
8645 * [I] Width : new width
8646 * [I] Height : new height
8651 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8653 RECT rcOld = infoPtr->rcList;
8655 TRACE("(width=%d, height=%d)\n", Width, Height);
8657 LISTVIEW_UpdateSize(infoPtr);
8658 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8660 /* do not bother with display related stuff if we're not redrawing */
8661 if (!is_redrawing(infoPtr)) return 0;
8663 if (is_autoarrange(infoPtr))
8664 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8666 LISTVIEW_UpdateScroll(infoPtr);
8668 /* refresh all only for lists whose height changed significantly */
8669 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8670 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8671 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8672 LISTVIEW_InvalidateList(infoPtr);
8679 * Sets the size information.
8682 * [I] infoPtr : valid pointer to the listview structure
8687 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8689 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8691 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8693 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8695 if (uView == LVS_LIST)
8697 /* Apparently the "LIST" style is supposed to have the same
8698 * number of items in a column even if there is no scroll bar.
8699 * Since if a scroll bar already exists then the bottom is already
8700 * reduced, only reduce if the scroll bar does not currently exist.
8701 * The "2" is there to mimic the native control. I think it may be
8702 * related to either padding or edges. (GLA 7/2002)
8704 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
8705 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8706 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8708 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8713 hl.prc = &infoPtr->rcList;
8715 Header_Layout(infoPtr->hwndHeader, &hl);
8717 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8719 infoPtr->rcList.top = max(wp.cy, 0);
8722 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8727 * Processes WM_STYLECHANGED messages.
8730 * [I] infoPtr : valid pointer to the listview structure
8731 * [I] wStyleType : window style type (normal or extended)
8732 * [I] lpss : window style information
8737 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8738 const STYLESTRUCT *lpss)
8740 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8741 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8743 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8744 wStyleType, lpss->styleOld, lpss->styleNew);
8746 if (wStyleType != GWL_STYLE) return 0;
8748 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8749 /* what if LVS_OWNERDATA changed? */
8750 /* or LVS_SINGLESEL */
8751 /* or LVS_SORT{AS,DES}CENDING */
8753 infoPtr->dwStyle = lpss->styleNew;
8755 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8756 ((lpss->styleNew & WS_HSCROLL) == 0))
8757 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8759 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8760 ((lpss->styleNew & WS_VSCROLL) == 0))
8761 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8763 if (uNewView != uOldView)
8765 SIZE oldIconSize = infoPtr->iconSize;
8768 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8769 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8771 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8772 SetRectEmpty(&infoPtr->rcFocus);
8774 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8775 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8777 if (uNewView == LVS_ICON)
8779 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8781 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8782 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8783 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8786 else if (uNewView == LVS_REPORT)
8791 hl.prc = &infoPtr->rcList;
8793 Header_Layout(infoPtr->hwndHeader, &hl);
8794 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8797 LISTVIEW_UpdateItemSize(infoPtr);
8800 if (uNewView == LVS_REPORT)
8801 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8803 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8804 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8805 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8807 /* update the size of the client area */
8808 LISTVIEW_UpdateSize(infoPtr);
8810 /* add scrollbars if needed */
8811 LISTVIEW_UpdateScroll(infoPtr);
8813 /* invalidate client area + erase background */
8814 LISTVIEW_InvalidateList(infoPtr);
8821 * Window procedure of the listview control.
8824 static LRESULT WINAPI
8825 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8827 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8829 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8831 if (!infoPtr && (uMsg != WM_CREATE))
8832 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8836 case LVM_APPROXIMATEVIEWRECT:
8837 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8838 LOWORD(lParam), HIWORD(lParam));
8840 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8842 /* case LVM_CANCELEDITLABEL: */
8844 case LVM_CREATEDRAGIMAGE:
8845 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
8847 case LVM_DELETEALLITEMS:
8848 return LISTVIEW_DeleteAllItems(infoPtr);
8850 case LVM_DELETECOLUMN:
8851 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8853 case LVM_DELETEITEM:
8854 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8856 case LVM_EDITLABELW:
8857 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8859 case LVM_EDITLABELA:
8860 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8862 /* case LVM_ENABLEGROUPVIEW: */
8864 case LVM_ENSUREVISIBLE:
8865 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8868 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8871 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8873 case LVM_GETBKCOLOR:
8874 return infoPtr->clrBk;
8876 /* case LVM_GETBKIMAGE: */
8878 case LVM_GETCALLBACKMASK:
8879 return infoPtr->uCallbackMask;
8881 case LVM_GETCOLUMNA:
8882 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8884 case LVM_GETCOLUMNW:
8885 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8887 case LVM_GETCOLUMNORDERARRAY:
8888 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8890 case LVM_GETCOLUMNWIDTH:
8891 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8893 case LVM_GETCOUNTPERPAGE:
8894 return LISTVIEW_GetCountPerPage(infoPtr);
8896 case LVM_GETEDITCONTROL:
8897 return (LRESULT)infoPtr->hwndEdit;
8899 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8900 return infoPtr->dwLvExStyle;
8902 /* case LVM_GETGROUPINFO: */
8904 /* case LVM_GETGROUPMETRICS: */
8907 return (LRESULT)infoPtr->hwndHeader;
8909 case LVM_GETHOTCURSOR:
8910 return (LRESULT)infoPtr->hHotCursor;
8912 case LVM_GETHOTITEM:
8913 return infoPtr->nHotItem;
8915 case LVM_GETHOVERTIME:
8916 return infoPtr->dwHoverTime;
8918 case LVM_GETIMAGELIST:
8919 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8921 /* case LVM_GETINSERTMARK: */
8923 /* case LVM_GETINSERTMARKCOLOR: */
8925 /* case LVM_GETINSERTMARKRECT: */
8927 case LVM_GETISEARCHSTRINGA:
8928 case LVM_GETISEARCHSTRINGW:
8929 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8933 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8936 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8938 case LVM_GETITEMCOUNT:
8939 return infoPtr->nItemCount;
8941 case LVM_GETITEMPOSITION:
8942 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8944 case LVM_GETITEMRECT:
8945 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8947 case LVM_GETITEMSPACING:
8948 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8950 case LVM_GETITEMSTATE:
8951 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8953 case LVM_GETITEMTEXTA:
8954 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8956 case LVM_GETITEMTEXTW:
8957 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8959 case LVM_GETNEXTITEM:
8960 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8962 case LVM_GETNUMBEROFWORKAREAS:
8963 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8967 if (!lParam) return FALSE;
8968 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8971 /* case LVM_GETOUTLINECOLOR: */
8973 /* case LVM_GETSELECTEDCOLUMN: */
8975 case LVM_GETSELECTEDCOUNT:
8976 return LISTVIEW_GetSelectedCount(infoPtr);
8978 case LVM_GETSELECTIONMARK:
8979 return infoPtr->nSelectionMark;
8981 case LVM_GETSTRINGWIDTHA:
8982 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8984 case LVM_GETSTRINGWIDTHW:
8985 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8987 case LVM_GETSUBITEMRECT:
8988 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8990 case LVM_GETTEXTBKCOLOR:
8991 return infoPtr->clrTextBk;
8993 case LVM_GETTEXTCOLOR:
8994 return infoPtr->clrText;
8996 /* case LVM_GETTILEINFO: */
8998 /* case LVM_GETTILEVIEWINFO: */
9000 case LVM_GETTOOLTIPS:
9001 if( !infoPtr->hwndToolTip )
9002 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9003 return (LRESULT)infoPtr->hwndToolTip;
9005 case LVM_GETTOPINDEX:
9006 return LISTVIEW_GetTopIndex(infoPtr);
9008 /*case LVM_GETUNICODEFORMAT:
9009 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9012 /* case LVM_GETVIEW: */
9014 case LVM_GETVIEWRECT:
9015 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9017 case LVM_GETWORKAREAS:
9018 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9021 /* case LVM_HASGROUP: */
9024 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9026 case LVM_INSERTCOLUMNA:
9027 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9029 case LVM_INSERTCOLUMNW:
9030 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9032 /* case LVM_INSERTGROUP: */
9034 /* case LVM_INSERTGROUPSORTED: */
9036 case LVM_INSERTITEMA:
9037 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9039 case LVM_INSERTITEMW:
9040 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9042 /* case LVM_INSERTMARKHITTEST: */
9044 /* case LVM_ISGROUPVIEWENABLED: */
9046 /* case LVM_MAPIDTOINDEX: */
9048 /* case LVM_MAPINDEXTOID: */
9050 /* case LVM_MOVEGROUP: */
9052 /* case LVM_MOVEITEMTOGROUP: */
9054 case LVM_REDRAWITEMS:
9055 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9057 /* case LVM_REMOVEALLGROUPS: */
9059 /* case LVM_REMOVEGROUP: */
9062 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9064 case LVM_SETBKCOLOR:
9065 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9067 /* case LVM_SETBKIMAGE: */
9069 case LVM_SETCALLBACKMASK:
9070 infoPtr->uCallbackMask = (UINT)wParam;
9073 case LVM_SETCOLUMNA:
9074 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9076 case LVM_SETCOLUMNW:
9077 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9079 case LVM_SETCOLUMNORDERARRAY:
9080 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9082 case LVM_SETCOLUMNWIDTH:
9083 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9085 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9086 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9088 /* case LVM_SETGROUPINFO: */
9090 /* case LVM_SETGROUPMETRICS: */
9092 case LVM_SETHOTCURSOR:
9093 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9095 case LVM_SETHOTITEM:
9096 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9098 case LVM_SETHOVERTIME:
9099 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9101 case LVM_SETICONSPACING:
9102 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9104 case LVM_SETIMAGELIST:
9105 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9107 /* case LVM_SETINFOTIP: */
9109 /* case LVM_SETINSERTMARK: */
9111 /* case LVM_SETINSERTMARKCOLOR: */
9114 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9117 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9119 case LVM_SETITEMCOUNT:
9120 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9122 case LVM_SETITEMPOSITION:
9125 pt.x = (short)LOWORD(lParam);
9126 pt.y = (short)HIWORD(lParam);
9127 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9130 case LVM_SETITEMPOSITION32:
9131 if (lParam == 0) return FALSE;
9132 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9134 case LVM_SETITEMSTATE:
9135 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9137 case LVM_SETITEMTEXTA:
9138 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9140 case LVM_SETITEMTEXTW:
9141 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9143 /* case LVM_SETOUTLINECOLOR: */
9145 /* case LVM_SETSELECTEDCOLUMN: */
9147 case LVM_SETSELECTIONMARK:
9148 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9150 case LVM_SETTEXTBKCOLOR:
9151 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9153 case LVM_SETTEXTCOLOR:
9154 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9156 /* case LVM_SETTILEINFO: */
9158 /* case LVM_SETTILEVIEWINFO: */
9160 /* case LVM_SETTILEWIDTH: */
9162 case LVM_SETTOOLTIPS:
9163 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9165 /* case LVM_SETUNICODEFORMAT: */
9167 /* case LVM_SETVIEW: */
9169 /* case LVM_SETWORKAREAS: */
9171 /* case LVM_SORTGROUPS: */
9174 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9176 /* LVM_SORTITEMSEX: */
9178 case LVM_SUBITEMHITTEST:
9179 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9182 return LISTVIEW_Update(infoPtr, (INT)wParam);
9185 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9188 return LISTVIEW_Command(infoPtr, wParam, lParam);
9191 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9194 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9197 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9200 return (LRESULT)infoPtr->hFont;
9203 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9206 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9209 return LISTVIEW_KillFocus(infoPtr);
9211 case WM_LBUTTONDBLCLK:
9212 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9214 case WM_LBUTTONDOWN:
9215 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9218 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9221 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9224 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9227 return LISTVIEW_NCDestroy(infoPtr);
9230 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9231 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9234 case WM_NOTIFYFORMAT:
9235 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9237 case WM_PRINTCLIENT:
9238 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9241 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9243 case WM_RBUTTONDBLCLK:
9244 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9246 case WM_RBUTTONDOWN:
9247 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9250 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9253 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9258 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9261 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9264 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9267 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9269 case WM_STYLECHANGED:
9270 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9272 case WM_SYSCOLORCHANGE:
9273 COMCTL32_RefreshSysColors();
9276 /* case WM_TIMER: */
9279 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9282 if (wParam & (MK_SHIFT | MK_CONTROL))
9283 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9284 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9286 case WM_WINDOWPOSCHANGED:
9287 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9289 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9290 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9291 LISTVIEW_UpdateSize(infoPtr);
9292 LISTVIEW_UpdateScroll(infoPtr);
9294 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9296 /* case WM_WININICHANGE: */
9299 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9300 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9303 /* call default window procedure */
9304 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9311 * Registers the window class.
9319 void LISTVIEW_Register(void)
9323 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9324 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9325 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9326 wndClass.cbClsExtra = 0;
9327 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9328 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9329 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9330 wndClass.lpszClassName = WC_LISTVIEWW;
9331 RegisterClassW(&wndClass);
9336 * Unregisters the window class.
9344 void LISTVIEW_Unregister(void)
9346 UnregisterClassW(WC_LISTVIEWW, NULL);
9351 * Handle any WM_COMMAND messages
9354 * [I] infoPtr : valid pointer to the listview structure
9355 * [I] wParam : the first message parameter
9356 * [I] lParam : the second message parameter
9361 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9363 switch (HIWORD(wParam))
9368 * Adjust the edit window size
9371 HDC hdc = GetDC(infoPtr->hwndEdit);
9372 HFONT hFont, hOldFont = 0;
9377 if (!infoPtr->hwndEdit || !hdc) return 0;
9378 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9379 GetWindowRect(infoPtr->hwndEdit, &rect);
9381 /* Select font to get the right dimension of the string */
9382 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9385 hOldFont = SelectObject(hdc, hFont);
9388 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9390 TEXTMETRICW textMetric;
9392 /* Add Extra spacing for the next character */
9393 GetTextMetricsW(hdc, &textMetric);
9394 sz.cx += (textMetric.tmMaxCharWidth * 2);
9402 rect.bottom - rect.top,
9403 SWP_DRAWFRAME|SWP_NOMOVE);
9406 SelectObject(hdc, hOldFont);
9408 ReleaseDC(infoPtr->hwndSelf, hdc);
9414 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9423 * Subclassed edit control windproc function
9426 * [I] hwnd : the edit window handle
9427 * [I] uMsg : the message that is to be processed
9428 * [I] wParam : first message parameter
9429 * [I] lParam : second message parameter
9430 * [I] isW : TRUE if input is Unicode
9435 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9437 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9438 BOOL cancel = FALSE;
9440 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9441 hwnd, uMsg, wParam, lParam, isW);
9446 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9453 WNDPROC editProc = infoPtr->EditWndProc;
9454 infoPtr->EditWndProc = 0;
9455 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9456 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9460 if (VK_ESCAPE == (INT)wParam)
9465 else if (VK_RETURN == (INT)wParam)
9469 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9473 if (infoPtr->hwndEdit)
9475 LPWSTR buffer = NULL;
9477 infoPtr->hwndEdit = 0;
9480 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9484 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9486 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9487 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9491 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9493 if (buffer) Free(buffer);
9497 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9503 * Subclassed edit control Unicode windproc function
9506 * [I] hwnd : the edit window handle
9507 * [I] uMsg : the message that is to be processed
9508 * [I] wParam : first message parameter
9509 * [I] lParam : second message parameter
9513 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9515 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9520 * Subclassed edit control ANSI windproc function
9523 * [I] hwnd : the edit window handle
9524 * [I] uMsg : the message that is to be processed
9525 * [I] wParam : first message parameter
9526 * [I] lParam : second message parameter
9530 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9532 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9537 * Creates a subclassed edit cotrol
9540 * [I] infoPtr : valid pointer to the listview structure
9541 * [I] text : initial text for the edit
9542 * [I] style : the window style
9543 * [I] isW : TRUE if input is Unicode
9547 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9548 INT x, INT y, INT width, INT height, BOOL isW)
9550 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9555 TEXTMETRICW textMetric;
9556 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9558 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9560 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9561 hdc = GetDC(infoPtr->hwndSelf);
9563 /* Select the font to get appropriate metric dimensions */
9564 if(infoPtr->hFont != 0)
9565 hOldFont = SelectObject(hdc, infoPtr->hFont);
9567 /*Get String Length in pixels */
9568 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9570 /*Add Extra spacing for the next character */
9571 GetTextMetricsW(hdc, &textMetric);
9572 sz.cx += (textMetric.tmMaxCharWidth * 2);
9574 if(infoPtr->hFont != 0)
9575 SelectObject(hdc, hOldFont);
9577 ReleaseDC(infoPtr->hwndSelf, hdc);
9579 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9581 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9583 if (!hedit) return 0;
9585 infoPtr->EditWndProc = (WNDPROC)
9586 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9587 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9589 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);