4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on Oct. 21, 2002, by Dimitrie O. Paun.
29 * Unless otherwise noted, we belive this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
36 * -- Hot item handling, mouse hovering
37 * -- Workareas support
42 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
43 * -- Support CustonDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs.
44 * -- in LISTVIEW_AddGroupSelection, se whould send LVN_ODSTATECHANGED
45 * -- LVA_SNAPTOGRID not implemented
46 * -- LISTVIEW_ApproximateViewRect partially implemented
47 * -- LISTVIEW_[GS]etColumnOrderArray stubs
48 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
49 * -- LISTVIEW_SetIconSpacing is incomplete
50 * -- LISTVIEW_SortItems is broken
51 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
54 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
55 * linear in the number of items in the list, and this is
56 * unacceptable for large lists.
57 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
58 * instead of inserting in the right spot
59 * -- we should keep an ordered array of coordinates in iconic mode
60 * this would allow to frame items (iterator_frameditems),
61 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
69 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
76 * -- LVS_NOSCROLL (see Q137520)
77 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
80 * -- LVS_EX_BORDERSELECT
83 * -- LVS_EX_HEADERDRAGDROP
86 * -- LVS_EX_MULTIWORKAREAS
87 * -- LVS_EX_ONECLICKACTIVATE
89 * -- LVS_EX_SIMPLESELECT
90 * -- LVS_EX_TRACKSELECT
91 * -- LVS_EX_TWOCLICKACTIVATE
92 * -- LVS_EX_UNDERLINECOLD
93 * -- LVS_EX_UNDERLINEHOT
96 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
101 * -- LVN_ODSTATECHANGED
106 * -- LVM_CANCELEDITLABEL
107 * -- LVM_ENABLEGROUPVIEW
108 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
109 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
110 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
111 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
112 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
113 * -- LVM_GETINSERTMARKRECT
114 * -- LVM_GETNUMBEROFWORKAREAS
115 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
116 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
117 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
118 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
119 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
120 * -- LVM_GETTOOLTIPS, LVM_SETTOOLTIPS
121 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
122 * -- LVM_GETVIEW, LVM_SETVIEW
123 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
124 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
125 * -- LVM_INSERTGROUPSORTED
126 * -- LVM_INSERTMARKHITTEST
127 * -- LVM_ISGROUPVIEWENABLED
128 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
130 * -- LVM_MOVEITEMTOGROUP
132 * -- LVM_SETTILEWIDTH
136 * Known differences in message stream from native control (not known if
137 * these differences cause problems):
138 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
139 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
140 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
141 * processing for "USEDOUBLECLICKTIME".
145 #include "wine/port.h"
160 #include "commctrl.h"
161 #include "comctl32.h"
163 #include "wine/debug.h"
164 #include "wine/unicode.h"
166 WINE_DEFAULT_DEBUG_CHANNEL(listview);
168 /* make sure you set this to 0 for production use! */
169 #define DEBUG_RANGES 1
171 typedef struct tagCOLUMN_INFO
173 RECT rcHeader; /* tracks the header's rectangle */
174 int fmt; /* same as LVCOLUMN.fmt */
177 typedef struct tagITEMHDR
181 } ITEMHDR, *LPITEMHDR;
183 typedef struct tagSUBITEM_INFO
189 typedef struct tagITEM_INFO
197 typedef struct tagRANGE
203 typedef struct tagRANGES
208 typedef struct tagITERATOR
217 typedef struct tagLISTVIEW_INFO
224 COLORREF clrTextBkDefault;
225 HIMAGELIST himlNormal;
226 HIMAGELIST himlSmall;
227 HIMAGELIST himlState;
230 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
233 RANGES selectionRanges;
238 RECT rcList; /* This rectangle is really the window
239 * client rectangle possibly reduced by the
240 * horizontal scroll bar and/or header - see
241 * LISTVIEW_UpdateSize. This rectangle offset
242 * by the LISTVIEW_GetOrigin value is in
243 * client coordinates */
252 INT ntmHeight; /* Some cached metrics of the font used */
253 INT ntmAveCharWidth; /* by the listview to draw items */
254 BOOL bRedraw; /* Turns on/off repaints & invalidations */
255 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
257 BOOL bDoChangeNotify; /* send change notification messages? */
260 DWORD dwStyle; /* the cached window GWL_STYLE */
261 DWORD dwLvExStyle; /* extended listview style */
262 INT nItemCount; /* the number of items in the list */
263 HDPA hdpaItems; /* array ITEM_INFO pointers */
264 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
265 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
266 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
267 POINT currIconPos; /* this is the position next icon will be placed */
268 PFNLVCOMPARE pfnCompare;
276 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
278 DWORD lastKeyPressTimestamp;
280 INT nSearchParamLength;
281 WCHAR szSearchParam[ MAX_PATH ];
288 /* How many we debug buffer to allocate */
289 #define DEBUG_BUFFERS 20
290 /* The size of a single debug bbuffer */
291 #define DEBUG_BUFFER_SIZE 256
293 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
294 #define SB_INTERNAL -1
296 /* maximum size of a label */
297 #define DISP_TEXT_SIZE 512
299 /* padding for items in list and small icon display modes */
300 #define WIDTH_PADDING 12
302 /* padding for items in list, report and small icon display modes */
303 #define HEIGHT_PADDING 1
305 /* offset of items in report display mode */
306 #define REPORT_MARGINX 2
308 /* padding for icon in large icon display mode
309 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
310 * that HITTEST will see.
311 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
312 * ICON_TOP_PADDING - sum of the two above.
313 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
314 * LABEL_HOR_PADDING - between text and sides of box
315 * LABEL_VERT_PADDING - between bottom of text and end of box
317 * ICON_LR_PADDING - additional width above icon size.
318 * ICON_LR_HALF - half of the above value
320 #define ICON_TOP_PADDING_NOTHITABLE 2
321 #define ICON_TOP_PADDING_HITABLE 2
322 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
323 #define ICON_BOTTOM_PADDING 4
324 #define LABEL_HOR_PADDING 5
325 #define LABEL_VERT_PADDING 7
326 #define ICON_LR_PADDING 16
327 #define ICON_LR_HALF (ICON_LR_PADDING/2)
329 /* default label width for items in list and small icon display modes */
330 #define DEFAULT_LABEL_WIDTH 40
332 /* default column width for items in list display mode */
333 #define DEFAULT_COLUMN_WIDTH 128
335 /* Size of "line" scroll for V & H scrolls */
336 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
338 /* Padding betwen image and label */
339 #define IMAGE_PADDING 2
341 /* Padding behind the label */
342 #define TRAILING_LABEL_PADDING 12
343 #define TRAILING_HEADER_PADDING 11
345 /* Border for the icon caption */
346 #define CAPTION_BORDER 2
348 /* Standard DrawText flags */
349 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
350 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
351 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
353 /* The time in milliseconds to reset the search in the list */
354 #define KEY_DELAY 450
356 /* Dump the LISTVIEW_INFO structure to the debug channel */
357 #define LISTVIEW_DUMP(iP) do { \
358 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
359 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
360 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
361 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
362 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
363 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
364 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
365 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
366 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
367 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
371 * forward declarations
373 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
374 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
375 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
376 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
377 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
378 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
379 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
380 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
381 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
382 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
383 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
384 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
385 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
386 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
387 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
388 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
389 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
390 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
391 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
392 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
393 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
394 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
395 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
396 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
397 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
398 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
400 /******** Text handling functions *************************************/
402 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
403 * text string. The string may be ANSI or Unicode, in which case
404 * the boolean isW tells us the type of the string.
406 * The name of the function tell what type of strings it expects:
407 * W: Unicode, T: ANSI/Unicode - function of isW
410 static inline BOOL is_textW(LPCWSTR text)
412 return text != NULL && text != LPSTR_TEXTCALLBACKW;
415 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
417 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
418 return is_textW(text);
421 static inline int textlenT(LPCWSTR text, BOOL isW)
423 return !is_textT(text, isW) ? 0 :
424 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
427 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
430 if (isSrcW) lstrcpynW(dest, src, max);
431 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
433 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
434 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
437 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
439 LPWSTR wstr = (LPWSTR)text;
441 if (!isW && is_textT(text, isW))
443 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
444 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
445 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
447 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
451 static inline void textfreeT(LPWSTR wstr, BOOL isW)
453 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
457 * dest is a pointer to a Unicode string
458 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
460 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
464 if (src == LPSTR_TEXTCALLBACKW)
466 if (is_textW(*dest)) Free(*dest);
467 *dest = LPSTR_TEXTCALLBACKW;
471 LPWSTR pszText = textdupTtoW(src, isW);
472 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
473 bResult = Str_SetPtrW(dest, pszText);
474 textfreeT(pszText, isW);
480 * compares a Unicode to a Unicode/ANSI text string
482 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
484 if (!aw) return bt ? -1 : 0;
485 if (!bt) return aw ? 1 : 0;
486 if (aw == LPSTR_TEXTCALLBACKW)
487 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
488 if (bt != LPSTR_TEXTCALLBACKW)
490 LPWSTR bw = textdupTtoW(bt, isW);
491 int r = bw ? lstrcmpW(aw, bw) : 1;
499 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
503 n = min(min(n, strlenW(s1)), strlenW(s2));
504 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
505 return res ? res - sizeof(WCHAR) : res;
508 /******** Debugging functions *****************************************/
510 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
512 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
513 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
516 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
518 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
519 n = min(textlenT(text, isW), n);
520 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
523 static char* debug_getbuf()
525 static int index = 0;
526 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
527 return buffers[index++ % DEBUG_BUFFERS];
530 static inline const char* debugrange(const RANGE *lprng)
534 char* buf = debug_getbuf();
535 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
537 } else return "(null)";
540 static inline const char* debugpoint(const POINT *lppt)
544 char* buf = debug_getbuf();
545 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
547 } else return "(null)";
550 static inline const char* debugrect(const RECT *rect)
554 char* buf = debug_getbuf();
555 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
556 rect->left, rect->top, rect->right, rect->bottom);
558 } else return "(null)";
561 static const char * debugscrollinfo(const SCROLLINFO *pScrollInfo)
563 char* buf = debug_getbuf(), *text = buf;
564 int len, size = DEBUG_BUFFER_SIZE;
566 if (pScrollInfo == NULL) return "(null)";
567 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
568 if (len == -1) goto end; buf += len; size -= len;
569 if (pScrollInfo->fMask & SIF_RANGE)
570 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
572 if (len == -1) goto end; buf += len; size -= len;
573 if (pScrollInfo->fMask & SIF_PAGE)
574 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
576 if (len == -1) goto end; buf += len; size -= len;
577 if (pScrollInfo->fMask & SIF_POS)
578 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
580 if (len == -1) goto end; buf += len; size -= len;
581 if (pScrollInfo->fMask & SIF_TRACKPOS)
582 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
584 if (len == -1) goto end; buf += len; size -= len;
587 buf = text + strlen(text);
589 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
593 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
597 char* buf = debug_getbuf();
598 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
599 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
600 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
601 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
603 } else return "(null)";
606 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
608 char* buf = debug_getbuf(), *text = buf;
609 int len, size = DEBUG_BUFFER_SIZE;
611 if (lpLVItem == NULL) return "(null)";
612 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
613 if (len == -1) goto end; buf += len; size -= len;
614 if (lpLVItem->mask & LVIF_STATE)
615 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
617 if (len == -1) goto end; buf += len; size -= len;
618 if (lpLVItem->mask & LVIF_TEXT)
619 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
621 if (len == -1) goto end; buf += len; size -= len;
622 if (lpLVItem->mask & LVIF_IMAGE)
623 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
625 if (len == -1) goto end; buf += len; size -= len;
626 if (lpLVItem->mask & LVIF_PARAM)
627 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
629 if (len == -1) goto end; buf += len; size -= len;
630 if (lpLVItem->mask & LVIF_INDENT)
631 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
633 if (len == -1) goto end; buf += len; size -= len;
636 buf = text + strlen(text);
638 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
642 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
644 char* buf = debug_getbuf(), *text = buf;
645 int len, size = DEBUG_BUFFER_SIZE;
647 if (lpColumn == NULL) return "(null)";
648 len = snprintf(buf, size, "{");
649 if (len == -1) goto end; buf += len; size -= len;
650 if (lpColumn->mask & LVCF_SUBITEM)
651 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
653 if (len == -1) goto end; buf += len; size -= len;
654 if (lpColumn->mask & LVCF_FMT)
655 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
657 if (len == -1) goto end; buf += len; size -= len;
658 if (lpColumn->mask & LVCF_WIDTH)
659 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
661 if (len == -1) goto end; buf += len; size -= len;
662 if (lpColumn->mask & LVCF_TEXT)
663 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
665 if (len == -1) goto end; buf += len; size -= len;
666 if (lpColumn->mask & LVCF_IMAGE)
667 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
669 if (len == -1) goto end; buf += len; size -= len;
670 if (lpColumn->mask & LVCF_ORDER)
671 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
673 if (len == -1) goto end; buf += len; size -= len;
676 buf = text + strlen(text);
678 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
682 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
686 char* buf = debug_getbuf();
687 snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
688 debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
690 } else return "(null)";
693 /* Return the corresponding text for a given scroll value */
694 static inline LPCSTR debugscrollcode(int nScrollCode)
698 case SB_LINELEFT: return "SB_LINELEFT";
699 case SB_LINERIGHT: return "SB_LINERIGHT";
700 case SB_PAGELEFT: return "SB_PAGELEFT";
701 case SB_PAGERIGHT: return "SB_PAGERIGHT";
702 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
703 case SB_THUMBTRACK: return "SB_THUMBTRACK";
704 case SB_ENDSCROLL: return "SB_ENDSCROLL";
705 case SB_INTERNAL: return "SB_INTERNAL";
706 default: return "unknown";
711 /******** Notification functions i************************************/
713 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
717 TRACE("(code=%d)\n", code);
719 pnmh->hwndFrom = infoPtr->hwndSelf;
720 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
722 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
723 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
725 TRACE(" <= %ld\n", result);
730 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
733 return notify_hdr(infoPtr, code, &nmh);
736 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
747 item.mask = LVIF_PARAM|LVIF_STATE;
748 item.iItem = htInfo->iItem;
750 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
751 nmia.lParam = item.lParam;
752 nmia.uOldState = item.state;
753 nmia.uNewState = item.state | LVIS_ACTIVATING;
754 nmia.uChanged = LVIF_STATE;
757 nmia.iItem = htInfo->iItem;
758 nmia.iSubItem = htInfo->iSubItem;
759 nmia.ptAction = htInfo->pt;
761 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
762 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
763 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
765 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
768 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
770 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
771 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
774 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
779 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
780 ZeroMemory(&nmlv, sizeof(nmlv));
781 nmlv.iItem = lvht->iItem;
782 nmlv.iSubItem = lvht->iSubItem;
783 nmlv.ptAction = lvht->pt;
784 item.mask = LVIF_PARAM;
785 item.iItem = lvht->iItem;
787 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
788 return notify_listview(infoPtr, code, &nmlv);
791 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
796 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
798 item.mask = LVIF_PARAM;
801 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
802 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
805 static int get_ansi_notification(INT unicodeNotificationCode)
807 switch (unicodeNotificationCode)
809 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
810 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
811 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
812 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
813 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
814 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
816 ERR("unknown notification %x\n", unicodeNotificationCode);
822 With testing on Windows 2000 it looks like the notify format
823 has nothing to do with this message. It ALWAYS seems to be
826 infoPtr : listview struct
827 notificationCode : *Unicode* notification code
828 pdi : dispinfo structure (can be unicode or ansi)
829 isW : TRUE if dispinfo is Unicode
831 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
833 BOOL bResult = FALSE;
834 BOOL convertToAnsi = FALSE;
835 INT cchTempBufMax = 0, savCchTextMax = 0;
836 LPWSTR pszTempBuf = NULL, savPszText = NULL;
838 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
843 if (notificationCode != LVN_GETDISPINFOW)
845 cchTempBufMax = WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText,
846 -1, NULL, 0, NULL, NULL);
850 cchTempBufMax = pdi->item.cchTextMax;
851 *pdi->item.pszText = 0; /* make sure we don't process garbage */
854 pszTempBuf = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) *
856 if (!pszTempBuf) return FALSE;
858 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR)
859 pszTempBuf, cchTempBufMax, NULL, NULL);
861 savCchTextMax = pdi->item.cchTextMax;
862 savPszText = pdi->item.pszText;
863 pdi->item.pszText = pszTempBuf;
864 pdi->item.cchTextMax = cchTempBufMax;
867 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat !=
870 bResult = notify_hdr(infoPtr, get_ansi_notification(notificationCode),
875 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
876 savPszText, savCchTextMax);
877 pdi->item.pszText = savPszText; /* restores our buffer */
878 pdi->item.cchTextMax = savCchTextMax;
879 HeapFree(GetProcessHeap(), 0, pszTempBuf);
884 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
885 const RECT *rcBounds, const LVITEMW *lplvItem)
887 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
888 lpnmlvcd->nmcd.hdc = hdc;
889 lpnmlvcd->nmcd.rc = *rcBounds;
890 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
891 lpnmlvcd->clrText = infoPtr->clrText;
892 if (!lplvItem) return;
893 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
894 lpnmlvcd->iSubItem = lplvItem->iSubItem;
895 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
896 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
897 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
898 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
901 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
903 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
906 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
907 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
908 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
909 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
910 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
911 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
915 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
917 /* apprently, for selected items, we have to override the returned values */
918 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
922 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
923 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
925 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
927 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
928 lpnmlvcd->clrText = comctl32_color.clrBtnText;
932 /* Set the text attributes */
933 if (lpnmlvcd->clrTextBk != CLR_NONE)
935 SetBkMode(hdc, OPAQUE);
936 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
937 SetBkColor(hdc, infoPtr->clrTextBkDefault);
939 SetBkColor(hdc,lpnmlvcd->clrTextBk);
942 SetBkMode(hdc, TRANSPARENT);
943 SetTextColor(hdc, lpnmlvcd->clrText);
946 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
948 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
951 /******** Item iterator functions **********************************/
953 static RANGES ranges_create(int count);
954 static void ranges_destroy(RANGES ranges);
955 static BOOL ranges_add(RANGES ranges, RANGE range);
956 static BOOL ranges_del(RANGES ranges, RANGE range);
957 static void ranges_dump(RANGES ranges);
959 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
961 RANGE range = { nItem, nItem + 1 };
963 return ranges_add(ranges, range);
966 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
968 RANGE range = { nItem, nItem + 1 };
970 return ranges_del(ranges, range);
974 * ITERATOR DOCUMENTATION
976 * The iterator functions allow for easy, and convenient iteration
977 * over items of iterest in the list. Typically, you create a
978 * iterator, use it, and destroy it, as such:
981 * iterator_xxxitems(&i, ...);
982 * while (iterator_{prev,next}(&i)
984 * //code which uses i.nItem
986 * iterator_destroy(&i);
988 * where xxx is either: framed, or visible.
989 * Note that it is important that the code destroys the iterator
990 * after it's done with it, as the creation of the iterator may
991 * allocate memory, which thus needs to be freed.
993 * You can iterate both forwards, and backwards through the list,
994 * by using iterator_next or iterator_prev respectively.
996 * Lower numbered items are draw on top of higher number items in
997 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
998 * items may overlap). So, to test items, you should use
1000 * which lists the items top to bottom (in Z-order).
1001 * For drawing items, you should use
1003 * which lists the items bottom to top (in Z-order).
1004 * If you keep iterating over the items after the end-of-items
1005 * marker (-1) is returned, the iterator will start from the
1006 * beginning. Typically, you don't need to test for -1,
1007 * because iterator_{next,prev} will return TRUE if more items
1008 * are to be iterated over, or FALSE otherwise.
1010 * Note: the iterator is defined to be bidirectional. That is,
1011 * any number of prev followed by any number of next, or
1012 * five versa, should leave the iterator at the same item:
1013 * prev * n, next * n = next * n, prev * n
1015 * The iterator has a notion of a out-of-order, special item,
1016 * which sits at the start of the list. This is used in
1017 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1018 * which needs to be first, as it may overlap other items.
1020 * The code is a bit messy because we have:
1021 * - a special item to deal with
1022 * - simple range, or composite range
1024 * If you find bugs, or want to add features, please make sure you
1025 * always check/modify *both* iterator_prev, and iterator_next.
1029 * This function iterates through the items in increasing order,
1030 * but prefixed by the special item, then -1. That is:
1031 * special, 1, 2, 3, ..., n, -1.
1032 * Each item is listed only once.
1034 static inline BOOL iterator_next(ITERATOR* i)
1038 i->nItem = i->nSpecial;
1039 if (i->nItem != -1) return TRUE;
1041 if (i->nItem == i->nSpecial)
1043 if (i->ranges) i->index = 0;
1049 if (i->nItem == i->nSpecial) i->nItem++;
1050 if (i->nItem < i->range.upper) return TRUE;
1055 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1056 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1059 else if (i->nItem >= i->range.upper) goto end;
1061 i->nItem = i->range.lower;
1062 if (i->nItem >= 0) goto testitem;
1069 * This function iterates through the items in decreasing order,
1070 * followed by the special item, then -1. That is:
1071 * n, n-1, ..., 3, 2, 1, special, -1.
1072 * Each item is listed only once.
1074 static inline BOOL iterator_prev(ITERATOR* i)
1081 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1084 if (i->nItem == i->nSpecial)
1092 if (i->nItem == i->nSpecial) i->nItem--;
1093 if (i->nItem >= i->range.lower) return TRUE;
1099 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1102 else if (!start && i->nItem < i->range.lower) goto end;
1104 i->nItem = i->range.upper;
1105 if (i->nItem > 0) goto testitem;
1107 return (i->nItem = i->nSpecial) != -1;
1110 static RANGE iterator_range(ITERATOR* i)
1114 if (!i->ranges) return i->range;
1116 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1117 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1122 * Releases resources associated with this ierator.
1124 static inline void iterator_destroy(ITERATOR* i)
1126 ranges_destroy(i->ranges);
1130 * Create an empty iterator.
1132 static inline BOOL iterator_empty(ITERATOR* i)
1134 ZeroMemory(i, sizeof(*i));
1135 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1140 * Create an iterator over a range.
1142 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1150 * Create an iterator over a bunch of ranges.
1151 * Please note that the iterator will take ownership of the ranges,
1152 * and will free them upon destruction.
1154 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1162 * Creates an iterator over the items which intersect lprc.
1164 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1166 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1167 RECT frame = *lprc, rcItem, rcTemp;
1170 /* in case we fail, we want to return an empty iterator */
1171 if (!iterator_empty(i)) return FALSE;
1173 LISTVIEW_GetOrigin(infoPtr, &Origin);
1175 TRACE("(lprc=%s)\n", debugrect(lprc));
1176 OffsetRect(&frame, -Origin.x, -Origin.y);
1178 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1182 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1184 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1185 if (IntersectRect(&rcTemp, &rcItem, lprc))
1186 i->nSpecial = infoPtr->nFocusedItem;
1188 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1189 /* to do better here, we need to have PosX, and PosY sorted */
1190 TRACE("building icon ranges:\n");
1191 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1193 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1194 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1195 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1196 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1197 if (IntersectRect(&rcTemp, &rcItem, &frame))
1198 ranges_additem(i->ranges, nItem);
1202 else if (uView == LVS_REPORT)
1206 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1207 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1209 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1210 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1211 if (range.upper <= range.lower) return TRUE;
1212 if (!iterator_rangeitems(i, range)) return FALSE;
1213 TRACE(" report=%s\n", debugrange(&i->range));
1217 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1218 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1219 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1220 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1221 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1222 INT lower = nFirstCol * nPerCol + nFirstRow;
1226 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1227 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1229 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1231 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1232 TRACE("building list ranges:\n");
1233 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1235 item_range.lower = nCol * nPerCol + nFirstRow;
1236 if(item_range.lower >= infoPtr->nItemCount) break;
1237 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1238 TRACE(" list=%s\n", debugrange(&item_range));
1239 ranges_add(i->ranges, item_range);
1247 * Creates an iterator over the items which intersect the visible region of hdc.
1249 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1251 POINT Origin, Position;
1252 RECT rcItem, rcClip;
1255 rgntype = GetClipBox(hdc, &rcClip);
1256 if (rgntype == NULLREGION) return iterator_empty(i);
1257 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1258 if (rgntype == SIMPLEREGION) return TRUE;
1260 /* first deal with the special item */
1261 if (i->nSpecial != -1)
1263 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1264 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1267 /* if we can't deal with the region, we'll just go with the simple range */
1268 LISTVIEW_GetOrigin(infoPtr, &Origin);
1269 TRACE("building visible range:\n");
1270 if (!i->ranges && i->range.lower < i->range.upper)
1272 if (!(i->ranges = ranges_create(50))) return TRUE;
1273 if (!ranges_add(i->ranges, i->range))
1275 ranges_destroy(i->ranges);
1281 /* now delete the invisible items from the list */
1282 while(iterator_next(i))
1284 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1285 rcItem.left = Position.x + Origin.x;
1286 rcItem.top = Position.y + Origin.y;
1287 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1288 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1289 if (!RectVisible(hdc, &rcItem))
1290 ranges_delitem(i->ranges, i->nItem);
1292 /* the iterator should restart on the next iterator_next */
1298 /******** Misc helper functions ************************************/
1300 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1301 WPARAM wParam, LPARAM lParam, BOOL isW)
1303 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1304 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1307 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1309 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1311 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1312 (uView == LVS_ICON || uView == LVS_SMALLICON);
1315 /******** Internal API functions ************************************/
1317 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1319 static COLUMN_INFO mainItem;
1321 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1322 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1323 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1326 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1328 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1331 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1333 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1336 /* Listview invalidation functions: use _only_ these functions to invalidate */
1338 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1340 return infoPtr->bRedraw;
1343 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1345 if(!is_redrawing(infoPtr)) return;
1346 TRACE(" invalidating rect=%s\n", debugrect(rect));
1347 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1350 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1354 if(!is_redrawing(infoPtr)) return;
1355 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1356 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1359 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1361 POINT Origin, Position;
1364 if(!is_redrawing(infoPtr)) return;
1365 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1366 LISTVIEW_GetOrigin(infoPtr, &Origin);
1367 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1368 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1370 rcBox.bottom = infoPtr->nItemHeight;
1371 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1372 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1375 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1377 LISTVIEW_InvalidateRect(infoPtr, NULL);
1380 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1384 if(!is_redrawing(infoPtr)) return;
1385 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1386 rcCol.top = infoPtr->rcList.top;
1387 rcCol.bottom = infoPtr->rcList.bottom;
1388 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1393 * Retrieves the number of items that can fit vertically in the client area.
1396 * [I] infoPtr : valid pointer to the listview structure
1399 * Number of items per row.
1401 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1403 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1405 return max(nListWidth/infoPtr->nItemWidth, 1);
1410 * Retrieves the number of items that can fit horizontally in the client
1414 * [I] infoPtr : valid pointer to the listview structure
1417 * Number of items per column.
1419 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1421 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1423 return max(nListHeight / infoPtr->nItemHeight, 1);
1427 /*************************************************************************
1428 * LISTVIEW_ProcessLetterKeys
1430 * Processes keyboard messages generated by pressing the letter keys
1432 * What this does is perform a case insensitive search from the
1433 * current position with the following quirks:
1434 * - If two chars or more are pressed in quick succession we search
1435 * for the corresponding string (e.g. 'abc').
1436 * - If there is a delay we wipe away the current search string and
1437 * restart with just that char.
1438 * - If the user keeps pressing the same character, whether slowly or
1439 * fast, so that the search string is entirely composed of this
1440 * character ('aaaaa' for instance), then we search for first item
1441 * that starting with that character.
1442 * - If the user types the above character in quick succession, then
1443 * we must also search for the corresponding string ('aaaaa'), and
1444 * go to that string if there is a match.
1447 * [I] hwnd : handle to the window
1448 * [I] charCode : the character code, the actual character
1449 * [I] keyData : key data
1457 * - The current implementation has a list of characters it will
1458 * accept and it ignores averything else. In particular it will
1459 * ignore accentuated characters which seems to match what
1460 * Windows does. But I'm not sure it makes sense to follow
1462 * - We don't sound a beep when the search fails.
1466 * TREEVIEW_ProcessLetterKeys
1468 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1473 WCHAR buffer[MAX_PATH];
1474 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1476 /* simple parameter checking */
1477 if (!charCode || !keyData) return 0;
1479 /* only allow the valid WM_CHARs through */
1480 if (!isalnum(charCode) &&
1481 charCode != '.' && charCode != '`' && charCode != '!' &&
1482 charCode != '@' && charCode != '#' && charCode != '$' &&
1483 charCode != '%' && charCode != '^' && charCode != '&' &&
1484 charCode != '*' && charCode != '(' && charCode != ')' &&
1485 charCode != '-' && charCode != '_' && charCode != '+' &&
1486 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1487 charCode != '}' && charCode != '[' && charCode != '{' &&
1488 charCode != '/' && charCode != '?' && charCode != '>' &&
1489 charCode != '<' && charCode != ',' && charCode != '~')
1492 /* if there's one item or less, there is no where to go */
1493 if (infoPtr->nItemCount <= 1) return 0;
1495 /* update the search parameters */
1496 infoPtr->lastKeyPressTimestamp = GetTickCount();
1497 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1498 if (infoPtr->nSearchParamLength < MAX_PATH)
1499 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1500 if (infoPtr->charCode != charCode)
1501 infoPtr->charCode = charCode = 0;
1503 infoPtr->charCode=charCode;
1504 infoPtr->szSearchParam[0]=charCode;
1505 infoPtr->nSearchParamLength=1;
1506 /* Redundant with the 1 char string */
1510 /* and search from the current position */
1512 if (infoPtr->nFocusedItem >= 0) {
1513 endidx=infoPtr->nFocusedItem;
1515 /* if looking for single character match,
1516 * then we must always move forward
1518 if (infoPtr->nSearchParamLength == 1)
1521 endidx=infoPtr->nItemCount;
1525 if (idx == infoPtr->nItemCount) {
1526 if (endidx == infoPtr->nItemCount || endidx == 0)
1532 item.mask = LVIF_TEXT;
1535 item.pszText = buffer;
1536 item.cchTextMax = MAX_PATH;
1537 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1539 /* check for a match */
1540 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1543 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1544 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1545 /* This would work but we must keep looking for a longer match */
1549 } while (idx != endidx);
1552 LISTVIEW_KeySelection(infoPtr, nItem);
1557 /*************************************************************************
1558 * LISTVIEW_UpdateHeaderSize [Internal]
1560 * Function to resize the header control
1563 * [I] hwnd : handle to a window
1564 * [I] nNewScrollPos : scroll pos to set
1569 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1574 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1576 GetWindowRect(infoPtr->hwndHeader, &winRect);
1577 point[0].x = winRect.left;
1578 point[0].y = winRect.top;
1579 point[1].x = winRect.right;
1580 point[1].y = winRect.bottom;
1582 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1583 point[0].x = -nNewScrollPos;
1584 point[1].x += nNewScrollPos;
1586 SetWindowPos(infoPtr->hwndHeader,0,
1587 point[0].x,point[0].y,point[1].x,point[1].y,
1588 SWP_NOZORDER | SWP_NOACTIVATE);
1593 * Update the scrollbars. This functions should be called whenever
1594 * the content, size or view changes.
1597 * [I] infoPtr : valid pointer to the listview structure
1602 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1604 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1605 SCROLLINFO horzInfo, vertInfo;
1607 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1609 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1610 horzInfo.cbSize = sizeof(SCROLLINFO);
1611 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1613 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1614 if (uView == LVS_LIST)
1616 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1617 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1619 /* scroll by at least one column per page */
1620 if(horzInfo.nPage < infoPtr->nItemWidth)
1621 horzInfo.nPage = infoPtr->nItemWidth;
1623 horzInfo.nPage /= infoPtr->nItemWidth;
1625 else if (uView == LVS_REPORT)
1627 horzInfo.nMax = infoPtr->nItemWidth;
1629 else /* LVS_ICON, or LVS_SMALLICON */
1633 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1636 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1637 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1638 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1639 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1641 /* Setting the horizontal scroll can change the listview size
1642 * (and potentially everything else) so we need to recompute
1643 * everything again for the vertical scroll
1646 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1647 vertInfo.cbSize = sizeof(SCROLLINFO);
1648 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1650 if (uView == LVS_REPORT)
1652 vertInfo.nMax = infoPtr->nItemCount;
1654 /* scroll by at least one page */
1655 if(vertInfo.nPage < infoPtr->nItemHeight)
1656 vertInfo.nPage = infoPtr->nItemHeight;
1658 vertInfo.nPage /= infoPtr->nItemHeight;
1660 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1664 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1667 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1668 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1669 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1670 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1672 /* Update the Header Control */
1673 if (uView == LVS_REPORT)
1675 horzInfo.fMask = SIF_POS;
1676 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1677 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1684 * Shows/hides the focus rectangle.
1687 * [I] infoPtr : valid pointer to the listview structure
1688 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1693 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1695 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1698 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1700 if (infoPtr->nFocusedItem < 0) return;
1702 /* we need some gymnastics in ICON mode to handle large items */
1703 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1707 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1708 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1710 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1715 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1717 /* for some reason, owner draw should work only in report mode */
1718 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1723 item.iItem = infoPtr->nFocusedItem;
1725 item.mask = LVIF_PARAM;
1726 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1728 ZeroMemory(&dis, sizeof(dis));
1729 dis.CtlType = ODT_LISTVIEW;
1730 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1731 dis.itemID = item.iItem;
1732 dis.itemAction = ODA_FOCUS;
1733 if (fShow) dis.itemState |= ODS_FOCUS;
1734 dis.hwndItem = infoPtr->hwndSelf;
1736 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1737 dis.itemData = item.lParam;
1739 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1743 DrawFocusRect(hdc, &infoPtr->rcFocus);
1746 ReleaseDC(infoPtr->hwndSelf, hdc);
1750 * Invalidates all visible selected items.
1752 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1756 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1757 while(iterator_next(&i))
1759 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1760 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1762 iterator_destroy(&i);
1767 * DESCRIPTION: [INTERNAL]
1768 * Computes an item's (left,top) corner, relative to rcView.
1769 * That is, the position has NOT been made relative to the Origin.
1770 * This is deliberate, to avoid computing the Origin over, and
1771 * over again, when this function is call in a loop. Instead,
1772 * one ca factor the computation of the Origin before the loop,
1773 * and offset the value retured by this function, on every iteration.
1776 * [I] infoPtr : valid pointer to the listview structure
1777 * [I] nItem : item number
1778 * [O] lpptOrig : item top, left corner
1783 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1785 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1787 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1789 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1791 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1792 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1794 else if (uView == LVS_LIST)
1796 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1797 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1798 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1800 else /* LVS_REPORT */
1802 lpptPosition->x = 0;
1803 lpptPosition->y = nItem * infoPtr->nItemHeight;
1808 * DESCRIPTION: [INTERNAL]
1809 * Compute the rectangles of an item. This is to localize all
1810 * the computations in one place. If you are not interested in some
1811 * of these values, simply pass in a NULL -- the fucntion is smart
1812 * enough to compute only what's necessary. The function computes
1813 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1814 * one, the BOX rectangle. This rectangle is very cheap to compute,
1815 * and is guaranteed to contain all the other rectangles. Computing
1816 * the ICON rect is also cheap, but all the others are potentaily
1817 * expensive. This gives an easy and effective optimization when
1818 * searching (like point inclusion, or rectangle intersection):
1819 * first test against the BOX, and if TRUE, test agains the desired
1821 * If the function does not have all the necessary information
1822 * to computed the requested rectangles, will crash with a
1823 * failed assertion. This is done so we catch all programming
1824 * errors, given that the function is called only from our code.
1826 * We have the following 'special' meanings for a few fields:
1827 * * If LVIS_FOCUSED is set, we assume the item has the focus
1828 * This is important in ICON mode, where it might get a larger
1829 * then usual rectange
1831 * Please note that subitem support works only in REPORT mode.
1834 * [I] infoPtr : valid pointer to the listview structure
1835 * [I] lpLVItem : item to compute the measures for
1836 * [O] lprcBox : ptr to Box rectangle
1837 * The internal LVIR_BOX rectangle
1838 * [0] lprcState : ptr to State icon rectangle
1839 * The internal LVIR_STATE rectangle
1840 * [O] lprcIcon : ptr to Icon rectangle
1841 * Same as LVM_GETITEMRECT with LVIR_ICON
1842 * [O] lprcLabel : ptr to Label rectangle
1843 * Same as LVM_GETITEMRECT with LVIR_LABEL
1848 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1849 LPRECT lprcBox, LPRECT lprcState,
1850 LPRECT lprcIcon, LPRECT lprcLabel)
1852 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1853 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1854 RECT Box, State, Icon, Label;
1855 COLUMN_INFO *lpColumnInfo = NULL;
1857 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1859 /* Be smart and try to figure out the minimum we have to do */
1860 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1861 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1863 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1864 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1866 if (lprcLabel) doLabel = TRUE;
1867 if (doLabel || lprcIcon) doIcon = TRUE;
1868 if (doIcon || lprcState) doState = TRUE;
1870 /************************************************************/
1871 /* compute the box rectangle (it should be cheap to do) */
1872 /************************************************************/
1873 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1874 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1876 if (lpLVItem->iSubItem)
1878 Box = lpColumnInfo->rcHeader;
1883 Box.right = infoPtr->nItemWidth;
1886 Box.bottom = infoPtr->nItemHeight;
1888 /************************************************************/
1889 /* compute STATEICON bounding box */
1890 /************************************************************/
1893 if (uView == LVS_ICON)
1895 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1896 if (infoPtr->himlNormal)
1897 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1898 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1902 /* we need the ident in report mode, if we don't have it, we fail */
1903 State.left = Box.left;
1904 if (uView == LVS_REPORT)
1906 if (lpLVItem->iSubItem == 0)
1908 State.left += REPORT_MARGINX;
1909 assert(lpLVItem->mask & LVIF_INDENT);
1910 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1913 State.top = Box.top;
1915 State.right = State.left;
1916 State.bottom = State.top;
1917 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1919 State.right += infoPtr->iconStateSize.cx;
1920 State.bottom += infoPtr->iconStateSize.cy;
1922 if (lprcState) *lprcState = State;
1923 TRACE(" - state=%s\n", debugrect(&State));
1926 /************************************************************/
1927 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1928 /************************************************************/
1931 if (uView == LVS_ICON)
1933 Icon.left = Box.left;
1934 if (infoPtr->himlNormal)
1935 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1936 Icon.top = Box.top + ICON_TOP_PADDING;
1937 Icon.right = Icon.left;
1938 Icon.bottom = Icon.top;
1939 if (infoPtr->himlNormal)
1941 Icon.right += infoPtr->iconSize.cx;
1942 Icon.bottom += infoPtr->iconSize.cy;
1945 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1947 Icon.left = State.right;
1949 Icon.right = Icon.left;
1950 if (infoPtr->himlSmall &&
1951 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1952 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1953 Icon.right += infoPtr->iconSize.cx;
1954 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1956 if(lprcIcon) *lprcIcon = Icon;
1957 TRACE(" - icon=%s\n", debugrect(&Icon));
1960 /************************************************************/
1961 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1962 /************************************************************/
1965 SIZE labelSize = { 0, 0 };
1967 /* calculate how far to the right can the label strech */
1968 Label.right = Box.right;
1969 if (uView == LVS_REPORT)
1971 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1974 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1976 labelSize.cx = infoPtr->nItemWidth;
1977 labelSize.cy = infoPtr->nItemHeight;
1981 /* we need the text in non owner draw mode */
1982 assert(lpLVItem->mask & LVIF_TEXT);
1983 if (is_textT(lpLVItem->pszText, TRUE))
1985 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1986 HDC hdc = GetDC(infoPtr->hwndSelf);
1987 HFONT hOldFont = SelectObject(hdc, hFont);
1991 /* compute rough rectangle where the label will go */
1992 SetRectEmpty(&rcText);
1993 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
1994 rcText.bottom = infoPtr->nItemHeight;
1995 if (uView == LVS_ICON)
1996 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1998 /* now figure out the flags */
1999 if (uView == LVS_ICON)
2000 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2002 uFormat = LV_SL_DT_FLAGS;
2004 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2006 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2007 labelSize.cy = rcText.bottom - rcText.top;
2009 SelectObject(hdc, hOldFont);
2010 ReleaseDC(infoPtr->hwndSelf, hdc);
2014 if (uView == LVS_ICON)
2016 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2017 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2018 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2019 Label.right = Label.left + labelSize.cx;
2020 Label.bottom = Label.top + infoPtr->nItemHeight;
2021 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2023 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2024 labelSize.cy /= infoPtr->ntmHeight;
2025 labelSize.cy = max(labelSize.cy, 1);
2026 labelSize.cy *= infoPtr->ntmHeight;
2028 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2030 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2032 Label.left = Icon.right;
2033 Label.top = Box.top;
2034 Label.right = min(Label.left + labelSize.cx, Label.right);
2035 Label.bottom = Label.top + infoPtr->nItemHeight;
2038 if (lprcLabel) *lprcLabel = Label;
2039 TRACE(" - label=%s\n", debugrect(&Label));
2042 /* Fix the Box if necessary */
2045 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2046 else *lprcBox = Box;
2048 TRACE(" - box=%s\n", debugrect(&Box));
2052 * DESCRIPTION: [INTERNAL]
2055 * [I] infoPtr : valid pointer to the listview structure
2056 * [I] nItem : item number
2057 * [O] lprcBox : ptr to Box rectangle
2062 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2064 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2065 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2066 POINT Position, Origin;
2069 LISTVIEW_GetOrigin(infoPtr, &Origin);
2070 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2072 /* Be smart and try to figure out the minimum we have to do */
2074 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2075 lvItem.mask |= LVIF_TEXT;
2076 lvItem.iItem = nItem;
2077 lvItem.iSubItem = 0;
2078 lvItem.pszText = szDispText;
2079 lvItem.cchTextMax = DISP_TEXT_SIZE;
2080 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2081 if (uView == LVS_ICON)
2083 lvItem.mask |= LVIF_STATE;
2084 lvItem.stateMask = LVIS_FOCUSED;
2085 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2087 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2089 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2095 * Returns the current icon position, and advances it along the top.
2096 * The returned position is not offset by Origin.
2099 * [I] infoPtr : valid pointer to the listview structure
2100 * [O] lpPos : will get the current icon position
2105 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2107 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2109 *lpPos = infoPtr->currIconPos;
2111 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2112 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2114 infoPtr->currIconPos.x = 0;
2115 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2121 * Returns the current icon position, and advances it down the left edge.
2122 * The returned position is not offset by Origin.
2125 * [I] infoPtr : valid pointer to the listview structure
2126 * [O] lpPos : will get the current icon position
2131 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2133 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2135 *lpPos = infoPtr->currIconPos;
2137 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2138 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2140 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2141 infoPtr->currIconPos.y = 0;
2147 * Moves an icon to the specified position.
2148 * It takes care of invalidating the item, etc.
2151 * [I] infoPtr : valid pointer to the listview structure
2152 * [I] nItem : the item to move
2153 * [I] lpPos : the new icon position
2154 * [I] isNew : flags the item as being new
2160 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2166 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2167 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2169 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2170 LISTVIEW_InvalidateItem(infoPtr, nItem);
2173 /* Allocating a POINTER for every item is too resource intensive,
2174 * so we'll keep the (x,y) in different arrays */
2175 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2176 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2178 LISTVIEW_InvalidateItem(infoPtr, nItem);
2185 * Arranges listview items in icon display mode.
2188 * [I] infoPtr : valid pointer to the listview structure
2189 * [I] nAlignCode : alignment code
2195 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2197 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2198 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2202 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2204 TRACE("nAlignCode=%d\n", nAlignCode);
2206 if (nAlignCode == LVA_DEFAULT)
2208 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2209 else nAlignCode = LVA_ALIGNTOP;
2214 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2215 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2216 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2217 default: return FALSE;
2220 infoPtr->bAutoarrange = TRUE;
2221 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2222 for (i = 0; i < infoPtr->nItemCount; i++)
2224 next_pos(infoPtr, &pos);
2225 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2233 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2236 * [I] infoPtr : valid pointer to the listview structure
2237 * [O] lprcView : bounding rectangle
2243 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2247 SetRectEmpty(lprcView);
2249 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2253 for (i = 0; i < infoPtr->nItemCount; i++)
2255 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2256 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2257 lprcView->right = max(lprcView->right, x);
2258 lprcView->bottom = max(lprcView->bottom, y);
2260 if (infoPtr->nItemCount > 0)
2262 lprcView->right += infoPtr->nItemWidth;
2263 lprcView->bottom += infoPtr->nItemHeight;
2268 y = LISTVIEW_GetCountPerColumn(infoPtr);
2269 x = infoPtr->nItemCount / y;
2270 if (infoPtr->nItemCount % y) x++;
2271 lprcView->right = x * infoPtr->nItemWidth;
2272 lprcView->bottom = y * infoPtr->nItemHeight;
2276 lprcView->right = infoPtr->nItemWidth;
2277 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2284 * Retrieves the bounding rectangle of all the items.
2287 * [I] infoPtr : valid pointer to the listview structure
2288 * [O] lprcView : bounding rectangle
2294 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2298 TRACE("(lprcView=%p)\n", lprcView);
2300 if (!lprcView) return FALSE;
2302 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2303 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2304 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2306 TRACE("lprcView=%s\n", debugrect(lprcView));
2313 * Retrieves the subitem pointer associated with the subitem index.
2316 * [I] hdpaSubItems : DPA handle for a specific item
2317 * [I] nSubItem : index of subitem
2320 * SUCCESS : subitem pointer
2323 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2325 SUBITEM_INFO *lpSubItem;
2328 /* we should binary search here if need be */
2329 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2331 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2332 if (lpSubItem->iSubItem == nSubItem)
2342 * Caclulates the desired item width.
2345 * [I] infoPtr : valid pointer to the listview structure
2348 * The desired item width.
2350 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2352 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2355 TRACE("uView=%d\n", uView);
2357 if (uView == LVS_ICON)
2358 nItemWidth = infoPtr->iconSpacing.cx;
2359 else if (uView == LVS_REPORT)
2363 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2365 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2366 nItemWidth = rcHeader.right;
2369 else /* LVS_SMALLICON, or LVS_LIST */
2373 for (i = 0; i < infoPtr->nItemCount; i++)
2374 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2376 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2377 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2379 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2382 return max(nItemWidth, 1);
2387 * Caclulates the desired item height.
2390 * [I] infoPtr : valid pointer to the listview structure
2393 * The desired item height.
2395 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2397 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2400 TRACE("uView=%d\n", uView);
2402 if (uView == LVS_ICON)
2403 nItemHeight = infoPtr->iconSpacing.cy;
2406 nItemHeight = infoPtr->ntmHeight;
2407 if (infoPtr->himlState)
2408 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2409 if (infoPtr->himlSmall)
2410 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2411 if (infoPtr->himlState || infoPtr->himlSmall)
2412 nItemHeight += HEIGHT_PADDING;
2415 return max(nItemHeight, 1);
2420 * Updates the width, and height of an item.
2423 * [I] infoPtr : valid pointer to the listview structure
2428 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2430 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2431 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2437 * Retrieves and saves important text metrics info for the current
2441 * [I] infoPtr : valid pointer to the listview structure
2444 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2446 HDC hdc = GetDC(infoPtr->hwndSelf);
2447 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2448 HFONT hOldFont = SelectObject(hdc, hFont);
2451 if (GetTextMetricsW(hdc, &tm))
2453 infoPtr->ntmHeight = tm.tmHeight;
2454 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2456 SelectObject(hdc, hOldFont);
2457 ReleaseDC(infoPtr->hwndSelf, hdc);
2459 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2464 * A compare function for ranges
2467 * [I] range1 : pointer to range 1;
2468 * [I] range2 : pointer to range 2;
2472 * > 0 : if range 1 > range 2
2473 * < 0 : if range 2 > range 1
2474 * = 0 : if range intersects range 2
2476 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2480 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2482 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2487 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2493 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2495 #define ranges_check(ranges, desc) do { } while(0)
2498 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2503 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2505 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2506 ranges_dump(ranges);
2507 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2508 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2509 assert (prev->lower >= 0 && prev->lower < prev->upper);
2510 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2512 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2513 assert (prev->upper <= curr->lower);
2514 assert (curr->lower < curr->upper);
2517 TRACE("--- Done checking---\n");
2520 static RANGES ranges_create(int count)
2522 RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2523 if (!ranges) return NULL;
2524 ranges->hdpa = DPA_Create(count);
2525 if (ranges->hdpa) return ranges;
2530 static void ranges_clear(RANGES ranges)
2534 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2535 Free(DPA_GetPtr(ranges->hdpa, i));
2536 DPA_DeleteAllPtrs(ranges->hdpa);
2540 static void ranges_destroy(RANGES ranges)
2542 if (!ranges) return;
2543 ranges_clear(ranges);
2544 DPA_Destroy(ranges->hdpa);
2548 static RANGES ranges_clone(RANGES ranges)
2553 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2555 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2557 RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2558 if (!newrng) goto fail;
2559 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2560 DPA_SetPtr(clone->hdpa, i, newrng);
2565 TRACE ("clone failed\n");
2566 ranges_destroy(clone);
2570 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2574 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2575 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2580 static void ranges_dump(RANGES ranges)
2584 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2585 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2588 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2590 RANGE srchrng = { nItem, nItem + 1 };
2592 TRACE("(nItem=%d)\n", nItem);
2593 ranges_check(ranges, "before contain");
2594 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2597 static INT ranges_itemcount(RANGES ranges)
2601 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2603 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2604 count += sel->upper - sel->lower;
2610 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2612 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2615 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2616 if (index == -1) return TRUE;
2618 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2620 chkrng = DPA_GetPtr(ranges->hdpa, index);
2621 if (chkrng->lower >= nItem)
2622 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2623 if (chkrng->upper > nItem)
2624 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2629 static BOOL ranges_add(RANGES ranges, RANGE range)
2634 TRACE("(%s)\n", debugrange(&range));
2635 ranges_check(ranges, "before add");
2637 /* try find overlapping regions first */
2638 srchrgn.lower = range.lower - 1;
2639 srchrgn.upper = range.upper + 1;
2640 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2646 TRACE("Adding new range\n");
2648 /* create the brand new range to insert */
2649 newrgn = (RANGE *)Alloc(sizeof(RANGE));
2650 if(!newrgn) goto fail;
2653 /* figure out where to insert it */
2654 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2655 TRACE("index=%d\n", index);
2656 if (index == -1) index = 0;
2658 /* and get it over with */
2659 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2667 RANGE *chkrgn, *mrgrgn;
2668 INT fromindex, mergeindex;
2670 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2671 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2673 chkrgn->lower = min(range.lower, chkrgn->lower);
2674 chkrgn->upper = max(range.upper, chkrgn->upper);
2676 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2678 /* merge now common anges */
2680 srchrgn.lower = chkrgn->lower - 1;
2681 srchrgn.upper = chkrgn->upper + 1;
2685 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2686 if (mergeindex == -1) break;
2687 if (mergeindex == index)
2689 fromindex = index + 1;
2693 TRACE("Merge with index %i\n", mergeindex);
2695 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2696 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2697 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2699 DPA_DeletePtr(ranges->hdpa, mergeindex);
2700 if (mergeindex < index) index --;
2704 ranges_check(ranges, "after add");
2708 ranges_check(ranges, "failed add");
2712 static BOOL ranges_del(RANGES ranges, RANGE range)
2717 TRACE("(%s)\n", debugrange(&range));
2718 ranges_check(ranges, "before del");
2720 /* we don't use DPAS_SORTED here, since we need *
2721 * to find the first overlapping range */
2722 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2725 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2727 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2729 /* case 1: Same range */
2730 if ( (chkrgn->upper == range.upper) &&
2731 (chkrgn->lower == range.lower) )
2733 DPA_DeletePtr(ranges->hdpa, index);
2736 /* case 2: engulf */
2737 else if ( (chkrgn->upper <= range.upper) &&
2738 (chkrgn->lower >= range.lower) )
2740 DPA_DeletePtr(ranges->hdpa, index);
2742 /* case 3: overlap upper */
2743 else if ( (chkrgn->upper <= range.upper) &&
2744 (chkrgn->lower < range.lower) )
2746 chkrgn->upper = range.lower;
2748 /* case 4: overlap lower */
2749 else if ( (chkrgn->upper > range.upper) &&
2750 (chkrgn->lower >= range.lower) )
2752 chkrgn->lower = range.upper;
2755 /* case 5: fully internal */
2758 RANGE tmprgn = *chkrgn, *newrgn;
2760 if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2761 newrgn->lower = chkrgn->lower;
2762 newrgn->upper = range.lower;
2763 chkrgn->lower = range.upper;
2764 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2773 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2776 ranges_check(ranges, "after del");
2780 ranges_check(ranges, "failed del");
2786 * Removes all selection ranges
2789 * [I] infoPtr : valid pointer to the listview structure
2790 * [I] toSkip : item range to skip removing the selection
2796 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2805 lvItem.stateMask = LVIS_SELECTED;
2807 /* need to clone the DPA because callbacks can change it */
2808 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2809 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2810 while(iterator_next(&i))
2811 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2812 /* note that the iterator destructor will free the cloned range */
2813 iterator_destroy(&i);
2818 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2822 if (!(toSkip = ranges_create(1))) return FALSE;
2823 if (nItem != -1) ranges_additem(toSkip, nItem);
2824 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2825 ranges_destroy(toSkip);
2829 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2831 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2836 * Retrieves the number of items that are marked as selected.
2839 * [I] infoPtr : valid pointer to the listview structure
2842 * Number of items selected.
2844 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2846 INT nSelectedCount = 0;
2848 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2851 for (i = 0; i < infoPtr->nItemCount; i++)
2853 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2858 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2860 TRACE("nSelectedCount=%d\n", nSelectedCount);
2861 return nSelectedCount;
2866 * Manages the item focus.
2869 * [I] infoPtr : valid pointer to the listview structure
2870 * [I] nItem : item index
2873 * TRUE : focused item changed
2874 * FALSE : focused item has NOT changed
2876 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2878 INT oldFocus = infoPtr->nFocusedItem;
2881 if (nItem == infoPtr->nFocusedItem) return FALSE;
2883 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2884 lvItem.stateMask = LVIS_FOCUSED;
2885 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2887 return oldFocus != infoPtr->nFocusedItem;
2890 /* Helper function for LISTVIEW_ShiftIndices *only* */
2891 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2893 if (nShiftItem < nItem) return nShiftItem;
2895 if (nShiftItem > nItem) return nShiftItem + direction;
2897 if (direction > 0) return nShiftItem + direction;
2899 return min(nShiftItem, infoPtr->nItemCount - 1);
2904 * Updates the various indices after an item has been inserted or deleted.
2907 * [I] infoPtr : valid pointer to the listview structure
2908 * [I] nItem : item index
2909 * [I] direction : Direction of shift, +1 or -1.
2914 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2919 /* temporarily disable change notification while shifting items */
2920 bOldChange = infoPtr->bDoChangeNotify;
2921 infoPtr->bDoChangeNotify = FALSE;
2923 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2925 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2927 assert(abs(direction) == 1);
2929 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2931 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2932 if (nNewFocus != infoPtr->nFocusedItem)
2933 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2935 /* But we are not supposed to modify nHotItem! */
2937 infoPtr->bDoChangeNotify = bOldChange;
2943 * Adds a block of selections.
2946 * [I] infoPtr : valid pointer to the listview structure
2947 * [I] nItem : item index
2952 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2954 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2955 INT nLast = max(infoPtr->nSelectionMark, nItem);
2959 if (nFirst == -1) nFirst = nItem;
2961 item.state = LVIS_SELECTED;
2962 item.stateMask = LVIS_SELECTED;
2964 /* FIXME: this is not correct LVS_OWNERDATA
2965 * setting the item states individually will generate
2966 * a LVN_ITEMCHANGED notification for each one. Instead,
2967 * we have to send a LVN_ODSTATECHANGED notification.
2968 * See MSDN documentation for LVN_ITEMCHANGED.
2970 for (i = nFirst; i <= nLast; i++)
2971 LISTVIEW_SetItemState(infoPtr,i,&item);
2977 * Sets a single group selection.
2980 * [I] infoPtr : valid pointer to the listview structure
2981 * [I] nItem : item index
2986 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2988 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2993 if (!(selection = ranges_create(100))) return;
2995 item.state = LVIS_SELECTED;
2996 item.stateMask = LVIS_SELECTED;
2998 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3000 if (infoPtr->nSelectionMark == -1)
3002 infoPtr->nSelectionMark = nItem;
3003 ranges_additem(selection, nItem);
3009 sel.lower = min(infoPtr->nSelectionMark, nItem);
3010 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3011 ranges_add(selection, sel);
3016 RECT rcItem, rcSel, rcSelMark;
3019 rcItem.left = LVIR_BOUNDS;
3020 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3021 rcSelMark.left = LVIR_BOUNDS;
3022 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3023 UnionRect(&rcSel, &rcItem, &rcSelMark);
3024 iterator_frameditems(&i, infoPtr, &rcSel);
3025 while(iterator_next(&i))
3027 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3028 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3030 iterator_destroy(&i);
3033 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3034 iterator_rangesitems(&i, selection);
3035 while(iterator_next(&i))
3036 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3037 /* this will also destroy the selection */
3038 iterator_destroy(&i);
3040 LISTVIEW_SetItemFocus(infoPtr, nItem);
3045 * Sets a single selection.
3048 * [I] infoPtr : valid pointer to the listview structure
3049 * [I] nItem : item index
3054 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3058 TRACE("nItem=%d\n", nItem);
3060 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3062 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3063 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3064 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3066 infoPtr->nSelectionMark = nItem;
3071 * Set selection(s) with keyboard.
3074 * [I] infoPtr : valid pointer to the listview structure
3075 * [I] nItem : item index
3078 * SUCCESS : TRUE (needs to be repainted)
3079 * FAILURE : FALSE (nothing has changed)
3081 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3083 /* FIXME: pass in the state */
3084 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3085 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3086 BOOL bResult = FALSE;
3088 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3090 if (infoPtr->dwStyle & LVS_SINGLESEL)
3093 LISTVIEW_SetSelection(infoPtr, nItem);
3100 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3104 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3109 LISTVIEW_SetSelection(infoPtr, nItem);
3112 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3115 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3122 * Called when the mouse is being actively tracked and has hovered for a specified
3126 * [I] infoPtr : valid pointer to the listview structure
3127 * [I] fwKeys : key indicator
3128 * [I] pts : mouse position
3131 * 0 if the message was processed, non-zero if there was an error
3134 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3135 * over the item for a certain period of time.
3138 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
3140 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3141 /* FIXME: select the item!!! */
3142 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3149 * Called whenever WM_MOUSEMOVE is received.
3152 * [I] infoPtr : valid pointer to the listview structure
3153 * [I] fwKeys : key indicator
3154 * [I] pts : mouse position
3157 * 0 if the message is processed, non-zero if there was an error
3159 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
3161 TRACKMOUSEEVENT trackinfo;
3163 /* see if we are supposed to be tracking mouse hovering */
3164 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3165 /* fill in the trackinfo struct */
3166 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3167 trackinfo.dwFlags = TME_QUERY;
3168 trackinfo.hwndTrack = infoPtr->hwndSelf;
3169 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3171 /* see if we are already tracking this hwnd */
3172 _TrackMouseEvent(&trackinfo);
3174 if(!(trackinfo.dwFlags & TME_HOVER)) {
3175 trackinfo.dwFlags = TME_HOVER;
3177 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3178 _TrackMouseEvent(&trackinfo);
3187 * Tests wheather the item is assignable to a list with style lStyle
3189 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3191 if ( (lpLVItem->mask & LVIF_TEXT) &&
3192 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3193 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3201 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3204 * [I] infoPtr : valid pointer to the listview structure
3205 * [I] lpLVItem : valid pointer to new item atttributes
3206 * [I] isNew : the item being set is being inserted
3207 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3208 * [O] bChanged : will be set to TRUE if the item really changed
3214 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3216 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3224 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3226 if (lpLVItem->mask == 0) return TRUE;
3228 if (infoPtr->dwStyle & LVS_OWNERDATA)
3230 /* a virtual listview we stores only selection and focus */
3231 if (lpLVItem->mask & ~LVIF_STATE)
3237 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3238 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3242 /* we need to get the lParam and state of the item */
3243 item.iItem = lpLVItem->iItem;
3244 item.iSubItem = lpLVItem->iSubItem;
3245 item.mask = LVIF_STATE | LVIF_PARAM;
3246 item.stateMask = ~0;
3249 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3251 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3252 /* determine what fields will change */
3253 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3254 uChanged |= LVIF_STATE;
3256 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3257 uChanged |= LVIF_IMAGE;
3259 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3260 uChanged |= LVIF_PARAM;
3262 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3263 uChanged |= LVIF_INDENT;
3265 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3266 uChanged |= LVIF_TEXT;
3268 TRACE("uChanged=0x%x\n", uChanged);
3269 if (!uChanged) return TRUE;
3272 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3273 nmlv.iItem = lpLVItem->iItem;
3274 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3275 nmlv.uOldState = item.state;
3276 nmlv.uChanged = uChanged;
3277 nmlv.lParam = item.lParam;
3279 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3280 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3282 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3283 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3286 /* copy information */
3287 if (lpLVItem->mask & LVIF_TEXT)
3288 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3290 if (lpLVItem->mask & LVIF_IMAGE)
3291 lpItem->hdr.iImage = lpLVItem->iImage;
3293 if (lpLVItem->mask & LVIF_PARAM)
3294 lpItem->lParam = lpLVItem->lParam;
3296 if (lpLVItem->mask & LVIF_INDENT)
3297 lpItem->iIndent = lpLVItem->iIndent;
3299 if (uChanged & LVIF_STATE)
3301 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3303 lpItem->state &= ~lpLVItem->stateMask;
3304 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3306 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3308 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3309 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3311 else if (lpLVItem->stateMask & LVIS_SELECTED)
3312 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3314 /* if we are asked to change focus, and we manage it, do it */
3315 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3317 if (lpLVItem->state & LVIS_FOCUSED)
3319 LISTVIEW_SetItemFocus(infoPtr, -1);
3320 infoPtr->nFocusedItem = lpLVItem->iItem;
3321 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3323 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3324 infoPtr->nFocusedItem = -1;
3328 /* if we're inserting the item, we're done */
3329 if (isNew) return TRUE;
3331 /* send LVN_ITEMCHANGED notification */
3332 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3333 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3340 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3343 * [I] infoPtr : valid pointer to the listview structure
3344 * [I] lpLVItem : valid pointer to new subitem atttributes
3345 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3346 * [O] bChanged : will be set to TRUE if the item really changed
3352 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3355 SUBITEM_INFO *lpSubItem;
3357 /* we do not support subitems for virtual listviews */
3358 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3360 /* set subitem only if column is present */
3361 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3363 /* First do some sanity checks */
3364 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3365 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3367 /* get the subitem structure, and create it if not there */
3368 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3369 assert (hdpaSubItems);
3371 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3374 SUBITEM_INFO *tmpSubItem;
3377 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3378 if (!lpSubItem) return FALSE;
3379 /* we could binary search here, if need be...*/
3380 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3382 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3383 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3385 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3390 lpSubItem->iSubItem = lpLVItem->iSubItem;
3391 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3395 if (lpLVItem->mask & LVIF_IMAGE)
3396 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3398 lpSubItem->hdr.iImage = lpLVItem->iImage;
3402 if (lpLVItem->mask & LVIF_TEXT)
3403 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3405 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3414 * Sets item attributes.
3417 * [I] infoPtr : valid pointer to the listview structure
3418 * [I] lpLVItem : new item atttributes
3419 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3425 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3427 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3428 LPWSTR pszText = NULL;
3429 BOOL bResult, bChanged = FALSE;
3431 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3433 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3436 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3437 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3439 pszText = lpLVItem->pszText;
3440 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3443 /* actually set the fields */
3444 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3446 if (lpLVItem->iSubItem)
3447 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3449 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3451 /* redraw item, if necessary */
3452 if (bChanged && !infoPtr->bIsDrawing)
3454 /* this little optimization eliminates some nasty flicker */
3455 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3456 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3457 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3459 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3464 textfreeT(lpLVItem->pszText, isW);
3465 ((LVITEMW *)lpLVItem)->pszText = pszText;
3473 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3476 * [I] infoPtr : valid pointer to the listview structure
3481 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3483 LONG lStyle = infoPtr->dwStyle;
3484 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3486 SCROLLINFO scrollInfo;
3488 scrollInfo.cbSize = sizeof(SCROLLINFO);
3489 scrollInfo.fMask = SIF_POS;
3491 if (uView == LVS_LIST)
3493 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3494 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3496 else if (uView == LVS_REPORT)
3498 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3499 nItem = scrollInfo.nPos;
3503 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3504 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3507 TRACE("nItem=%d\n", nItem);
3515 * Erases the background of the given rectangle
3518 * [I] infoPtr : valid pointer to the listview structure
3519 * [I] hdc : device context handle
3520 * [I] lprcBox : clipping rectangle
3526 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3528 if (!infoPtr->hBkBrush) return FALSE;
3530 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3532 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3540 * [I] infoPtr : valid pointer to the listview structure
3541 * [I] hdc : device context handle
3542 * [I] nItem : item index
3543 * [I] nSubItem : subitem index
3544 * [I] pos : item position in client coordinates
3545 * [I] cdmode : custom draw mode
3551 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3553 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3554 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3555 WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3556 DWORD cdsubitemmode = CDRF_DODEFAULT;
3557 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3558 NMLVCUSTOMDRAW nmlvcd;
3562 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3564 /* get information needed for drawing the item */
3565 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3566 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3567 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3568 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3569 lvItem.iItem = nItem;
3570 lvItem.iSubItem = nSubItem;
3573 lvItem.cchTextMax = DISP_TEXT_SIZE;
3574 lvItem.pszText = szDispText;
3575 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3576 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3577 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3578 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3579 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3581 /* now check if we need to update the focus rectangle */
3582 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3584 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3585 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3586 OffsetRect(&rcBox, pos.x, pos.y);
3587 OffsetRect(&rcState, pos.x, pos.y);
3588 OffsetRect(&rcIcon, pos.x, pos.y);
3589 OffsetRect(&rcLabel, pos.x, pos.y);
3590 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3591 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3593 /* fill in the custom draw structure */
3594 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3596 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3597 if (cdmode & CDRF_NOTIFYITEMDRAW)
3598 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3599 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3600 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3601 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3602 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3604 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3605 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3607 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3608 prepaint_setup(infoPtr, hdc, &nmlvcd);
3610 /* in full row select, subitems, will just use main item's colors */
3611 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3612 nmlvcd.clrTextBk = CLR_NONE;
3615 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3617 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3620 TRACE("uStateImage=%d\n", uStateImage);
3621 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3626 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3627 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3629 TRACE("iImage=%d\n", lvItem.iImage);
3630 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3631 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3634 /* Don't bother painting item being edited */
3635 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3637 /* draw the selection background, if we're drawing the main item */
3641 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3642 rcSelect.right = rcBox.right;
3644 if (nmlvcd.clrTextBk != CLR_NONE)
3645 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3646 if(lprcFocus) *lprcFocus = rcSelect;
3649 /* figure out the text drawing flags */
3650 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3651 if (uView == LVS_ICON)
3652 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3655 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3657 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3658 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3659 default: uFormat |= DT_LEFT;
3662 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3664 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3665 else rcLabel.left += LABEL_HOR_PADDING;
3667 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3668 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3671 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3672 notify_postpaint(infoPtr, &nmlvcd);
3678 * Draws listview items when in owner draw mode.
3681 * [I] infoPtr : valid pointer to the listview structure
3682 * [I] hdc : device context handle
3687 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3689 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3690 DWORD cditemmode = CDRF_DODEFAULT;
3691 NMLVCUSTOMDRAW nmlvcd;
3692 POINT Origin, Position;
3698 ZeroMemory(&dis, sizeof(dis));
3700 /* Get scroll info once before loop */
3701 LISTVIEW_GetOrigin(infoPtr, &Origin);
3703 /* iterate through the invalidated rows */
3704 while(iterator_next(i))
3706 item.iItem = i->nItem;
3708 item.mask = LVIF_PARAM | LVIF_STATE;
3709 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3710 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3712 dis.CtlType = ODT_LISTVIEW;
3714 dis.itemID = item.iItem;
3715 dis.itemAction = ODA_DRAWENTIRE;
3717 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3718 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3719 dis.hwndItem = infoPtr->hwndSelf;
3721 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3722 dis.rcItem.left = Position.x + Origin.x;
3723 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3724 dis.rcItem.top = Position.y + Origin.y;
3725 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3726 dis.itemData = item.lParam;
3728 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3731 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3732 * structure for the rest. of the paint cycle
3734 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3735 if (cdmode & CDRF_NOTIFYITEMDRAW)
3736 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3738 if (!(cditemmode & CDRF_SKIPDEFAULT))
3740 prepaint_setup (infoPtr, hdc, &nmlvcd);
3741 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3744 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3745 notify_postpaint(infoPtr, &nmlvcd);
3751 * Draws listview items when in report display mode.
3754 * [I] infoPtr : valid pointer to the listview structure
3755 * [I] hdc : device context handle
3756 * [I] cdmode : custom draw mode
3761 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3764 RECT rcClip, rcItem;
3765 POINT Origin, Position;
3771 /* figure out what to draw */
3772 rgntype = GetClipBox(hdc, &rcClip);
3773 if (rgntype == NULLREGION) return;
3775 /* Get scroll info once before loop */
3776 LISTVIEW_GetOrigin(infoPtr, &Origin);
3778 /* narrow down the columns we need to paint */
3779 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3781 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3782 if (rcItem.right + Origin.x >= rcClip.left) break;
3784 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3786 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3787 if (rcItem.left + Origin.x < rcClip.right) break;
3789 iterator_rangeitems(&j, colRange);
3791 /* in full row select, we _have_ to draw the main item */
3792 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3795 /* iterate through the invalidated rows */
3796 while(iterator_next(i))
3798 /* iterate through the invalidated columns */
3799 while(iterator_next(&j))
3801 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3802 Position.x += Origin.x;
3803 Position.y += Origin.y;
3805 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3807 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3809 rcItem.bottom = infoPtr->nItemHeight;
3810 OffsetRect(&rcItem, Position.x, Position.y);
3811 if (!RectVisible(hdc, &rcItem)) continue;
3814 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3817 iterator_destroy(&j);
3822 * Draws listview items when in list display mode.
3825 * [I] infoPtr : valid pointer to the listview structure
3826 * [I] hdc : device context handle
3827 * [I] cdmode : custom draw mode
3832 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3834 POINT Origin, Position;
3836 /* Get scroll info once before loop */
3837 LISTVIEW_GetOrigin(infoPtr, &Origin);
3839 while(iterator_prev(i))
3841 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3842 Position.x += Origin.x;
3843 Position.y += Origin.y;
3845 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3852 * Draws listview items.
3855 * [I] infoPtr : valid pointer to the listview structure
3856 * [I] hdc : device context handle
3861 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3863 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3864 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3865 NMLVCUSTOMDRAW nmlvcd;
3872 LISTVIEW_DUMP(infoPtr);
3874 infoPtr->bIsDrawing = TRUE;
3876 /* save dc values we're gonna trash while drawing */
3877 hOldFont = SelectObject(hdc, infoPtr->hFont);
3878 oldBkMode = GetBkMode(hdc);
3879 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3880 oldTextColor = GetTextColor(hdc);
3882 oldClrTextBk = infoPtr->clrTextBk;
3883 oldClrText = infoPtr->clrText;
3885 infoPtr->cditemmode = CDRF_DODEFAULT;
3887 GetClientRect(infoPtr->hwndSelf, &rcClient);
3888 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3889 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3890 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3891 prepaint_setup(infoPtr, hdc, &nmlvcd);
3893 /* Use these colors to draw the items */
3894 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3895 infoPtr->clrText = nmlvcd.clrText;
3897 /* nothing to draw */
3898 if(infoPtr->nItemCount == 0) goto enddraw;
3900 /* figure out what we need to draw */
3901 iterator_visibleitems(&i, infoPtr, hdc);
3903 /* send cache hint notification */
3904 if (infoPtr->dwStyle & LVS_OWNERDATA)
3906 RANGE range = iterator_range(&i);
3909 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3910 nmlv.iFrom = range.lower;
3911 nmlv.iTo = range.upper - 1;
3912 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3915 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3916 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3919 if (uView == LVS_REPORT)
3920 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3921 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3922 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3924 /* if we have a focus rect, draw it */
3925 if (infoPtr->bFocus)
3926 DrawFocusRect(hdc, &infoPtr->rcFocus);
3928 iterator_destroy(&i);
3931 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3932 notify_postpaint(infoPtr, &nmlvcd);
3934 infoPtr->clrTextBk = oldClrTextBk;
3935 infoPtr->clrText = oldClrText;
3937 SelectObject(hdc, hOldFont);
3938 SetBkMode(hdc, oldBkMode);
3939 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3940 SetTextColor(hdc, oldTextColor);
3941 infoPtr->bIsDrawing = FALSE;
3947 * Calculates the approximate width and height of a given number of items.
3950 * [I] infoPtr : valid pointer to the listview structure
3951 * [I] nItemCount : number of items
3952 * [I] wWidth : width
3953 * [I] wHeight : height
3956 * Returns a DWORD. The width in the low word and the height in high word.
3958 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3959 WORD wWidth, WORD wHeight)
3961 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3962 INT nItemCountPerColumn = 1;
3963 INT nColumnCount = 0;
3964 DWORD dwViewRect = 0;
3966 if (nItemCount == -1)
3967 nItemCount = infoPtr->nItemCount;
3969 if (uView == LVS_LIST)
3971 if (wHeight == 0xFFFF)
3973 /* use current height */
3974 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3977 if (wHeight < infoPtr->nItemHeight)
3978 wHeight = infoPtr->nItemHeight;
3982 if (infoPtr->nItemHeight > 0)
3984 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3985 if (nItemCountPerColumn == 0)
3986 nItemCountPerColumn = 1;
3988 if (nItemCount % nItemCountPerColumn != 0)
3989 nColumnCount = nItemCount / nItemCountPerColumn;
3991 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3995 /* Microsoft padding magic */
3996 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3997 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3999 dwViewRect = MAKELONG(wWidth, wHeight);
4001 else if (uView == LVS_REPORT)
4005 if (infoPtr->nItemCount > 0)
4007 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4008 wWidth = rcBox.right - rcBox.left;
4009 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4013 /* use current height and width */
4014 if (wHeight == 0xffff)
4015 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4016 if (wWidth == 0xffff)
4017 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4020 dwViewRect = MAKELONG(wWidth, wHeight);
4022 else if (uView == LVS_SMALLICON)
4023 FIXME("uView == LVS_SMALLICON: not implemented\n");
4024 else if (uView == LVS_ICON)
4025 FIXME("uView == LVS_ICON: not implemented\n");
4033 * Create a drag image list for the specified item.
4036 * [I] infoPtr : valid pointer to the listview structure
4037 * [I] iItem : index of item
4038 * [O] lppt : Upperr-left corner of the image
4041 * Returns a handle to the image list if successful, NULL otherwise.
4043 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4049 HBITMAP hbmp, hOldbmp;
4050 HIMAGELIST dragList = 0;
4051 TRACE("iItem=%d Count=%d \n", iItem, infoPtr->nItemCount);
4053 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4056 rcItem.left = LVIR_BOUNDS;
4057 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4060 lppt->x = rcItem.left;
4061 lppt->y = rcItem.top;
4063 size.cx = rcItem.right - rcItem.left;
4064 size.cy = rcItem.bottom - rcItem.top;
4066 hdcOrig = GetDC(infoPtr->hwndSelf);
4067 hdc = CreateCompatibleDC(hdcOrig);
4068 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4069 hOldbmp = SelectObject(hdc, hbmp);
4071 rcItem.left = rcItem.top = 0;
4072 rcItem.right = size.cx;
4073 rcItem.bottom = size.cy;
4074 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4077 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4079 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4080 SelectObject(hdc, hOldbmp);
4081 ImageList_Add(dragList, hbmp, 0);
4084 SelectObject(hdc, hOldbmp);
4088 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4090 TRACE("ret=%p\n", dragList);
4098 * Removes all listview items and subitems.
4101 * [I] infoPtr : valid pointer to the listview structure
4107 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4110 HDPA hdpaSubItems = NULL;
4117 /* we do it directly, to avoid notifications */
4118 ranges_clear(infoPtr->selectionRanges);
4119 infoPtr->nSelectionMark = -1;
4120 infoPtr->nFocusedItem = -1;
4121 SetRectEmpty(&infoPtr->rcFocus);
4122 /* But we are supposed to leave nHotItem as is! */
4125 /* send LVN_DELETEALLITEMS notification */
4126 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4128 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4130 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4132 /* send LVN_DELETEITEM notification, if not supressed */
4133 if (!bSuppress) notify_deleteitem(infoPtr, i);
4134 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4136 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4137 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4139 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4140 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4143 DPA_Destroy(hdpaSubItems);
4144 DPA_DeletePtr(infoPtr->hdpaItems, i);
4146 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4147 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4148 infoPtr->nItemCount --;
4151 LISTVIEW_UpdateScroll(infoPtr);
4153 LISTVIEW_InvalidateList(infoPtr);
4160 * Scrolls, and updates the columns, when a column is changing width.
4163 * [I] infoPtr : valid pointer to the listview structure
4164 * [I] nColumn : column to scroll
4165 * [I] dx : amount of scroll, in pixels
4170 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4172 COLUMN_INFO *lpColumnInfo;
4176 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4177 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4178 rcCol = lpColumnInfo->rcHeader;
4179 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4180 rcCol.left = rcCol.right;
4182 /* ajust the other columns */
4183 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4185 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4186 lpColumnInfo->rcHeader.left += dx;
4187 lpColumnInfo->rcHeader.right += dx;
4190 /* do not update screen if not in report mode */
4191 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4193 /* if we have a focus, must first erase the focus rect */
4194 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4196 /* Need to reset the item width when inserting a new column */
4197 infoPtr->nItemWidth += dx;
4199 LISTVIEW_UpdateScroll(infoPtr);
4201 /* scroll to cover the deleted column, and invalidate for redraw */
4202 rcOld = infoPtr->rcList;
4203 rcOld.left = rcCol.left;
4204 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4206 /* we can restore focus now */
4207 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4212 * Removes a column from the listview control.
4215 * [I] infoPtr : valid pointer to the listview structure
4216 * [I] nColumn : column index
4222 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4226 TRACE("nColumn=%d\n", nColumn);
4228 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4229 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4231 /* While the MSDN specifically says that column zero should not be deleted,
4232 it does in fact work on WinNT, and at least one app depends on it. On
4233 WinNT, deleting column zero deletes the last column of items but the
4234 first header. Since no app will ever depend on that bizarre behavior,
4235 we just delete the last column including the header.
4238 nColumn = DPA_GetPtrCount(infoPtr->hdpaColumns) - 1;
4240 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4242 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4245 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4246 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4248 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4250 SUBITEM_INFO *lpSubItem, *lpDelItem;
4252 INT nItem, nSubItem, i;
4255 return LISTVIEW_DeleteAllItems(infoPtr);
4257 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4259 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4262 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4264 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4265 if (lpSubItem->iSubItem == nColumn)
4268 lpDelItem = lpSubItem;
4270 else if (lpSubItem->iSubItem > nColumn)
4272 lpSubItem->iSubItem--;
4276 /* if we found our subitem, zapp it */
4280 if (is_textW(lpDelItem->hdr.pszText))
4281 Free(lpDelItem->hdr.pszText);
4286 /* free dpa memory */
4287 DPA_DeletePtr(hdpaSubItems, nSubItem);
4292 /* update the other column info */
4293 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4300 * Invalidates the listview after an item's insertion or deletion.
4303 * [I] infoPtr : valid pointer to the listview structure
4304 * [I] nItem : item index
4305 * [I] dir : -1 if deleting, 1 if inserting
4310 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4312 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4313 INT nPerCol, nItemCol, nItemRow;
4317 /* if we don't refresh, what's the point of scrolling? */
4318 if (!is_redrawing(infoPtr)) return;
4320 assert (abs(dir) == 1);
4322 /* arrange icons if autoarrange is on */
4323 if (is_autoarrange(infoPtr))
4325 BOOL arrange = TRUE;
4326 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4327 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4328 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4331 /* scrollbars need updating */
4332 LISTVIEW_UpdateScroll(infoPtr);
4334 /* figure out the item's position */
4335 if (uView == LVS_REPORT)
4336 nPerCol = infoPtr->nItemCount + 1;
4337 else if (uView == LVS_LIST)
4338 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4339 else /* LVS_ICON, or LVS_SMALLICON */
4342 nItemCol = nItem / nPerCol;
4343 nItemRow = nItem % nPerCol;
4344 LISTVIEW_GetOrigin(infoPtr, &Origin);
4346 /* move the items below up a slot */
4347 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4348 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4349 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4350 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4351 OffsetRect(&rcScroll, Origin.x, Origin.y);
4352 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4353 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4355 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4356 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4357 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4360 /* report has only that column, so we're done */
4361 if (uView == LVS_REPORT) return;
4363 /* now for LISTs, we have to deal with the columns to the right */
4364 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4366 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4367 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4368 OffsetRect(&rcScroll, Origin.x, Origin.y);
4369 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4370 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4371 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4376 * Removes an item from the listview control.
4379 * [I] infoPtr : valid pointer to the listview structure
4380 * [I] nItem : item index
4386 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4388 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4391 TRACE("(nItem=%d)\n", nItem);
4393 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4395 /* remove selection, and focus */
4397 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4398 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4400 /* send LVN_DELETEITEM notification. */
4401 notify_deleteitem(infoPtr, nItem);
4403 /* we need to do this here, because we'll be deleting stuff */
4404 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4405 LISTVIEW_InvalidateItem(infoPtr, nItem);
4407 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4413 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4414 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4416 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4417 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4420 DPA_Destroy(hdpaSubItems);
4423 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4425 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4426 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4429 infoPtr->nItemCount--;
4430 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4432 /* now is the invalidation fun */
4433 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4440 * Callback implementation for editlabel control
4443 * [I] infoPtr : valid pointer to the listview structure
4444 * [I] pszText : modified text
4445 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4451 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4453 NMLVDISPINFOW dispInfo;
4455 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4457 ZeroMemory(&dispInfo, sizeof(dispInfo));
4458 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4459 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4460 dispInfo.item.iSubItem = 0;
4461 dispInfo.item.stateMask = ~0;
4462 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4463 /* add the text from the edit in */
4464 dispInfo.item.mask |= LVIF_TEXT;
4465 dispInfo.item.pszText = pszText;
4466 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4468 /* Do we need to update the Item Text */
4469 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4470 if (!pszText) return TRUE;
4472 ZeroMemory(&dispInfo, sizeof(dispInfo));
4473 dispInfo.item.mask = LVIF_TEXT;
4474 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4475 dispInfo.item.iSubItem = 0;
4476 dispInfo.item.pszText = pszText;
4477 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4478 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4483 * Begin in place editing of specified list view item
4486 * [I] infoPtr : valid pointer to the listview structure
4487 * [I] nItem : item index
4488 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4494 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4496 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4497 NMLVDISPINFOW dispInfo;
4500 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4502 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4503 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4505 infoPtr->nEditLabelItem = nItem;
4507 /* Is the EditBox still there, if so remove it */
4508 if(infoPtr->hwndEdit != 0)
4510 SetFocus(infoPtr->hwndSelf);
4511 infoPtr->hwndEdit = 0;
4514 LISTVIEW_SetSelection(infoPtr, nItem);
4515 LISTVIEW_SetItemFocus(infoPtr, nItem);
4516 LISTVIEW_InvalidateItem(infoPtr, nItem);
4518 rect.left = LVIR_LABEL;
4519 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4521 ZeroMemory(&dispInfo, sizeof(dispInfo));
4522 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4523 dispInfo.item.iItem = nItem;
4524 dispInfo.item.iSubItem = 0;
4525 dispInfo.item.stateMask = ~0;
4526 dispInfo.item.pszText = szDispText;
4527 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4528 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4530 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4531 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4532 if (!infoPtr->hwndEdit) return 0;
4534 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4536 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4537 infoPtr->hwndEdit = 0;
4541 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4542 SetFocus(infoPtr->hwndEdit);
4543 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4544 return infoPtr->hwndEdit;
4550 * Ensures the specified item is visible, scrolling into view if necessary.
4553 * [I] infoPtr : valid pointer to the listview structure
4554 * [I] nItem : item index
4555 * [I] bPartial : partially or entirely visible
4561 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4563 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4564 INT nScrollPosHeight = 0;
4565 INT nScrollPosWidth = 0;
4566 INT nHorzAdjust = 0;
4567 INT nVertAdjust = 0;
4570 RECT rcItem, rcTemp;
4572 rcItem.left = LVIR_BOUNDS;
4573 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4575 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4577 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4579 /* scroll left/right, but in LVS_REPORT mode */
4580 if (uView == LVS_LIST)
4581 nScrollPosWidth = infoPtr->nItemWidth;
4582 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4583 nScrollPosWidth = 1;
4585 if (rcItem.left < infoPtr->rcList.left)
4588 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4593 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4597 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4599 /* scroll up/down, but not in LVS_LIST mode */
4600 if (uView == LVS_REPORT)
4601 nScrollPosHeight = infoPtr->nItemHeight;
4602 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4603 nScrollPosHeight = 1;
4605 if (rcItem.top < infoPtr->rcList.top)
4608 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4613 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4617 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4619 if (nScrollPosWidth)
4621 INT diff = nHorzDiff / nScrollPosWidth;
4622 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4623 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4626 if (nScrollPosHeight)
4628 INT diff = nVertDiff / nScrollPosHeight;
4629 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4630 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4638 * Searches for an item with specific characteristics.
4641 * [I] hwnd : window handle
4642 * [I] nStart : base item index
4643 * [I] lpFindInfo : item information to look for
4646 * SUCCESS : index of item
4649 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4650 const LVFINDINFOW *lpFindInfo)
4652 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4653 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4654 BOOL bWrap = FALSE, bNearest = FALSE;
4655 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4656 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4657 POINT Position, Destination;
4660 if (!lpFindInfo || nItem < 0) return -1;
4663 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4665 lvItem.mask |= LVIF_TEXT;
4666 lvItem.pszText = szDispText;
4667 lvItem.cchTextMax = DISP_TEXT_SIZE;
4670 if (lpFindInfo->flags & LVFI_WRAP)
4673 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4674 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4679 LISTVIEW_GetOrigin(infoPtr, &Origin);
4680 Destination.x = lpFindInfo->pt.x - Origin.x;
4681 Destination.y = lpFindInfo->pt.y - Origin.y;
4682 switch(lpFindInfo->vkDirection)
4684 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4685 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4686 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4687 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4688 case VK_HOME: Destination.x = Destination.y = 0; break;
4689 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4690 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4692 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4693 Destination.x = rcArea.right;
4694 Destination.y = rcArea.bottom;
4696 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4701 /* if LVFI_PARAM is specified, all other flags are ignored */
4702 if (lpFindInfo->flags & LVFI_PARAM)
4704 lvItem.mask |= LVIF_PARAM;
4706 lvItem.mask &= ~LVIF_TEXT;
4710 for (; nItem < nLast; nItem++)
4712 lvItem.iItem = nItem;
4713 lvItem.iSubItem = 0;
4714 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4716 if (lvItem.mask & LVIF_PARAM)
4718 if (lpFindInfo->lParam == lvItem.lParam)
4724 if (lvItem.mask & LVIF_TEXT)
4726 if (lpFindInfo->flags & LVFI_PARTIAL)
4728 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4732 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4736 if (!bNearest) return nItem;
4738 /* This is very inefficient. To do a good job here,
4739 * we need a sorted array of (x,y) item positions */
4740 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4742 /* compute the distance^2 to the destination */
4743 xdist = Destination.x - Position.x;
4744 ydist = Destination.y - Position.y;
4745 dist = xdist * xdist + ydist * ydist;
4747 /* remember the distance, and item if it's closer */
4751 nNearestItem = nItem;
4758 nLast = min(nStart + 1, infoPtr->nItemCount);
4763 return nNearestItem;
4768 * Searches for an item with specific characteristics.
4771 * [I] hwnd : window handle
4772 * [I] nStart : base item index
4773 * [I] lpFindInfo : item information to look for
4776 * SUCCESS : index of item
4779 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4780 const LVFINDINFOA *lpFindInfo)
4782 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4786 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4787 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4788 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4789 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4795 * Retrieves the background image of the listview control.
4798 * [I] infoPtr : valid pointer to the listview structure
4799 * [O] lpBkImage : background image attributes
4805 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4807 /* FIXME (listview, "empty stub!\n"); */
4813 * Retrieves column attributes.
4816 * [I] infoPtr : valid pointer to the listview structure
4817 * [I] nColumn : column index
4818 * [IO] lpColumn : column information
4819 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4820 * otherwise it is in fact a LPLVCOLUMNA
4826 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4828 COLUMN_INFO *lpColumnInfo;
4831 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4832 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4834 /* initialize memory */
4835 ZeroMemory(&hdi, sizeof(hdi));
4837 if (lpColumn->mask & LVCF_TEXT)
4839 hdi.mask |= HDI_TEXT;
4840 hdi.pszText = lpColumn->pszText;
4841 hdi.cchTextMax = lpColumn->cchTextMax;
4844 if (lpColumn->mask & LVCF_IMAGE)
4845 hdi.mask |= HDI_IMAGE;
4847 if (lpColumn->mask & LVCF_ORDER)
4848 hdi.mask |= HDI_ORDER;
4850 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4852 if (lpColumn->mask & LVCF_FMT)
4853 lpColumn->fmt = lpColumnInfo->fmt;
4855 if (lpColumn->mask & LVCF_WIDTH)
4856 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4858 if (lpColumn->mask & LVCF_IMAGE)
4859 lpColumn->iImage = hdi.iImage;
4861 if (lpColumn->mask & LVCF_ORDER)
4862 lpColumn->iOrder = hdi.iOrder;
4868 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4875 /* FIXME: little hack */
4876 for (i = 0; i < iCount; i++)
4884 * Retrieves the column width.
4887 * [I] infoPtr : valid pointer to the listview structure
4888 * [I] int : column index
4891 * SUCCESS : column width
4894 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4896 INT nColumnWidth = 0;
4899 TRACE("nColumn=%d\n", nColumn);
4901 /* we have a 'column' in LIST and REPORT mode only */
4902 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4905 nColumnWidth = infoPtr->nItemWidth;
4908 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
4909 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4910 nColumnWidth = rcHeader.right - rcHeader.left;
4914 TRACE("nColumnWidth=%d\n", nColumnWidth);
4915 return nColumnWidth;
4920 * In list or report display mode, retrieves the number of items that can fit
4921 * vertically in the visible area. In icon or small icon display mode,
4922 * retrieves the total number of visible items.
4925 * [I] infoPtr : valid pointer to the listview structure
4928 * Number of fully visible items.
4930 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4932 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4936 return infoPtr->nItemCount;
4938 return LISTVIEW_GetCountPerColumn(infoPtr);
4940 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4948 * Retrieves an image list handle.
4951 * [I] infoPtr : valid pointer to the listview structure
4952 * [I] nImageList : image list identifier
4955 * SUCCESS : image list handle
4958 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4962 case LVSIL_NORMAL: return infoPtr->himlNormal;
4963 case LVSIL_SMALL: return infoPtr->himlSmall;
4964 case LVSIL_STATE: return infoPtr->himlState;
4969 /* LISTVIEW_GetISearchString */
4973 * Retrieves item attributes.
4976 * [I] hwnd : window handle
4977 * [IO] lpLVItem : item info
4978 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4979 * if FALSE, the lpLVItem is a LPLVITEMA.
4982 * This is the internal 'GetItem' interface -- it tries to
4983 * be smart, and avoids text copies, if possible, by modifing
4984 * lpLVItem->pszText to point to the text string. Please note
4985 * that this is not always possible (e.g. OWNERDATA), so on
4986 * entry you *must* supply valid values for pszText, and cchTextMax.
4987 * The only difference to the documented interface is that upon
4988 * return, you should use *only* the lpLVItem->pszText, rather than
4989 * the buffer pointer you provided on input. Most code already does
4990 * that, so it's not a problem.
4991 * For the two cases when the text must be copied (that is,
4992 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
4998 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5000 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5001 NMLVDISPINFOW dispInfo;
5007 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5009 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5012 if (lpLVItem->mask == 0) return TRUE;
5014 /* make a local copy */
5015 isubitem = lpLVItem->iSubItem;
5017 /* a quick optimization if all we're asked is the focus state
5018 * these queries are worth optimising since they are common,
5019 * and can be answered in constant time, without the heavy accesses */
5020 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5021 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5023 lpLVItem->state = 0;
5024 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5025 lpLVItem->state |= LVIS_FOCUSED;
5029 ZeroMemory(&dispInfo, sizeof(dispInfo));
5031 /* if the app stores all the data, handle it separately */
5032 if (infoPtr->dwStyle & LVS_OWNERDATA)
5034 dispInfo.item.state = 0;
5036 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5037 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5039 /* NOTE: copy only fields which we _know_ are initialized, some apps
5040 * depend on the uninitialized fields being 0 */
5041 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5042 dispInfo.item.iItem = lpLVItem->iItem;
5043 dispInfo.item.iSubItem = isubitem;
5044 if (lpLVItem->mask & LVIF_TEXT)
5046 dispInfo.item.pszText = lpLVItem->pszText;
5047 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5049 if (lpLVItem->mask & LVIF_STATE)
5050 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5051 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5052 dispInfo.item.stateMask = lpLVItem->stateMask;
5053 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5055 /* full size structure expected - _WIN32IE >= 0x560 */
5056 *lpLVItem = dispInfo.item;
5058 else if (lpLVItem->mask & LVIF_INDENT)
5060 /* indent member expected - _WIN32IE >= 0x300 */
5061 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5065 /* minimal structure expected */
5066 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5068 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5071 /* make sure lParam is zeroed out */
5072 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5074 /* we store only a little state, so if we're not asked, we're done */
5075 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5077 /* if focus is handled by us, report it */
5078 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5080 lpLVItem->state &= ~LVIS_FOCUSED;
5081 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5082 lpLVItem->state |= LVIS_FOCUSED;
5085 /* and do the same for selection, if we handle it */
5086 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5088 lpLVItem->state &= ~LVIS_SELECTED;
5089 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5090 lpLVItem->state |= LVIS_SELECTED;
5096 /* find the item and subitem structures before we proceed */
5097 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5098 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5103 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5104 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5107 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5112 pItemHdr = &lpItem->hdr;
5114 /* Do we need to query the state from the app? */
5115 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5117 dispInfo.item.mask |= LVIF_STATE;
5118 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5121 /* Do we need to enquire about the image? */
5122 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5123 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5125 dispInfo.item.mask |= LVIF_IMAGE;
5126 dispInfo.item.iImage = I_IMAGECALLBACK;
5129 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5130 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5132 dispInfo.item.mask |= LVIF_TEXT;
5133 dispInfo.item.pszText = lpLVItem->pszText;
5134 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5135 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5136 *dispInfo.item.pszText = '\0';
5139 /* If we don't have all the requested info, query the application */
5140 if (dispInfo.item.mask != 0)
5142 dispInfo.item.iItem = lpLVItem->iItem;
5143 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5144 dispInfo.item.lParam = lpItem->lParam;
5145 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5146 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5149 /* we should not store values for subitems */
5150 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5152 /* Now, handle the iImage field */
5153 if (dispInfo.item.mask & LVIF_IMAGE)
5155 lpLVItem->iImage = dispInfo.item.iImage;
5156 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5157 pItemHdr->iImage = dispInfo.item.iImage;
5159 else if (lpLVItem->mask & LVIF_IMAGE)
5161 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5162 lpLVItem->iImage = pItemHdr->iImage;
5164 lpLVItem->iImage = 0;
5167 /* The pszText field */
5168 if (dispInfo.item.mask & LVIF_TEXT)
5170 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5171 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5173 lpLVItem->pszText = dispInfo.item.pszText;
5175 else if (lpLVItem->mask & LVIF_TEXT)
5177 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5178 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5181 /* if this is a subitem, we're done */
5182 if (isubitem) return TRUE;
5184 /* Next is the lParam field */
5185 if (dispInfo.item.mask & LVIF_PARAM)
5187 lpLVItem->lParam = dispInfo.item.lParam;
5188 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5189 lpItem->lParam = dispInfo.item.lParam;
5191 else if (lpLVItem->mask & LVIF_PARAM)
5192 lpLVItem->lParam = lpItem->lParam;
5194 /* ... the state field (this one is different due to uCallbackmask) */
5195 if (lpLVItem->mask & LVIF_STATE)
5197 lpLVItem->state = lpItem->state;
5198 if (dispInfo.item.mask & LVIF_STATE)
5200 lpLVItem->state &= ~dispInfo.item.stateMask;
5201 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5203 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5205 lpLVItem->state &= ~LVIS_FOCUSED;
5206 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5207 lpLVItem->state |= LVIS_FOCUSED;
5209 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5211 lpLVItem->state &= ~LVIS_SELECTED;
5212 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5213 lpLVItem->state |= LVIS_SELECTED;
5217 /* and last, but not least, the indent field */
5218 if (lpLVItem->mask & LVIF_INDENT)
5219 lpLVItem->iIndent = lpItem->iIndent;
5226 * Retrieves item attributes.
5229 * [I] hwnd : window handle
5230 * [IO] lpLVItem : item info
5231 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5232 * if FALSE, the lpLVItem is a LPLVITEMA.
5235 * This is the external 'GetItem' interface -- it properly copies
5236 * the text in the provided buffer.
5242 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5247 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5250 pszText = lpLVItem->pszText;
5251 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5252 if (bResult && lpLVItem->pszText != pszText)
5253 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5254 lpLVItem->pszText = pszText;
5262 * Retrieves the position (upper-left) of the listview control item.
5263 * Note that for LVS_ICON style, the upper-left is that of the icon
5264 * and not the bounding box.
5267 * [I] infoPtr : valid pointer to the listview structure
5268 * [I] nItem : item index
5269 * [O] lpptPosition : coordinate information
5275 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5277 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5280 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5282 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5284 LISTVIEW_GetOrigin(infoPtr, &Origin);
5285 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5287 if (uView == LVS_ICON)
5289 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5290 lpptPosition->y += ICON_TOP_PADDING;
5292 lpptPosition->x += Origin.x;
5293 lpptPosition->y += Origin.y;
5295 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5302 * Retrieves the bounding rectangle for a listview control item.
5305 * [I] infoPtr : valid pointer to the listview structure
5306 * [I] nItem : item index
5307 * [IO] lprc : bounding rectangle coordinates
5308 * lprc->left specifies the portion of the item for which the bounding
5309 * rectangle will be retrieved.
5311 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5312 * including the icon and label.
5315 * * Experiment shows that native control returns:
5316 * * width = min (48, length of text line)
5317 * * .left = position.x - (width - iconsize.cx)/2
5318 * * .right = .left + width
5319 * * height = #lines of text * ntmHeight + icon height + 8
5320 * * .top = position.y - 2
5321 * * .bottom = .top + height
5322 * * separation between items .y = itemSpacing.cy - height
5323 * * .x = itemSpacing.cx - width
5324 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5327 * * Experiment shows that native control returns:
5328 * * width = iconSize.cx + 16
5329 * * .left = position.x - (width - iconsize.cx)/2
5330 * * .right = .left + width
5331 * * height = iconSize.cy + 4
5332 * * .top = position.y - 2
5333 * * .bottom = .top + height
5334 * * separation between items .y = itemSpacing.cy - height
5335 * * .x = itemSpacing.cx - width
5336 * LVIR_LABEL Returns the bounding rectangle of the item text.
5339 * * Experiment shows that native control returns:
5340 * * width = text length
5341 * * .left = position.x - width/2
5342 * * .right = .left + width
5343 * * height = ntmH * linecount + 2
5344 * * .top = position.y + iconSize.cy + 6
5345 * * .bottom = .top + height
5346 * * separation between items .y = itemSpacing.cy - height
5347 * * .x = itemSpacing.cx - width
5348 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5349 * rectangles, but excludes columns in report view.
5356 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5357 * upon whether the window has the focus currently and on whether the item
5358 * is the one with the focus. Ensure that the control's record of which
5359 * item has the focus agrees with the items' records.
5361 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5363 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5364 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5365 BOOL doLabel = TRUE, oversizedBox = FALSE;
5366 POINT Position, Origin;
5370 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5372 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5374 LISTVIEW_GetOrigin(infoPtr, &Origin);
5375 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5377 /* Be smart and try to figure out the minimum we have to do */
5378 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5379 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5380 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5381 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5382 oversizedBox = TRUE;
5384 /* get what we need from the item before hand, so we make
5385 * only one request. This can speed up things, if data
5386 * is stored on the app side */
5388 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5389 if (doLabel) lvItem.mask |= LVIF_TEXT;
5390 lvItem.iItem = nItem;
5391 lvItem.iSubItem = 0;
5392 lvItem.pszText = szDispText;
5393 lvItem.cchTextMax = DISP_TEXT_SIZE;
5394 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5395 /* we got the state already up, simulate it here, to avoid a reget */
5396 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5398 lvItem.mask |= LVIF_STATE;
5399 lvItem.stateMask = LVIS_FOCUSED;
5400 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5403 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5404 lprc->left = LVIR_BOUNDS;
5408 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5412 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5416 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5419 case LVIR_SELECTBOUNDS:
5420 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5421 UnionRect(lprc, lprc, &label_rect);
5425 WARN("Unknown value: %ld\n", lprc->left);
5429 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5431 TRACE(" rect=%s\n", debugrect(lprc));
5438 * Retrieves the spacing between listview control items.
5441 * [I] infoPtr : valid pointer to the listview structure
5442 * [IO] lprc : rectangle to receive the output
5443 * on input, lprc->top = nSubItem
5444 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5446 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5447 * not only those of the first column.
5448 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5454 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5459 if (!lprc) return FALSE;
5461 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5462 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5464 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5466 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5468 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5471 lvItem.iItem = nItem;
5472 lvItem.iSubItem = lprc->top;
5474 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5478 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5483 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5487 ERR("Unknown bounds=%ld\n", lprc->left);
5491 OffsetRect(lprc, Position.x, Position.y);
5498 * Retrieves the width of a label.
5501 * [I] infoPtr : valid pointer to the listview structure
5504 * SUCCESS : string width (in pixels)
5507 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5509 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5512 TRACE("(nItem=%d)\n", nItem);
5514 lvItem.mask = LVIF_TEXT;
5515 lvItem.iItem = nItem;
5516 lvItem.iSubItem = 0;
5517 lvItem.pszText = szDispText;
5518 lvItem.cchTextMax = DISP_TEXT_SIZE;
5519 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5521 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5526 * Retrieves the spacing between listview control items.
5529 * [I] infoPtr : valid pointer to the listview structure
5530 * [I] bSmall : flag for small or large icon
5533 * Horizontal + vertical spacing
5535 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5541 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5545 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5546 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5548 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5555 * Retrieves the state of a listview control item.
5558 * [I] infoPtr : valid pointer to the listview structure
5559 * [I] nItem : item index
5560 * [I] uMask : state mask
5563 * State specified by the mask.
5565 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5569 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5571 lvItem.iItem = nItem;
5572 lvItem.iSubItem = 0;
5573 lvItem.mask = LVIF_STATE;
5574 lvItem.stateMask = uMask;
5575 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5577 return lvItem.state & uMask;
5582 * Retrieves the text of a listview control item or subitem.
5585 * [I] hwnd : window handle
5586 * [I] nItem : item index
5587 * [IO] lpLVItem : item information
5588 * [I] isW : TRUE if lpLVItem is Unicode
5591 * SUCCESS : string length
5594 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5596 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5598 lpLVItem->mask = LVIF_TEXT;
5599 lpLVItem->iItem = nItem;
5600 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5602 return textlenT(lpLVItem->pszText, isW);
5607 * Searches for an item based on properties + relationships.
5610 * [I] infoPtr : valid pointer to the listview structure
5611 * [I] nItem : item index
5612 * [I] uFlags : relationship flag
5615 * SUCCESS : item index
5618 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5620 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5622 LVFINDINFOW lvFindInfo;
5623 INT nCountPerColumn;
5627 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5628 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5630 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5632 if (uFlags & LVNI_CUT)
5635 if (uFlags & LVNI_DROPHILITED)
5636 uMask |= LVIS_DROPHILITED;
5638 if (uFlags & LVNI_FOCUSED)
5639 uMask |= LVIS_FOCUSED;
5641 if (uFlags & LVNI_SELECTED)
5642 uMask |= LVIS_SELECTED;
5644 /* if we're asked for the focused item, that's only one,
5645 * so it's worth optimizing */
5646 if (uFlags & LVNI_FOCUSED)
5648 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5649 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5652 if (uFlags & LVNI_ABOVE)
5654 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5659 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5665 /* Special case for autoarrange - move 'til the top of a list */
5666 if (is_autoarrange(infoPtr))
5668 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5669 while (nItem - nCountPerRow >= 0)
5671 nItem -= nCountPerRow;
5672 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5677 lvFindInfo.flags = LVFI_NEARESTXY;
5678 lvFindInfo.vkDirection = VK_UP;
5679 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5680 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5682 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5687 else if (uFlags & LVNI_BELOW)
5689 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5691 while (nItem < infoPtr->nItemCount)
5694 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5700 /* Special case for autoarrange - move 'til the bottom of a list */
5701 if (is_autoarrange(infoPtr))
5703 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5704 while (nItem + nCountPerRow < infoPtr->nItemCount )
5706 nItem += nCountPerRow;
5707 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5712 lvFindInfo.flags = LVFI_NEARESTXY;
5713 lvFindInfo.vkDirection = VK_DOWN;
5714 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5715 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5717 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5722 else if (uFlags & LVNI_TOLEFT)
5724 if (uView == LVS_LIST)
5726 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5727 while (nItem - nCountPerColumn >= 0)
5729 nItem -= nCountPerColumn;
5730 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5734 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5736 /* Special case for autoarrange - move 'ti the beginning of a row */
5737 if (is_autoarrange(infoPtr))
5739 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5740 while (nItem % nCountPerRow > 0)
5743 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5748 lvFindInfo.flags = LVFI_NEARESTXY;
5749 lvFindInfo.vkDirection = VK_LEFT;
5750 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5751 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5753 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5758 else if (uFlags & LVNI_TORIGHT)
5760 if (uView == LVS_LIST)
5762 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5763 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5765 nItem += nCountPerColumn;
5766 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5770 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5772 /* Special case for autoarrange - move 'til the end of a row */
5773 if (is_autoarrange(infoPtr))
5775 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5776 while (nItem % nCountPerRow < nCountPerRow - 1 )
5779 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5784 lvFindInfo.flags = LVFI_NEARESTXY;
5785 lvFindInfo.vkDirection = VK_RIGHT;
5786 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5787 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5789 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5798 /* search by index */
5799 for (i = nItem; i < infoPtr->nItemCount; i++)
5801 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5809 /* LISTVIEW_GetNumberOfWorkAreas */
5813 * Retrieves the origin coordinates when in icon or small icon display mode.
5816 * [I] infoPtr : valid pointer to the listview structure
5817 * [O] lpptOrigin : coordinate information
5822 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5824 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5825 INT nHorzPos = 0, nVertPos = 0;
5826 SCROLLINFO scrollInfo;
5828 scrollInfo.cbSize = sizeof(SCROLLINFO);
5829 scrollInfo.fMask = SIF_POS;
5831 if ((infoPtr->dwStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5832 nHorzPos = scrollInfo.nPos;
5833 if ((infoPtr->dwStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5834 nVertPos = scrollInfo.nPos;
5836 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5838 lpptOrigin->x = infoPtr->rcList.left;
5839 lpptOrigin->y = infoPtr->rcList.top;
5840 if (uView == LVS_LIST)
5841 nHorzPos *= infoPtr->nItemWidth;
5842 else if (uView == LVS_REPORT)
5843 nVertPos *= infoPtr->nItemHeight;
5845 lpptOrigin->x -= nHorzPos;
5846 lpptOrigin->y -= nVertPos;
5848 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5853 * Retrieves the width of a string.
5856 * [I] hwnd : window handle
5857 * [I] lpszText : text string to process
5858 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5861 * SUCCESS : string width (in pixels)
5864 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5869 if (is_textT(lpszText, isW))
5871 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5872 HDC hdc = GetDC(infoPtr->hwndSelf);
5873 HFONT hOldFont = SelectObject(hdc, hFont);
5876 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5878 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5879 SelectObject(hdc, hOldFont);
5880 ReleaseDC(infoPtr->hwndSelf, hdc);
5882 return stringSize.cx;
5887 * Determines which listview item is located at the specified position.
5890 * [I] infoPtr : valid pointer to the listview structure
5891 * [IO] lpht : hit test information
5892 * [I] subitem : fill out iSubItem.
5893 * [I] select : return the index only if the hit selects the item
5896 * (mm 20001022): We must not allow iSubItem to be touched, for
5897 * an app might pass only a structure with space up to iItem!
5898 * (MS Office 97 does that for instance in the file open dialog)
5901 * SUCCESS : item index
5904 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5906 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5907 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5908 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5909 POINT Origin, Position, opt;
5914 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5918 if (subitem) lpht->iSubItem = 0;
5920 if (infoPtr->rcList.left > lpht->pt.x)
5921 lpht->flags |= LVHT_TOLEFT;
5922 else if (infoPtr->rcList.right < lpht->pt.x)
5923 lpht->flags |= LVHT_TORIGHT;
5925 if (infoPtr->rcList.top > lpht->pt.y)
5926 lpht->flags |= LVHT_ABOVE;
5927 else if (infoPtr->rcList.bottom < lpht->pt.y)
5928 lpht->flags |= LVHT_BELOW;
5930 TRACE("lpht->flags=0x%x\n", lpht->flags);
5931 if (lpht->flags) return -1;
5933 lpht->flags |= LVHT_NOWHERE;
5935 LISTVIEW_GetOrigin(infoPtr, &Origin);
5937 /* first deal with the large items */
5938 rcSearch.left = lpht->pt.x;
5939 rcSearch.top = lpht->pt.y;
5940 rcSearch.right = rcSearch.left + 1;
5941 rcSearch.bottom = rcSearch.top + 1;
5943 iterator_frameditems(&i, infoPtr, &rcSearch);
5944 iterator_next(&i); /* go to first item in the sequence */
5946 iterator_destroy(&i);
5948 TRACE("lpht->iItem=%d\n", iItem);
5949 if (iItem == -1) return -1;
5951 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5952 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5953 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5954 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5955 lvItem.iItem = iItem;
5956 lvItem.iSubItem = 0;
5957 lvItem.pszText = szDispText;
5958 lvItem.cchTextMax = DISP_TEXT_SIZE;
5959 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5960 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5962 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5963 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
5964 opt.x = lpht->pt.x - Position.x - Origin.x;
5965 opt.y = lpht->pt.y - Position.y - Origin.y;
5967 if (uView == LVS_REPORT)
5970 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5971 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5972 if (!PtInRect(&rcBounds, opt)) return -1;
5974 if (PtInRect(&rcIcon, opt))
5975 lpht->flags |= LVHT_ONITEMICON;
5976 else if (PtInRect(&rcLabel, opt))
5977 lpht->flags |= LVHT_ONITEMLABEL;
5978 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5979 lpht->flags |= LVHT_ONITEMSTATEICON;
5980 if (lpht->flags & LVHT_ONITEM)
5981 lpht->flags &= ~LVHT_NOWHERE;
5983 TRACE("lpht->flags=0x%x\n", lpht->flags);
5984 if (uView == LVS_REPORT && subitem)
5988 rcBounds.right = rcBounds.left;
5989 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
5991 rcBounds.left = rcBounds.right;
5992 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5993 if (PtInRect(&rcBounds, opt))
6001 if (select && !(uView == LVS_REPORT &&
6002 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6003 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6005 if (uView == LVS_REPORT)
6007 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6008 UnionRect(&rcBounds, &rcBounds, &rcState);
6010 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6012 return lpht->iItem = iItem;
6016 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6017 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6018 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6019 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6020 their own sort proc. when sending LVM_SORTITEMS.
6023 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6025 LVS_SORTXXX must be specified,
6026 LVS_OWNERDRAW is not set,
6027 <item>.pszText is not LPSTR_TEXTCALLBACK.
6029 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6030 are sorted based on item text..."
6032 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6034 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6035 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6036 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6038 /* if we're sorting descending, negate the return value */
6039 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6044 * Inserts a new item in the listview control.
6047 * [I] infoPtr : valid pointer to the listview structure
6048 * [I] lpLVItem : item information
6049 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6052 * SUCCESS : new item index
6055 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6057 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6062 BOOL is_sorted, has_changed;
6065 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6067 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6069 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6070 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6072 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6074 if ( !(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO))) )
6077 /* insert item in listview control data structure */
6078 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6079 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6081 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6082 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6084 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6085 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6086 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6087 if (nItem == -1) goto fail;
6088 infoPtr->nItemCount++;
6090 /* shift indices first so they don't get tangled */
6091 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6093 /* set the item attributes */
6094 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6096 /* full size structure expected - _WIN32IE >= 0x560 */
6099 else if (lpLVItem->mask & LVIF_INDENT)
6101 /* indent member expected - _WIN32IE >= 0x300 */
6102 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6106 /* minimal structure expected */
6107 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6110 item.state &= ~LVIS_STATEIMAGEMASK;
6111 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6113 /* if we're sorted, sort the list, and update the index */
6116 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6117 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6118 assert(nItem != -1);
6121 /* make room for the position, if we are in the right mode */
6122 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6124 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6126 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6128 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6133 /* send LVN_INSERTITEM notification */
6134 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6136 nmlv.lParam = lpItem->lParam;
6137 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6139 /* align items (set position of each item) */
6140 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6144 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6145 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6147 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6149 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6152 /* now is the invalidation fun */
6153 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6157 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6158 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6159 infoPtr->nItemCount--;
6161 DPA_DeletePtr(hdpaSubItems, 0);
6162 DPA_Destroy (hdpaSubItems);
6169 * Redraws a range of items.
6172 * [I] infoPtr : valid pointer to the listview structure
6173 * [I] nFirst : first item
6174 * [I] nLast : last item
6180 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6184 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6185 max(nFirst, nLast) >= infoPtr->nItemCount)
6188 for (i = nFirst; i <= nLast; i++)
6189 LISTVIEW_InvalidateItem(infoPtr, i);
6196 * Scroll the content of a listview.
6199 * [I] infoPtr : valid pointer to the listview structure
6200 * [I] dx : horizontal scroll amount in pixels
6201 * [I] dy : vertical scroll amount in pixels
6208 * If the control is in report mode (LVS_REPORT) the control can
6209 * be scrolled only in line increments. "dy" will be rounded to the
6210 * nearest number of pixels that are a whole line. Ex: if line height
6211 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6212 * is passed the the scroll will be 0. (per MSDN 7/2002)
6214 * For: (per experimentaion with native control and CSpy ListView)
6215 * LVS_ICON dy=1 = 1 pixel (vertical only)
6217 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6219 * LVS_LIST dx=1 = 1 column (horizontal only)
6220 * but will only scroll 1 column per message
6221 * no matter what the value.
6222 * dy must be 0 or FALSE returned.
6223 * LVS_REPORT dx=1 = 1 pixel
6227 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6229 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6231 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6232 dy /= infoPtr->nItemHeight;
6235 if (dy != 0) return FALSE;
6242 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6243 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6250 * Sets the background color.
6253 * [I] infoPtr : valid pointer to the listview structure
6254 * [I] clrBk : background color
6260 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6262 TRACE("(clrBk=%lx)\n", clrBk);
6264 if(infoPtr->clrBk != clrBk) {
6265 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6266 infoPtr->clrBk = clrBk;
6267 if (clrBk == CLR_NONE)
6268 infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6270 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6271 LISTVIEW_InvalidateList(infoPtr);
6277 /* LISTVIEW_SetBkImage */
6279 /*** Helper for {Insert,Set}ColumnT *only* */
6280 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6282 if (lpColumn->mask & LVCF_FMT)
6284 /* format member is valid */
6285 lphdi->mask |= HDI_FORMAT;
6287 /* set text alignment (leftmost column must be left-aligned) */
6288 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
6289 lphdi->fmt |= HDF_LEFT;
6290 else if (lpColumn->fmt & LVCFMT_RIGHT)
6291 lphdi->fmt |= HDF_RIGHT;
6292 else if (lpColumn->fmt & LVCFMT_CENTER)
6293 lphdi->fmt |= HDF_CENTER;
6295 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6296 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6298 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6300 lphdi->fmt |= HDF_IMAGE;
6301 lphdi->iImage = I_IMAGECALLBACK;
6305 if (lpColumn->mask & LVCF_WIDTH)
6307 lphdi->mask |= HDI_WIDTH;
6308 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6310 /* make it fill the remainder of the controls width */
6314 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6316 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6317 lphdi->cxy += rcHeader.right - rcHeader.left;
6320 /* retrieve the layout of the header */
6321 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6322 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6324 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6327 lphdi->cxy = lpColumn->cx;
6330 if (lpColumn->mask & LVCF_TEXT)
6332 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6333 lphdi->fmt |= HDF_STRING;
6334 lphdi->pszText = lpColumn->pszText;
6335 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6338 if (lpColumn->mask & LVCF_IMAGE)
6340 lphdi->mask |= HDI_IMAGE;
6341 lphdi->iImage = lpColumn->iImage;
6344 if (lpColumn->mask & LVCF_ORDER)
6346 lphdi->mask |= HDI_ORDER;
6347 lphdi->iOrder = lpColumn->iOrder;
6354 * Inserts a new column.
6357 * [I] infoPtr : valid pointer to the listview structure
6358 * [I] nColumn : column index
6359 * [I] lpColumn : column information
6360 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6363 * SUCCESS : new column index
6366 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6367 const LVCOLUMNW *lpColumn, BOOL isW)
6369 COLUMN_INFO *lpColumnInfo;
6373 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6375 if (!lpColumn || nColumn < 0) return -1;
6376 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6378 ZeroMemory(&hdi, sizeof(HDITEMW));
6379 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6381 /* insert item in header control */
6382 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6383 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6384 (WPARAM)nColumn, (LPARAM)&hdi);
6385 if (nNewColumn == -1) return -1;
6386 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6388 /* create our own column info */
6389 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6390 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6392 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6393 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6395 /* now we have to actually adjust the data */
6396 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6398 SUBITEM_INFO *lpSubItem;
6402 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6404 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6405 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6407 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6408 if (lpSubItem->iSubItem >= nNewColumn)
6409 lpSubItem->iSubItem++;
6414 /* make space for the new column */
6415 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6420 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6423 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6431 * Sets the attributes of a header item.
6434 * [I] infoPtr : valid pointer to the listview structure
6435 * [I] nColumn : column index
6436 * [I] lpColumn : column attributes
6437 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6443 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6444 const LVCOLUMNW *lpColumn, BOOL isW)
6446 HDITEMW hdi, hdiget;
6449 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6451 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6453 ZeroMemory(&hdi, sizeof(HDITEMW));
6454 if (lpColumn->mask & LVCF_FMT)
6456 hdi.mask |= HDI_FORMAT;
6457 hdiget.mask = HDI_FORMAT;
6458 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6459 hdi.fmt = hdiget.fmt & HDF_STRING;
6461 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6463 /* set header item attributes */
6464 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6465 if (!bResult) return FALSE;
6467 if (lpColumn->mask & LVCF_FMT)
6469 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6470 int oldFmt = lpColumnInfo->fmt;
6472 lpColumnInfo->fmt = lpColumn->fmt;
6473 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6475 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6476 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6485 * Sets the column order array
6488 * [I] infoPtr : valid pointer to the listview structure
6489 * [I] iCount : number of elements in column order array
6490 * [I] lpiArray : pointer to column order array
6496 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6498 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6509 * Sets the width of a column
6512 * [I] infoPtr : valid pointer to the listview structure
6513 * [I] nColumn : column index
6514 * [I] cx : column width
6520 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6522 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6523 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6527 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6529 /* set column width only if in report or list mode */
6530 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6532 /* take care of invalid cx values */
6533 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6534 else if (uView == LVS_LIST && cx < 1) return FALSE;
6536 /* resize all columns if in LVS_LIST mode */
6537 if(uView == LVS_LIST)
6539 infoPtr->nItemWidth = cx;
6540 LISTVIEW_InvalidateList(infoPtr);
6544 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6546 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6551 lvItem.mask = LVIF_TEXT;
6553 lvItem.iSubItem = nColumn;
6554 lvItem.pszText = szDispText;
6555 lvItem.cchTextMax = DISP_TEXT_SIZE;
6556 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6558 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6559 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6560 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6562 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6563 max_cx += infoPtr->iconSize.cx;
6564 max_cx += TRAILING_LABEL_PADDING;
6567 /* autosize based on listview items width */
6568 if(cx == LVSCW_AUTOSIZE)
6570 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6572 /* if iCol is the last column make it fill the remainder of the controls width */
6573 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6578 LISTVIEW_GetOrigin(infoPtr, &Origin);
6579 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6581 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6585 /* Despite what the MS docs say, if this is not the last
6586 column, then MS resizes the column to the width of the
6587 largest text string in the column, including headers
6588 and items. This is different from LVSCW_AUTOSIZE in that
6589 LVSCW_AUTOSIZE ignores the header string length. */
6592 /* retrieve header text */
6593 hdi.mask = HDI_TEXT;
6594 hdi.cchTextMax = DISP_TEXT_SIZE;
6595 hdi.pszText = szDispText;
6596 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6598 HDC hdc = GetDC(infoPtr->hwndSelf);
6599 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6602 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6603 cx = size.cx + TRAILING_HEADER_PADDING;
6604 /* FIXME: Take into account the header image, if one is present */
6605 SelectObject(hdc, old_font);
6606 ReleaseDC(infoPtr->hwndSelf, hdc);
6608 cx = max (cx, max_cx);
6612 if (cx < 0) return FALSE;
6614 /* call header to update the column change */
6615 hdi.mask = HDI_WIDTH;
6617 TRACE("hdi.cxy=%d\n", hdi.cxy);
6618 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6622 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6625 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6628 HBITMAP hbm_im, hbm_mask, hbm_orig;
6630 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6631 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6634 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6635 ILC_COLOR | ILC_MASK, 2, 2);
6636 hdc_wnd = GetDC(infoPtr->hwndSelf);
6637 hdc = CreateCompatibleDC(hdc_wnd);
6638 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6639 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6640 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6642 rc.left = rc.top = 0;
6643 rc.right = GetSystemMetrics(SM_CXSMICON);
6644 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6646 hbm_orig = SelectObject(hdc, hbm_mask);
6647 FillRect(hdc, &rc, hbr_white);
6648 InflateRect(&rc, -3, -3);
6649 FillRect(hdc, &rc, hbr_black);
6651 SelectObject(hdc, hbm_im);
6652 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6653 SelectObject(hdc, hbm_orig);
6654 ImageList_Add(himl, hbm_im, hbm_mask);
6656 SelectObject(hdc, hbm_im);
6657 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6658 SelectObject(hdc, hbm_orig);
6659 ImageList_Add(himl, hbm_im, hbm_mask);
6661 DeleteObject(hbm_mask);
6662 DeleteObject(hbm_im);
6670 * Sets the extended listview style.
6673 * [I] infoPtr : valid pointer to the listview structure
6675 * [I] dwStyle : style
6678 * SUCCESS : previous style
6681 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6683 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6687 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6689 infoPtr->dwLvExStyle = dwStyle;
6691 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6693 HIMAGELIST himl = 0;
6694 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6695 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6696 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6704 * Sets the new hot cursor used during hot tracking and hover selection.
6707 * [I] infoPtr : valid pointer to the listview structure
6708 * [I} hCurosr : the new hot cursor handle
6711 * Returns the previous hot cursor
6713 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6715 HCURSOR oldCursor = infoPtr->hHotCursor;
6717 infoPtr->hHotCursor = hCursor;
6725 * Sets the hot item index.
6728 * [I] infoPtr : valid pointer to the listview structure
6729 * [I] iIndex : index
6732 * SUCCESS : previous hot item index
6733 * FAILURE : -1 (no hot item)
6735 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6737 INT iOldIndex = infoPtr->nHotItem;
6739 infoPtr->nHotItem = iIndex;
6747 * Sets the amount of time the cursor must hover over an item before it is selected.
6750 * [I] infoPtr : valid pointer to the listview structure
6751 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6754 * Returns the previous hover time
6756 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6758 DWORD oldHoverTime = infoPtr->dwHoverTime;
6760 infoPtr->dwHoverTime = dwHoverTime;
6762 return oldHoverTime;
6767 * Sets spacing for icons of LVS_ICON style.
6770 * [I] infoPtr : valid pointer to the listview structure
6771 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6772 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6775 * MAKELONG(oldcx, oldcy)
6777 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6779 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6780 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6782 TRACE("requested=(%d,%d)\n", cx, cy);
6784 /* this is supported only for LVS_ICON style */
6785 if (uView != LVS_ICON) return oldspacing;
6787 /* set to defaults, if instructed to */
6788 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6789 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6791 /* if 0 then compute width
6792 * FIXME: Should scan each item and determine max width of
6793 * icon or label, then make that the width */
6795 cx = infoPtr->iconSpacing.cx;
6797 /* if 0 then compute height */
6799 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6800 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6803 infoPtr->iconSpacing.cx = cx;
6804 infoPtr->iconSpacing.cy = cy;
6806 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6807 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6808 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6809 infoPtr->ntmHeight);
6811 /* these depend on the iconSpacing */
6812 LISTVIEW_UpdateItemSize(infoPtr);
6817 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6821 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6828 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6829 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6838 * [I] infoPtr : valid pointer to the listview structure
6839 * [I] nType : image list type
6840 * [I] himl : image list handle
6843 * SUCCESS : old image list
6846 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6848 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6849 INT oldHeight = infoPtr->nItemHeight;
6850 HIMAGELIST himlOld = 0;
6852 TRACE("(nType=%d, himl=%p\n", nType, himl);
6857 himlOld = infoPtr->himlNormal;
6858 infoPtr->himlNormal = himl;
6859 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6860 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6864 himlOld = infoPtr->himlSmall;
6865 infoPtr->himlSmall = himl;
6866 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6870 himlOld = infoPtr->himlState;
6871 infoPtr->himlState = himl;
6872 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6873 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6877 ERR("Unknown icon type=%d\n", nType);
6881 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6882 if (infoPtr->nItemHeight != oldHeight)
6883 LISTVIEW_UpdateScroll(infoPtr);
6890 * Preallocates memory (does *not* set the actual count of items !)
6893 * [I] infoPtr : valid pointer to the listview structure
6894 * [I] nItems : item count (projected number of items to allocate)
6895 * [I] dwFlags : update flags
6901 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6903 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6905 if (infoPtr->dwStyle & LVS_OWNERDATA)
6907 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6908 INT nOldCount = infoPtr->nItemCount;
6910 if (nItems < nOldCount)
6912 RANGE range = { nItems, nOldCount };
6913 ranges_del(infoPtr->selectionRanges, range);
6914 if (infoPtr->nFocusedItem >= nItems)
6916 infoPtr->nFocusedItem = -1;
6917 SetRectEmpty(&infoPtr->rcFocus);
6921 infoPtr->nItemCount = nItems;
6922 LISTVIEW_UpdateScroll(infoPtr);
6924 /* the flags are valid only in ownerdata report and list modes */
6925 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6927 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6928 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6930 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6931 LISTVIEW_InvalidateList(infoPtr);
6938 LISTVIEW_GetOrigin(infoPtr, &Origin);
6939 nFrom = min(nOldCount, nItems);
6940 nTo = max(nOldCount, nItems);
6942 if (uView == LVS_REPORT)
6945 rcErase.top = nFrom * infoPtr->nItemHeight;
6946 rcErase.right = infoPtr->nItemWidth;
6947 rcErase.bottom = nTo * infoPtr->nItemHeight;
6948 OffsetRect(&rcErase, Origin.x, Origin.y);
6949 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6950 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6954 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6956 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6957 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6958 rcErase.right = rcErase.left + infoPtr->nItemWidth;
6959 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6960 OffsetRect(&rcErase, Origin.x, Origin.y);
6961 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6962 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6964 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6966 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
6967 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6968 OffsetRect(&rcErase, Origin.x, Origin.y);
6969 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6970 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6976 /* According to MSDN for non-LVS_OWNERDATA this is just
6977 * a performance issue. The control allocates its internal
6978 * data structures for the number of items specified. It
6979 * cuts down on the number of memory allocations. Therefore
6980 * we will just issue a WARN here
6982 WARN("for non-ownerdata performance option not implemented.\n");
6990 * Sets the position of an item.
6993 * [I] infoPtr : valid pointer to the listview structure
6994 * [I] nItem : item index
6995 * [I] pt : coordinate
7001 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7003 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7006 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
7008 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7009 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7011 LISTVIEW_GetOrigin(infoPtr, &Origin);
7013 /* This point value seems to be an undocumented feature.
7014 * The best guess is that it means either at the origin,
7015 * or at true beginning of the list. I will assume the origin. */
7016 if ((pt.x == -1) && (pt.y == -1))
7019 if (uView == LVS_ICON)
7021 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7022 pt.y -= ICON_TOP_PADDING;
7027 infoPtr->bAutoarrange = FALSE;
7029 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7034 * Sets the state of one or many items.
7037 * [I] infoPtr : valid pointer to the listview structure
7038 * [I] nItem : item index
7039 * [I] lpLVItem : item or subitem info
7045 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7047 BOOL bResult = TRUE;
7050 lvItem.iItem = nItem;
7051 lvItem.iSubItem = 0;
7052 lvItem.mask = LVIF_STATE;
7053 lvItem.state = lpLVItem->state;
7054 lvItem.stateMask = lpLVItem->stateMask;
7055 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7059 /* apply to all items */
7060 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7061 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7064 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7067 *update selection mark
7069 * Investigation on windows 2k showed that selection mark was updated
7070 * whenever a new selection was made, but if the selected item was
7071 * unselected it was not updated.
7073 * we are probably still not 100% accurate, but this at least sets the
7074 * proper selection mark when it is needed
7077 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7078 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7081 infoPtr->nSelectionMark = -1;
7082 for (i = 0; i < infoPtr->nItemCount; i++)
7084 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7086 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7088 infoPtr->nSelectionMark = i;
7092 else if (ranges_contain(infoPtr->selectionRanges, i))
7094 infoPtr->nSelectionMark = i;
7105 * Sets the text of an item or subitem.
7108 * [I] hwnd : window handle
7109 * [I] nItem : item index
7110 * [I] lpLVItem : item or subitem info
7111 * [I] isW : TRUE if input is Unicode
7117 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7121 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7123 lvItem.iItem = nItem;
7124 lvItem.iSubItem = lpLVItem->iSubItem;
7125 lvItem.mask = LVIF_TEXT;
7126 lvItem.pszText = lpLVItem->pszText;
7127 lvItem.cchTextMax = lpLVItem->cchTextMax;
7129 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7131 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7136 * Set item index that marks the start of a multiple selection.
7139 * [I] infoPtr : valid pointer to the listview structure
7140 * [I] nIndex : index
7143 * Index number or -1 if there is no selection mark.
7145 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7147 INT nOldIndex = infoPtr->nSelectionMark;
7149 TRACE("(nIndex=%d)\n", nIndex);
7151 infoPtr->nSelectionMark = nIndex;
7158 * Sets the text background color.
7161 * [I] infoPtr : valid pointer to the listview structure
7162 * [I] clrTextBk : text background color
7168 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7170 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7172 if (infoPtr->clrTextBk != clrTextBk)
7174 infoPtr->clrTextBk = clrTextBk;
7175 LISTVIEW_InvalidateList(infoPtr);
7183 * Sets the text foreground color.
7186 * [I] infoPtr : valid pointer to the listview structure
7187 * [I] clrText : text color
7193 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7195 TRACE("(clrText=%lx)\n", clrText);
7197 if (infoPtr->clrText != clrText)
7199 infoPtr->clrText = clrText;
7200 LISTVIEW_InvalidateList(infoPtr);
7208 * Determines which listview item is located at the specified position.
7211 * [I] infoPtr : valid pointer to the listview structure
7212 * [I] hwndNewToolTip : handle to new ToolTip
7217 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7219 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7220 infoPtr->hwndToolTip = hwndNewToolTip;
7221 return hwndOldToolTip;
7224 /* LISTVIEW_SetUnicodeFormat */
7225 /* LISTVIEW_SetWorkAreas */
7229 * Callback internally used by LISTVIEW_SortItems()
7232 * [I] first : pointer to first ITEM_INFO to compare
7233 * [I] second : pointer to second ITEM_INFO to compare
7234 * [I] lParam : HWND of control
7237 * if first comes before second : negative
7238 * if first comes after second : positive
7239 * if first and second are equivalent : zero
7241 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7243 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7244 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7245 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7247 /* Forward the call to the client defined callback */
7248 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7253 * Sorts the listview items.
7256 * [I] infoPtr : valid pointer to the listview structure
7257 * [I] pfnCompare : application-defined value
7258 * [I] lParamSort : pointer to comparision callback
7264 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7266 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7269 LPVOID selectionMarkItem;
7273 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7275 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7277 if (!infoPtr->hdpaItems) return FALSE;
7279 /* if there are 0 or 1 items, there is no need to sort */
7280 if (infoPtr->nItemCount < 2) return TRUE;
7282 if (infoPtr->nFocusedItem >= 0)
7284 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7285 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7286 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7288 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7289 /* clear the lpItem->state for non-selected ones */
7290 /* remove the selection ranges */
7292 infoPtr->pfnCompare = pfnCompare;
7293 infoPtr->lParamSort = lParamSort;
7294 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7296 /* Adjust selections and indices so that they are the way they should
7297 * be after the sort (otherwise, the list items move around, but
7298 * whatever is at the item's previous original position will be
7301 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7302 for (i=0; i < infoPtr->nItemCount; i++)
7304 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7305 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7307 if (lpItem->state & LVIS_SELECTED)
7309 item.state = LVIS_SELECTED;
7310 item.stateMask = LVIS_SELECTED;
7311 LISTVIEW_SetItemState(infoPtr, i, &item);
7313 if (lpItem->state & LVIS_FOCUSED)
7315 infoPtr->nFocusedItem = i;
7316 lpItem->state &= ~LVIS_FOCUSED;
7319 if (selectionMarkItem != NULL)
7320 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7321 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7323 /* refresh the display */
7324 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7325 LISTVIEW_InvalidateList(infoPtr);
7332 * Updates an items or rearranges the listview control.
7335 * [I] infoPtr : valid pointer to the listview structure
7336 * [I] nItem : item index
7342 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7344 TRACE("(nItem=%d)\n", nItem);
7346 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7348 /* rearrange with default alignment style */
7349 if (is_autoarrange(infoPtr))
7350 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7352 LISTVIEW_InvalidateItem(infoPtr, nItem);
7360 * Creates the listview control.
7363 * [I] hwnd : window handle
7364 * [I] lpcs : the create parameters
7370 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7372 LISTVIEW_INFO *infoPtr;
7373 UINT uView = lpcs->style & LVS_TYPEMASK;
7376 TRACE("(lpcs=%p)\n", lpcs);
7378 /* initialize info pointer */
7379 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7380 if (!infoPtr) return -1;
7382 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7384 infoPtr->hwndSelf = hwnd;
7385 infoPtr->dwStyle = lpcs->style;
7386 /* determine the type of structures to use */
7387 infoPtr->hwndNotify = lpcs->hwndParent;
7388 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7389 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7391 /* initialize color information */
7392 infoPtr->clrBk = CLR_NONE;
7393 infoPtr->clrText = comctl32_color.clrWindowText;
7394 infoPtr->clrTextBk = CLR_DEFAULT;
7395 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7397 /* set default values */
7398 infoPtr->nFocusedItem = -1;
7399 infoPtr->nSelectionMark = -1;
7400 infoPtr->nHotItem = -1;
7401 infoPtr->bRedraw = TRUE;
7402 infoPtr->bNoItemMetrics = TRUE;
7403 infoPtr->bDoChangeNotify = TRUE;
7404 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7405 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7406 infoPtr->nEditLabelItem = -1;
7407 infoPtr->dwHoverTime = -1; /* default system hover time */
7409 /* get default font (icon title) */
7410 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7411 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7412 infoPtr->hFont = infoPtr->hDefaultFont;
7413 LISTVIEW_SaveTextMetrics(infoPtr);
7416 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7417 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7418 0, 0, 0, 0, hwnd, NULL,
7419 lpcs->hInstance, NULL);
7420 if (!infoPtr->hwndHeader) goto fail;
7422 /* set header unicode format */
7423 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7425 /* set header font */
7426 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7428 /* allocate memory for the data structure */
7429 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7430 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7431 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7432 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7433 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7435 /* initialize the icon sizes */
7436 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7437 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7439 /* init item size to avoid division by 0 */
7440 LISTVIEW_UpdateItemSize (infoPtr);
7442 if (uView == LVS_REPORT)
7444 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7446 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7450 /* set HDS_HIDDEN flag to hide the header bar */
7451 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7452 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7459 DestroyWindow(infoPtr->hwndHeader);
7460 ranges_destroy(infoPtr->selectionRanges);
7461 DPA_Destroy(infoPtr->hdpaItems);
7462 DPA_Destroy(infoPtr->hdpaPosX);
7463 DPA_Destroy(infoPtr->hdpaPosY);
7464 DPA_Destroy(infoPtr->hdpaColumns);
7471 * Erases the background of the listview control.
7474 * [I] infoPtr : valid pointer to the listview structure
7475 * [I] hdc : device context handle
7481 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7485 TRACE("(hdc=%p)\n", hdc);
7487 if (!GetClipBox(hdc, &rc)) return FALSE;
7489 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7495 * Helper function for LISTVIEW_[HV]Scroll *only*.
7496 * Performs vertical/horizontal scrolling by a give amount.
7499 * [I] infoPtr : valid pointer to the listview structure
7500 * [I] dx : amount of horizontal scroll
7501 * [I] dy : amount of vertical scroll
7503 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7505 /* now we can scroll the list */
7506 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7507 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7508 /* if we have focus, adjust rect */
7509 OffsetRect(&infoPtr->rcFocus, dx, dy);
7510 UpdateWindow(infoPtr->hwndSelf);
7515 * Performs vertical scrolling.
7518 * [I] infoPtr : valid pointer to the listview structure
7519 * [I] nScrollCode : scroll code
7520 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7521 * [I] hScrollWnd : scrollbar control window handle
7527 * SB_LINEUP/SB_LINEDOWN:
7528 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7529 * for LVS_REPORT is 1 line
7530 * for LVS_LIST cannot occur
7533 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7534 INT nScrollDiff, HWND hScrollWnd)
7536 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7537 INT nOldScrollPos, nNewScrollPos;
7538 SCROLLINFO scrollInfo;
7541 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7542 debugscrollcode(nScrollCode), nScrollDiff);
7544 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7546 scrollInfo.cbSize = sizeof(SCROLLINFO);
7547 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7549 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7551 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7553 nOldScrollPos = scrollInfo.nPos;
7554 switch (nScrollCode)
7560 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7564 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7568 nScrollDiff = -scrollInfo.nPage;
7572 nScrollDiff = scrollInfo.nPage;
7575 case SB_THUMBPOSITION:
7577 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7584 /* quit right away if pos isn't changing */
7585 if (nScrollDiff == 0) return 0;
7587 /* calculate new position, and handle overflows */
7588 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7589 if (nScrollDiff > 0) {
7590 if (nNewScrollPos < nOldScrollPos ||
7591 nNewScrollPos > scrollInfo.nMax)
7592 nNewScrollPos = scrollInfo.nMax;
7594 if (nNewScrollPos > nOldScrollPos ||
7595 nNewScrollPos < scrollInfo.nMin)
7596 nNewScrollPos = scrollInfo.nMin;
7599 /* set the new position, and reread in case it changed */
7600 scrollInfo.fMask = SIF_POS;
7601 scrollInfo.nPos = nNewScrollPos;
7602 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7604 /* carry on only if it really changed */
7605 if (nNewScrollPos == nOldScrollPos) return 0;
7607 /* now adjust to client coordinates */
7608 nScrollDiff = nOldScrollPos - nNewScrollPos;
7609 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7611 /* and scroll the window */
7612 scroll_list(infoPtr, 0, nScrollDiff);
7619 * Performs horizontal scrolling.
7622 * [I] infoPtr : valid pointer to the listview structure
7623 * [I] nScrollCode : scroll code
7624 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7625 * [I] hScrollWnd : scrollbar control window handle
7631 * SB_LINELEFT/SB_LINERIGHT:
7632 * for LVS_ICON, LVS_SMALLICON 1 pixel
7633 * for LVS_REPORT is 1 pixel
7634 * for LVS_LIST is 1 column --> which is a 1 because the
7635 * scroll is based on columns not pixels
7638 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7639 INT nScrollDiff, HWND hScrollWnd)
7641 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7642 INT nOldScrollPos, nNewScrollPos;
7643 SCROLLINFO scrollInfo;
7645 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7646 debugscrollcode(nScrollCode), nScrollDiff);
7648 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7650 scrollInfo.cbSize = sizeof(SCROLLINFO);
7651 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7653 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7655 nOldScrollPos = scrollInfo.nPos;
7657 switch (nScrollCode)
7671 nScrollDiff = -scrollInfo.nPage;
7675 nScrollDiff = scrollInfo.nPage;
7678 case SB_THUMBPOSITION:
7680 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7687 /* quit right away if pos isn't changing */
7688 if (nScrollDiff == 0) return 0;
7690 /* calculate new position, and handle overflows */
7691 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7692 if (nScrollDiff > 0) {
7693 if (nNewScrollPos < nOldScrollPos ||
7694 nNewScrollPos > scrollInfo.nMax)
7695 nNewScrollPos = scrollInfo.nMax;
7697 if (nNewScrollPos > nOldScrollPos ||
7698 nNewScrollPos < scrollInfo.nMin)
7699 nNewScrollPos = scrollInfo.nMin;
7702 /* set the new position, and reread in case it changed */
7703 scrollInfo.fMask = SIF_POS;
7704 scrollInfo.nPos = nNewScrollPos;
7705 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7707 /* carry on only if it really changed */
7708 if (nNewScrollPos == nOldScrollPos) return 0;
7710 if(uView == LVS_REPORT)
7711 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7713 /* now adjust to client coordinates */
7714 nScrollDiff = nOldScrollPos - nNewScrollPos;
7715 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7717 /* and scroll the window */
7718 scroll_list(infoPtr, nScrollDiff, 0);
7723 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7725 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7726 INT gcWheelDelta = 0;
7727 UINT pulScrollLines = 3;
7728 SCROLLINFO scrollInfo;
7730 TRACE("(wheelDelta=%d)\n", wheelDelta);
7732 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7733 gcWheelDelta -= wheelDelta;
7735 scrollInfo.cbSize = sizeof(SCROLLINFO);
7736 scrollInfo.fMask = SIF_POS;
7743 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7744 * should be fixed in the future.
7746 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7747 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7751 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7753 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7754 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7755 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7760 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7771 * [I] infoPtr : valid pointer to the listview structure
7772 * [I] nVirtualKey : virtual key
7773 * [I] lKeyData : key data
7778 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7780 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7782 NMLVKEYDOWN nmKeyDown;
7784 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7786 /* send LVN_KEYDOWN notification */
7787 nmKeyDown.wVKey = nVirtualKey;
7788 nmKeyDown.flags = 0;
7789 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7791 switch (nVirtualKey)
7794 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7796 notify(infoPtr, NM_RETURN);
7797 notify(infoPtr, LVN_ITEMACTIVATE);
7802 if (infoPtr->nItemCount > 0)
7807 if (infoPtr->nItemCount > 0)
7808 nItem = infoPtr->nItemCount - 1;
7812 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7816 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7820 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7824 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7828 if (uView == LVS_REPORT)
7829 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7831 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7832 * LISTVIEW_GetCountPerRow(infoPtr);
7833 if(nItem < 0) nItem = 0;
7837 if (uView == LVS_REPORT)
7838 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7840 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7841 * LISTVIEW_GetCountPerRow(infoPtr);
7842 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7846 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7847 LISTVIEW_KeySelection(infoPtr, nItem);
7857 * [I] infoPtr : valid pointer to the listview structure
7862 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7866 /* if we did not have the focus, there's nothing to do */
7867 if (!infoPtr->bFocus) return 0;
7869 /* send NM_KILLFOCUS notification */
7870 notify(infoPtr, NM_KILLFOCUS);
7872 /* if we have a focus rectagle, get rid of it */
7873 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7875 /* set window focus flag */
7876 infoPtr->bFocus = FALSE;
7878 /* invalidate the selected items before reseting focus flag */
7879 LISTVIEW_InvalidateSelectedItems(infoPtr);
7887 * Track mouse/dragging
7890 * [I] infoPtr : valid pointer to the listview structure
7891 * [I] pt : mouse coordinate
7896 static LRESULT LISTVIEW_TrackMouse(LISTVIEW_INFO *infoPtr, POINT pt)
7898 INT cxDrag = GetSystemMetrics(SM_CXDRAG);
7899 INT cyDrag = GetSystemMetrics(SM_CYDRAG);
7905 r.top = pt.y - cyDrag;
7906 r.left = pt.x - cxDrag;
7907 r.bottom = pt.y + cyDrag;
7908 r.right = pt.x + cxDrag;
7910 SetCapture(infoPtr->hwndSelf);
7914 if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
7916 if (msg.message == WM_MOUSEMOVE)
7918 pt.x = (short)LOWORD(msg.lParam);
7919 pt.y = (short)HIWORD(msg.lParam);
7920 if (PtInRect(&r, pt))
7928 else if (msg.message >= WM_LBUTTONDOWN &&
7929 msg.message <= WM_RBUTTONDBLCLK)
7934 DispatchMessageW(&msg);
7937 if (GetCapture() != infoPtr->hwndSelf)
7948 * Processes double click messages (left mouse button).
7951 * [I] infoPtr : valid pointer to the listview structure
7952 * [I] wKey : key flag
7953 * [I] pts : mouse coordinate
7958 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7960 LVHITTESTINFO htInfo;
7962 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7964 /* send NM_RELEASEDCAPTURE notification */
7965 notify(infoPtr, NM_RELEASEDCAPTURE);
7967 htInfo.pt.x = pts.x;
7968 htInfo.pt.y = pts.y;
7970 /* send NM_DBLCLK notification */
7971 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7972 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7974 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7975 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
7982 * Processes mouse down messages (left mouse button).
7985 * [I] infoPtr : valid pointer to the listview structure
7986 * [I] wKey : key flag
7987 * [I] pts : mouse coordinate
7992 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7994 LVHITTESTINFO lvHitTestInfo;
7995 static BOOL bGroupSelect = TRUE;
7996 POINT pt = { pts.x, pts.y };
7999 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8001 /* send NM_RELEASEDCAPTURE notification */
8002 notify(infoPtr, NM_RELEASEDCAPTURE);
8004 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8006 /* set left button down flag */
8007 infoPtr->bLButtonDown = TRUE;
8009 lvHitTestInfo.pt.x = pts.x;
8010 lvHitTestInfo.pt.y = pts.y;
8012 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8013 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
8014 infoPtr->nEditLabelItem = -1;
8015 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8017 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8019 DWORD state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK) >> 12;
8020 if(state == 1 || state == 2)
8024 lvitem.state = state << 12;
8025 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8026 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8030 if (LISTVIEW_TrackMouse(infoPtr, lvHitTestInfo.pt))
8034 ZeroMemory(&nmlv, sizeof(nmlv));
8036 nmlv.ptAction.x = lvHitTestInfo.pt.x;
8037 nmlv.ptAction.y = lvHitTestInfo.pt.y;
8039 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
8044 if (infoPtr->dwStyle & LVS_SINGLESEL)
8046 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8047 infoPtr->nEditLabelItem = nItem;
8049 LISTVIEW_SetSelection(infoPtr, nItem);
8053 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8057 LISTVIEW_AddGroupSelection(infoPtr, nItem);
8058 LISTVIEW_SetItemFocus(infoPtr, nItem);
8059 infoPtr->nSelectionMark = nItem;
8065 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8066 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8068 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8069 infoPtr->nSelectionMark = nItem;
8072 else if (wKey & MK_CONTROL)
8076 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8078 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8079 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8080 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8081 infoPtr->nSelectionMark = nItem;
8083 else if (wKey & MK_SHIFT)
8085 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8089 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8090 infoPtr->nEditLabelItem = nItem;
8092 /* set selection (clears other pre-existing selections) */
8093 LISTVIEW_SetSelection(infoPtr, nItem);
8099 /* remove all selections */
8100 LISTVIEW_DeselectAll(infoPtr);
8109 * Processes mouse up messages (left mouse button).
8112 * [I] infoPtr : valid pointer to the listview structure
8113 * [I] wKey : key flag
8114 * [I] pts : mouse coordinate
8119 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8121 LVHITTESTINFO lvHitTestInfo;
8123 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8125 if (!infoPtr->bLButtonDown) return 0;
8127 lvHitTestInfo.pt.x = pts.x;
8128 lvHitTestInfo.pt.y = pts.y;
8130 /* send NM_CLICK notification */
8131 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8132 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
8134 /* set left button flag */
8135 infoPtr->bLButtonDown = FALSE;
8137 /* if we clicked on a selected item, edit the label */
8138 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8139 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8146 * Destroys the listview control (called after WM_DESTROY).
8149 * [I] infoPtr : valid pointer to the listview structure
8154 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8158 /* delete all items */
8159 LISTVIEW_DeleteAllItems(infoPtr);
8161 /* destroy data structure */
8162 DPA_Destroy(infoPtr->hdpaItems);
8163 DPA_Destroy(infoPtr->hdpaPosX);
8164 DPA_Destroy(infoPtr->hdpaPosY);
8165 DPA_Destroy(infoPtr->hdpaColumns);
8166 ranges_destroy(infoPtr->selectionRanges);
8168 /* destroy image lists */
8169 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8171 if (infoPtr->himlNormal)
8172 ImageList_Destroy(infoPtr->himlNormal);
8173 if (infoPtr->himlSmall)
8174 ImageList_Destroy(infoPtr->himlSmall);
8175 if (infoPtr->himlState)
8176 ImageList_Destroy(infoPtr->himlState);
8179 /* destroy font, bkgnd brush */
8181 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8182 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8184 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
8186 /* free listview info pointer*/
8194 * Handles notifications from header.
8197 * [I] infoPtr : valid pointer to the listview structure
8198 * [I] nCtrlId : control identifier
8199 * [I] lpnmh : notification information
8204 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8206 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8208 TRACE("(lpnmh=%p)\n", lpnmh);
8210 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8212 switch (lpnmh->hdr.code)
8216 case HDN_ITEMCHANGEDW:
8217 case HDN_ITEMCHANGEDA:
8219 COLUMN_INFO *lpColumnInfo;
8222 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8226 hdi.mask = HDI_WIDTH;
8227 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8231 cxy = lpnmh->pitem->cxy;
8233 /* determine how much we change since the last know position */
8234 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8235 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8238 RECT rcCol = lpColumnInfo->rcHeader;
8240 lpColumnInfo->rcHeader.right += dx;
8241 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8242 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8244 /* this trick works for left aligned columns only */
8245 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8247 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
8248 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
8250 rcCol.top = infoPtr->rcList.top;
8251 rcCol.bottom = infoPtr->rcList.bottom;
8252 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8258 case HDN_ITEMCLICKW:
8259 case HDN_ITEMCLICKA:
8261 /* Handle sorting by Header Column */
8264 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8266 nmlv.iSubItem = lpnmh->iItem;
8267 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8277 * Determines the type of structure to use.
8280 * [I] infoPtr : valid pointer to the listview structureof the sender
8281 * [I] hwndFrom : listview window handle
8282 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8287 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8289 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8291 if (nCommand != NF_REQUERY) return 0;
8293 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8300 * Paints/Repaints the listview control.
8303 * [I] infoPtr : valid pointer to the listview structure
8304 * [I] hdc : device context handle
8309 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8311 TRACE("(hdc=%p)\n", hdc);
8313 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8315 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8317 infoPtr->bNoItemMetrics = FALSE;
8318 LISTVIEW_UpdateItemSize(infoPtr);
8319 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8320 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8321 LISTVIEW_UpdateScroll(infoPtr);
8324 LISTVIEW_Refresh(infoPtr, hdc);
8329 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8331 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8332 LISTVIEW_Refresh(infoPtr, hdc);
8333 EndPaint(infoPtr->hwndSelf, &ps);
8341 * Processes double click messages (right mouse button).
8344 * [I] infoPtr : valid pointer to the listview structure
8345 * [I] wKey : key flag
8346 * [I] pts : mouse coordinate
8351 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8353 LVHITTESTINFO lvHitTestInfo;
8355 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8357 /* send NM_RELEASEDCAPTURE notification */
8358 notify(infoPtr, NM_RELEASEDCAPTURE);
8360 /* send NM_RDBLCLK notification */
8361 lvHitTestInfo.pt.x = pts.x;
8362 lvHitTestInfo.pt.y = pts.y;
8363 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8364 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8371 * Processes mouse down messages (right mouse button).
8374 * [I] infoPtr : valid pointer to the listview structure
8375 * [I] wKey : key flag
8376 * [I] pts : mouse coordinate
8381 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8383 LVHITTESTINFO lvHitTestInfo;
8386 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8388 /* send NM_RELEASEDCAPTURE notification */
8389 notify(infoPtr, NM_RELEASEDCAPTURE);
8391 /* make sure the listview control window has the focus */
8392 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8394 /* set right button down flag */
8395 infoPtr->bRButtonDown = TRUE;
8397 /* determine the index of the selected item */
8398 lvHitTestInfo.pt.x = pts.x;
8399 lvHitTestInfo.pt.y = pts.y;
8400 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8402 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8404 LISTVIEW_SetItemFocus(infoPtr, nItem);
8405 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8406 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8407 LISTVIEW_SetSelection(infoPtr, nItem);
8411 LISTVIEW_DeselectAll(infoPtr);
8419 * Processes mouse up messages (right mouse button).
8422 * [I] infoPtr : valid pointer to the listview structure
8423 * [I] wKey : key flag
8424 * [I] pts : mouse coordinate
8429 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8431 LVHITTESTINFO lvHitTestInfo;
8434 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8436 if (!infoPtr->bRButtonDown) return 0;
8438 /* set button flag */
8439 infoPtr->bRButtonDown = FALSE;
8441 /* Send NM_RClICK notification */
8442 lvHitTestInfo.pt.x = pts.x;
8443 lvHitTestInfo.pt.y = pts.y;
8444 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8445 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8447 /* Change to screen coordinate for WM_CONTEXTMENU */
8448 pt = lvHitTestInfo.pt;
8449 ClientToScreen(infoPtr->hwndSelf, &pt);
8451 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8452 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8453 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8464 * [I] infoPtr : valid pointer to the listview structure
8465 * [I] hwnd : window handle of window containing the cursor
8466 * [I] nHittest : hit-test code
8467 * [I] wMouseMsg : ideintifier of the mouse message
8470 * TRUE if cursor is set
8473 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8475 LVHITTESTINFO lvHitTestInfo;
8477 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8479 if(!infoPtr->hHotCursor) return FALSE;
8481 GetCursorPos(&lvHitTestInfo.pt);
8482 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8484 SetCursor(infoPtr->hHotCursor);
8494 * [I] infoPtr : valid pointer to the listview structure
8495 * [I] hwndLoseFocus : handle of previously focused window
8500 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8502 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8504 /* if we have the focus already, there's nothing to do */
8505 if (infoPtr->bFocus) return 0;
8507 /* send NM_SETFOCUS notification */
8508 notify(infoPtr, NM_SETFOCUS);
8510 /* set window focus flag */
8511 infoPtr->bFocus = TRUE;
8513 /* put the focus rect back on */
8514 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8516 /* redraw all visible selected items */
8517 LISTVIEW_InvalidateSelectedItems(infoPtr);
8527 * [I] infoPtr : valid pointer to the listview structure
8528 * [I] fRedraw : font handle
8529 * [I] fRedraw : redraw flag
8534 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8536 HFONT oldFont = infoPtr->hFont;
8538 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8540 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8541 if (infoPtr->hFont == oldFont) return 0;
8543 LISTVIEW_SaveTextMetrics(infoPtr);
8545 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8546 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8548 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8555 * Message handling for WM_SETREDRAW.
8556 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8559 * [I] infoPtr : valid pointer to the listview structure
8560 * [I] bRedraw: state of redraw flag
8563 * DefWinProc return value
8565 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8567 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8569 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8570 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8572 infoPtr->bRedraw = bRedraw;
8574 if(!bRedraw) return 0;
8576 if (is_autoarrange(infoPtr))
8577 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8578 LISTVIEW_UpdateScroll(infoPtr);
8580 /* despite what the WM_SETREDRAW docs says, apps expect us
8581 * to invalidate the listview here... stupid! */
8582 LISTVIEW_InvalidateList(infoPtr);
8589 * Resizes the listview control. This function processes WM_SIZE
8590 * messages. At this time, the width and height are not used.
8593 * [I] infoPtr : valid pointer to the listview structure
8594 * [I] Width : new width
8595 * [I] Height : new height
8600 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8602 RECT rcOld = infoPtr->rcList;
8604 TRACE("(width=%d, height=%d)\n", Width, Height);
8606 LISTVIEW_UpdateSize(infoPtr);
8607 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8609 /* do not bother with display related stuff if we're not redrawing */
8610 if (!is_redrawing(infoPtr)) return 0;
8612 if (is_autoarrange(infoPtr))
8613 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8615 LISTVIEW_UpdateScroll(infoPtr);
8617 /* refresh all only for lists whose height changed significantly */
8618 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8619 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8620 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8621 LISTVIEW_InvalidateList(infoPtr);
8628 * Sets the size information.
8631 * [I] infoPtr : valid pointer to the listview structure
8636 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8638 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8640 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8642 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8644 if (uView == LVS_LIST)
8646 /* Apparently the "LIST" style is supposed to have the same
8647 * number of items in a column even if there is no scroll bar.
8648 * Since if a scroll bar already exists then the bottom is already
8649 * reduced, only reduce if the scroll bar does not currently exist.
8650 * The "2" is there to mimic the native control. I think it may be
8651 * related to either padding or edges. (GLA 7/2002)
8653 if (!(infoPtr->dwStyle & WS_HSCROLL))
8654 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8655 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8657 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8662 hl.prc = &infoPtr->rcList;
8664 Header_Layout(infoPtr->hwndHeader, &hl);
8666 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8668 infoPtr->rcList.top = max(wp.cy, 0);
8671 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8676 * Processes WM_STYLECHANGED messages.
8679 * [I] infoPtr : valid pointer to the listview structure
8680 * [I] wStyleType : window style type (normal or extended)
8681 * [I] lpss : window style information
8686 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8687 const STYLESTRUCT *lpss)
8689 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8690 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8692 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8693 wStyleType, lpss->styleOld, lpss->styleNew);
8695 if (wStyleType != GWL_STYLE) return 0;
8697 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8698 /* what if LVS_OWNERDATA changed? */
8699 /* or LVS_SINGLESEL */
8700 /* or LVS_SORT{AS,DES}CENDING */
8702 infoPtr->dwStyle = lpss->styleNew;
8704 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8705 ((lpss->styleNew & WS_HSCROLL) == 0))
8706 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8708 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8709 ((lpss->styleNew & WS_VSCROLL) == 0))
8710 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8712 if (uNewView != uOldView)
8714 SIZE oldIconSize = infoPtr->iconSize;
8717 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8718 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8720 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8721 SetRectEmpty(&infoPtr->rcFocus);
8723 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8724 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8726 if (uNewView == LVS_ICON)
8728 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8730 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8731 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8732 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8735 else if (uNewView == LVS_REPORT)
8740 hl.prc = &infoPtr->rcList;
8742 Header_Layout(infoPtr->hwndHeader, &hl);
8743 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8746 LISTVIEW_UpdateItemSize(infoPtr);
8749 if (uNewView == LVS_REPORT)
8750 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8752 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8753 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8754 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8756 /* update the size of the client area */
8757 LISTVIEW_UpdateSize(infoPtr);
8759 /* add scrollbars if needed */
8760 LISTVIEW_UpdateScroll(infoPtr);
8762 /* invalidate client area + erase background */
8763 LISTVIEW_InvalidateList(infoPtr);
8770 * Window procedure of the listview control.
8773 static LRESULT WINAPI
8774 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8776 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8778 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8780 if (!infoPtr && (uMsg != WM_CREATE))
8781 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8785 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8790 case LVM_APPROXIMATEVIEWRECT:
8791 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8792 LOWORD(lParam), HIWORD(lParam));
8794 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8796 /* case LVM_CANCELEDITLABEL: */
8798 case LVM_CREATEDRAGIMAGE:
8799 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
8801 case LVM_DELETEALLITEMS:
8802 return LISTVIEW_DeleteAllItems(infoPtr);
8804 case LVM_DELETECOLUMN:
8805 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8807 case LVM_DELETEITEM:
8808 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8810 case LVM_EDITLABELW:
8811 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8813 case LVM_EDITLABELA:
8814 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8816 /* case LVM_ENABLEGROUPVIEW: */
8818 case LVM_ENSUREVISIBLE:
8819 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8822 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8825 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8827 case LVM_GETBKCOLOR:
8828 return infoPtr->clrBk;
8830 /* case LVM_GETBKIMAGE: */
8832 case LVM_GETCALLBACKMASK:
8833 return infoPtr->uCallbackMask;
8835 case LVM_GETCOLUMNA:
8836 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8838 case LVM_GETCOLUMNW:
8839 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8841 case LVM_GETCOLUMNORDERARRAY:
8842 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8844 case LVM_GETCOLUMNWIDTH:
8845 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8847 case LVM_GETCOUNTPERPAGE:
8848 return LISTVIEW_GetCountPerPage(infoPtr);
8850 case LVM_GETEDITCONTROL:
8851 return (LRESULT)infoPtr->hwndEdit;
8853 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8854 return infoPtr->dwLvExStyle;
8856 /* case LVM_GETGROUPINFO: */
8858 /* case LVM_GETGROUPMETRICS: */
8861 return (LRESULT)infoPtr->hwndHeader;
8863 case LVM_GETHOTCURSOR:
8864 return (LRESULT)infoPtr->hHotCursor;
8866 case LVM_GETHOTITEM:
8867 return infoPtr->nHotItem;
8869 case LVM_GETHOVERTIME:
8870 return infoPtr->dwHoverTime;
8872 case LVM_GETIMAGELIST:
8873 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8875 /* case LVM_GETINSERTMARK: */
8877 /* case LVM_GETINSERTMARKCOLOR: */
8879 /* case LVM_GETINSERTMARKRECT: */
8881 case LVM_GETISEARCHSTRINGA:
8882 case LVM_GETISEARCHSTRINGW:
8883 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8887 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8890 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8892 case LVM_GETITEMCOUNT:
8893 return infoPtr->nItemCount;
8895 case LVM_GETITEMPOSITION:
8896 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8898 case LVM_GETITEMRECT:
8899 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8901 case LVM_GETITEMSPACING:
8902 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8904 case LVM_GETITEMSTATE:
8905 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8907 case LVM_GETITEMTEXTA:
8908 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8910 case LVM_GETITEMTEXTW:
8911 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8913 case LVM_GETNEXTITEM:
8914 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8916 case LVM_GETNUMBEROFWORKAREAS:
8917 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8921 if (!lParam) return FALSE;
8922 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8925 /* case LVM_GETOUTLINECOLOR: */
8927 /* case LVM_GETSELECTEDCOLUMN: */
8929 case LVM_GETSELECTEDCOUNT:
8930 return LISTVIEW_GetSelectedCount(infoPtr);
8932 case LVM_GETSELECTIONMARK:
8933 return infoPtr->nSelectionMark;
8935 case LVM_GETSTRINGWIDTHA:
8936 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8938 case LVM_GETSTRINGWIDTHW:
8939 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8941 case LVM_GETSUBITEMRECT:
8942 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8944 case LVM_GETTEXTBKCOLOR:
8945 return infoPtr->clrTextBk;
8947 case LVM_GETTEXTCOLOR:
8948 return infoPtr->clrText;
8950 /* case LVM_GETTILEINFO: */
8952 /* case LVM_GETTILEVIEWINFO: */
8954 case LVM_GETTOOLTIPS:
8955 return (LRESULT)infoPtr->hwndToolTip;
8957 case LVM_GETTOPINDEX:
8958 return LISTVIEW_GetTopIndex(infoPtr);
8960 /*case LVM_GETUNICODEFORMAT:
8961 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8964 /* case LVM_GETVIEW: */
8966 case LVM_GETVIEWRECT:
8967 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8969 case LVM_GETWORKAREAS:
8970 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8973 /* case LVM_HASGROUP: */
8976 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8978 case LVM_INSERTCOLUMNA:
8979 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8981 case LVM_INSERTCOLUMNW:
8982 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8984 /* case LVM_INSERTGROUP: */
8986 /* case LVM_INSERTGROUPSORTED: */
8988 case LVM_INSERTITEMA:
8989 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8991 case LVM_INSERTITEMW:
8992 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8994 /* case LVM_INSERTMARKHITTEST: */
8996 /* case LVM_ISGROUPVIEWENABLED: */
8998 /* case LVM_MAPIDTOINDEX: */
9000 /* case LVM_MAPINDEXTOID: */
9002 /* case LVM_MOVEGROUP: */
9004 /* case LVM_MOVEITEMTOGROUP: */
9006 case LVM_REDRAWITEMS:
9007 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9009 /* case LVM_REMOVEALLGROUPS: */
9011 /* case LVM_REMOVEGROUP: */
9014 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9016 case LVM_SETBKCOLOR:
9017 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9019 /* case LVM_SETBKIMAGE: */
9021 case LVM_SETCALLBACKMASK:
9022 infoPtr->uCallbackMask = (UINT)wParam;
9025 case LVM_SETCOLUMNA:
9026 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9028 case LVM_SETCOLUMNW:
9029 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9031 case LVM_SETCOLUMNORDERARRAY:
9032 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9034 case LVM_SETCOLUMNWIDTH:
9035 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9037 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9038 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9040 /* case LVM_SETGROUPINFO: */
9042 /* case LVM_SETGROUPMETRICS: */
9044 case LVM_SETHOTCURSOR:
9045 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9047 case LVM_SETHOTITEM:
9048 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9050 case LVM_SETHOVERTIME:
9051 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9053 case LVM_SETICONSPACING:
9054 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9056 case LVM_SETIMAGELIST:
9057 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9059 /* case LVM_SETINFOTIP: */
9061 /* case LVM_SETINSERTMARK: */
9063 /* case LVM_SETINSERTMARKCOLOR: */
9066 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9069 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9071 case LVM_SETITEMCOUNT:
9072 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9074 case LVM_SETITEMPOSITION:
9077 pt.x = (short)LOWORD(lParam);
9078 pt.y = (short)HIWORD(lParam);
9079 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9082 case LVM_SETITEMPOSITION32:
9083 if (lParam == 0) return FALSE;
9084 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9086 case LVM_SETITEMSTATE:
9087 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9089 case LVM_SETITEMTEXTA:
9090 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9092 case LVM_SETITEMTEXTW:
9093 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9095 /* case LVM_SETOUTLINECOLOR: */
9097 /* case LVM_SETSELECTEDCOLUMN: */
9099 case LVM_SETSELECTIONMARK:
9100 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9102 case LVM_SETTEXTBKCOLOR:
9103 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9105 case LVM_SETTEXTCOLOR:
9106 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9108 /* case LVM_SETTILEINFO: */
9110 /* case LVM_SETTILEVIEWINFO: */
9112 /* case LVM_SETTILEWIDTH: */
9114 case LVM_SETTOOLTIPS:
9115 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9117 /* case LVM_SETUNICODEFORMAT: */
9119 /* case LVM_SETVIEW: */
9121 /* case LVM_SETWORKAREAS: */
9123 /* case LVM_SORTGROUPS: */
9126 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9128 /* LVM_SORTITEMSEX: */
9130 case LVM_SUBITEMHITTEST:
9131 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9134 return LISTVIEW_Update(infoPtr, (INT)wParam);
9137 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9140 return LISTVIEW_Command(infoPtr, wParam, lParam);
9143 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9146 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9149 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9152 return (LRESULT)infoPtr->hFont;
9155 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9158 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9161 return LISTVIEW_KillFocus(infoPtr);
9163 case WM_LBUTTONDBLCLK:
9164 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9166 case WM_LBUTTONDOWN:
9167 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9170 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9173 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9176 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9179 return LISTVIEW_NCDestroy(infoPtr);
9182 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9183 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9186 case WM_NOTIFYFORMAT:
9187 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9190 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9192 case WM_RBUTTONDBLCLK:
9193 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9195 case WM_RBUTTONDOWN:
9196 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9199 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9202 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9207 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9210 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9213 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9216 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9218 case WM_STYLECHANGED:
9219 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9221 case WM_SYSCOLORCHANGE:
9222 COMCTL32_RefreshSysColors();
9225 /* case WM_TIMER: */
9228 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9231 if (wParam & (MK_SHIFT | MK_CONTROL))
9232 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9233 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9235 case WM_WINDOWPOSCHANGED:
9236 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9238 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9239 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9240 LISTVIEW_UpdateSize(infoPtr);
9241 LISTVIEW_UpdateScroll(infoPtr);
9243 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9245 /* case WM_WININICHANGE: */
9248 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9249 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9252 /* call default window procedure */
9253 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9261 * Registers the window class.
9269 void LISTVIEW_Register(void)
9273 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9274 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9275 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9276 wndClass.cbClsExtra = 0;
9277 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9278 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9279 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9280 wndClass.lpszClassName = WC_LISTVIEWW;
9281 RegisterClassW(&wndClass);
9286 * Unregisters the window class.
9294 void LISTVIEW_Unregister(void)
9296 UnregisterClassW(WC_LISTVIEWW, NULL);
9301 * Handle any WM_COMMAND messages
9304 * [I] infoPtr : valid pointer to the listview structure
9305 * [I] wParam : the first message parameter
9306 * [I] lParam : the second message parameter
9311 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9313 switch (HIWORD(wParam))
9318 * Adjust the edit window size
9321 HDC hdc = GetDC(infoPtr->hwndEdit);
9322 HFONT hFont, hOldFont = 0;
9327 if (!infoPtr->hwndEdit || !hdc) return 0;
9328 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9329 GetWindowRect(infoPtr->hwndEdit, &rect);
9331 /* Select font to get the right dimension of the string */
9332 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9335 hOldFont = SelectObject(hdc, hFont);
9338 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9340 TEXTMETRICW textMetric;
9342 /* Add Extra spacing for the next character */
9343 GetTextMetricsW(hdc, &textMetric);
9344 sz.cx += (textMetric.tmMaxCharWidth * 2);
9352 rect.bottom - rect.top,
9353 SWP_DRAWFRAME|SWP_NOMOVE);
9356 SelectObject(hdc, hOldFont);
9358 ReleaseDC(infoPtr->hwndSelf, hdc);
9364 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9373 * Subclassed edit control windproc function
9376 * [I] hwnd : the edit window handle
9377 * [I] uMsg : the message that is to be processed
9378 * [I] wParam : first message parameter
9379 * [I] lParam : second message parameter
9380 * [I] isW : TRUE if input is Unicode
9385 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9387 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9388 BOOL cancel = FALSE;
9390 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9391 hwnd, uMsg, wParam, lParam, isW);
9396 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9403 WNDPROC editProc = infoPtr->EditWndProc;
9404 infoPtr->EditWndProc = 0;
9405 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9406 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9410 if (VK_ESCAPE == (INT)wParam)
9415 else if (VK_RETURN == (INT)wParam)
9419 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9423 if (infoPtr->hwndEdit)
9425 LPWSTR buffer = NULL;
9427 infoPtr->hwndEdit = 0;
9430 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9434 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9436 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9437 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9441 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9443 if (buffer) Free(buffer);
9447 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9453 * Subclassed edit control Unicode windproc function
9456 * [I] hwnd : the edit window handle
9457 * [I] uMsg : the message that is to be processed
9458 * [I] wParam : first message parameter
9459 * [I] lParam : second message parameter
9463 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9465 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9470 * Subclassed edit control ANSI windproc function
9473 * [I] hwnd : the edit window handle
9474 * [I] uMsg : the message that is to be processed
9475 * [I] wParam : first message parameter
9476 * [I] lParam : second message parameter
9480 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9482 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9487 * Creates a subclassed edit cotrol
9490 * [I] infoPtr : valid pointer to the listview structure
9491 * [I] text : initial text for the edit
9492 * [I] style : the window style
9493 * [I] isW : TRUE if input is Unicode
9497 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9498 INT x, INT y, INT width, INT height, BOOL isW)
9500 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9505 TEXTMETRICW textMetric;
9506 HINSTANCE hinst = (HINSTANCE)GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9508 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9510 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9511 hdc = GetDC(infoPtr->hwndSelf);
9513 /* Select the font to get appropriate metric dimensions */
9514 if(infoPtr->hFont != 0)
9515 hOldFont = SelectObject(hdc, infoPtr->hFont);
9517 /*Get String Length in pixels */
9518 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9520 /*Add Extra spacing for the next character */
9521 GetTextMetricsW(hdc, &textMetric);
9522 sz.cx += (textMetric.tmMaxCharWidth * 2);
9524 if(infoPtr->hFont != 0)
9525 SelectObject(hdc, hOldFont);
9527 ReleaseDC(infoPtr->hwndSelf, hdc);
9529 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9531 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9533 if (!hedit) return 0;
9535 infoPtr->EditWndProc = (WNDPROC)
9536 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9537 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9539 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);