4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 Codeweavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on Oct. 21, 2002, by Dimitrie O. Paun.
29 * Unless otherwise noted, we belive this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
36 * -- Hot item handling, mouse hovering
37 * -- Workareas support
42 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
43 * -- Support CustonDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs.
44 * -- in LISTVIEW_AddGroupSelection, se whould send LVN_ODSTATECHANGED
45 * -- LVA_SNAPTOGRID not implemented
46 * -- LISTVIEW_ApproximateViewRect partially implemented
47 * -- LISTVIEW_[GS]etColumnOrderArray stubs
48 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
49 * -- LISTVIEW_SetIconSpacing is incomplete
50 * -- LISTVIEW_SortItems is broken
51 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
54 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
55 * linear in the number of items in the list, and this is
56 * unacceptable for large lists.
57 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
58 * instead of inserting in the right spot
59 * -- we should keep an ordered array of coordinates in iconic mode
60 * this would allow to frame items (iterator_frameditems),
61 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
69 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
76 * -- LVS_NOSCROLL (see Q137520)
77 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
80 * -- LVS_EX_BORDERSELECT
81 * -- LVS_EX_CHECKBOXES
84 * -- LVS_EX_HEADERDRAGDROP
87 * -- LVS_EX_MULTIWORKAREAS
88 * -- LVS_EX_ONECLICKACTIVATE
90 * -- LVS_EX_SIMPLESELECT
91 * -- LVS_EX_SUBITEMIMAGES
92 * -- LVS_EX_TRACKSELECT
93 * -- LVS_EX_TWOCLICKACTIVATE
94 * -- LVS_EX_UNDERLINECOLD
95 * -- LVS_EX_UNDERLINEHOT
98 * -- LVN_BEGINDRAG, LVN_BEGINRDRAG
99 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
102 * -- LVN_MARQUEEBEGIN
104 * -- LVN_ODSTATECHANGED
109 * -- LVM_CANCELEDITLABEL
110 * -- LVM_CREATEDRAGIMAGE
111 * -- LVM_ENABLEGROUPVIEW
112 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
113 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
114 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
115 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
116 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
117 * -- LVM_GETINSERTMARKRECT
118 * -- LVM_GETNUMBEROFWORKAREAS
119 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
120 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
121 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
122 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
123 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
124 * -- LVM_GETTOOLTIPS, LVM_SETTOOLTIPS
125 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
126 * -- LVM_GETVIEW, LVM_SETVIEW
127 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
128 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
129 * -- LVM_INSERTGROUPSORTED
130 * -- LVM_INSERTMARKHITTEST
131 * -- LVM_ISGROUPVIEWENABLED
132 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
134 * -- LVM_MOVEITEMTOGROUP
136 * -- LVM_SETTILEWIDTH
140 * Known differences in message stream from native control (not known if
141 * these differences cause problems):
142 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
143 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
144 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
145 * processing for "USEDOUBLECLICKTIME".
149 #include "wine/port.h"
159 #include "commctrl.h"
160 #include "comctl32.h"
162 #include "wine/debug.h"
163 #include "wine/unicode.h"
165 WINE_DEFAULT_DEBUG_CHANNEL(listview);
167 /* make sure you set this to 0 for production use! */
168 #define DEBUG_RANGES 1
170 typedef struct tagCOLUMN_INFO
172 RECT rcHeader; /* tracks the header's rectangle */
173 int fmt; /* same as LVCOLUMN.fmt */
176 typedef struct tagITEMHDR
180 } ITEMHDR, *LPITEMHDR;
182 typedef struct tagSUBITEM_INFO
188 typedef struct tagITEM_INFO
196 typedef struct tagRANGE
202 typedef struct tagRANGES
207 typedef struct tagITERATOR
216 typedef struct tagLISTVIEW_INFO
223 COLORREF clrTextBkDefault;
224 HIMAGELIST himlNormal;
225 HIMAGELIST himlSmall;
226 HIMAGELIST himlState;
229 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
232 RANGES selectionRanges;
236 RECT rcList; /* This rectangle is really the window
237 * client rectangle possibly reduced by the
238 * horizontal scroll bar and/or header - see
239 * LISTVIEW_UpdateSize. This rectangle offset
240 * by the LISTVIEW_GetOrigin value is in
241 * client coordinates */
250 INT ntmHeight; /* Some cached metrics of the font used */
251 INT ntmAveCharWidth; /* by the listview to draw items */
252 BOOL bRedraw; /* Turns on/off repaints & invalidations */
253 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
255 BOOL bDoChangeNotify; /* send change notification messages? */
258 DWORD dwStyle; /* the cached window GWL_STYLE */
259 DWORD dwLvExStyle; /* extended listview style */
260 INT nItemCount; /* the number of items in the list */
261 HDPA hdpaItems; /* array ITEM_INFO pointers */
262 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
263 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
264 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
265 POINT currIconPos; /* this is the position next icon will be placed */
266 PFNLVCOMPARE pfnCompare;
274 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
276 DWORD lastKeyPressTimestamp;
278 INT nSearchParamLength;
279 WCHAR szSearchParam[ MAX_PATH ];
286 /* How many we debug buffer to allocate */
287 #define DEBUG_BUFFERS 20
288 /* The size of a single debug bbuffer */
289 #define DEBUG_BUFFER_SIZE 256
291 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
292 #define SB_INTERNAL -1
294 /* maximum size of a label */
295 #define DISP_TEXT_SIZE 512
297 /* padding for items in list and small icon display modes */
298 #define WIDTH_PADDING 12
300 /* padding for items in list, report and small icon display modes */
301 #define HEIGHT_PADDING 1
303 /* offset of items in report display mode */
304 #define REPORT_MARGINX 2
306 /* padding for icon in large icon display mode
307 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
308 * that HITTEST will see.
309 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
310 * ICON_TOP_PADDING - sum of the two above.
311 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
312 * LABEL_HOR_PADDING - between text and sides of box
313 * LABEL_VERT_PADDING - between bottom of text and end of box
315 * ICON_LR_PADDING - additional width above icon size.
316 * ICON_LR_HALF - half of the above value
318 #define ICON_TOP_PADDING_NOTHITABLE 2
319 #define ICON_TOP_PADDING_HITABLE 2
320 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
321 #define ICON_BOTTOM_PADDING 4
322 #define LABEL_HOR_PADDING 5
323 #define LABEL_VERT_PADDING 7
324 #define ICON_LR_PADDING 16
325 #define ICON_LR_HALF (ICON_LR_PADDING/2)
327 /* default label width for items in list and small icon display modes */
328 #define DEFAULT_LABEL_WIDTH 40
330 /* default column width for items in list display mode */
331 #define DEFAULT_COLUMN_WIDTH 128
333 /* Size of "line" scroll for V & H scrolls */
334 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
336 /* Padding betwen image and label */
337 #define IMAGE_PADDING 2
339 /* Padding behind the label */
340 #define TRAILING_LABEL_PADDING 12
341 #define TRAILING_HEADER_PADDING 11
343 /* Border for the icon caption */
344 #define CAPTION_BORDER 2
346 /* Standard DrawText flags */
347 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
348 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
349 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
351 /* The time in milliseconds to reset the search in the list */
352 #define KEY_DELAY 450
354 /* Dump the LISTVIEW_INFO structure to the debug channel */
355 #define LISTVIEW_DUMP(iP) do { \
356 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
357 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
358 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
359 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
360 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
361 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
362 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
363 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
364 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
365 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
369 * forward declarations
371 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
372 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
373 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
374 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
375 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
376 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
377 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
378 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
379 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
380 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
381 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
382 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
383 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
384 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
385 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
386 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
387 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
388 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
389 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
390 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
391 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
392 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
393 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
394 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
395 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
397 /******** Text handling functions *************************************/
399 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
400 * text string. The string may be ANSI or Unicode, in which case
401 * the boolean isW tells us the type of the string.
403 * The name of the function tell what type of strings it expects:
404 * W: Unicode, T: ANSI/Unicode - function of isW
407 static inline BOOL is_textW(LPCWSTR text)
409 return text != NULL && text != LPSTR_TEXTCALLBACKW;
412 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
414 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
415 return is_textW(text);
418 static inline int textlenT(LPCWSTR text, BOOL isW)
420 return !is_textT(text, isW) ? 0 :
421 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
424 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
427 if (isSrcW) lstrcpynW(dest, src, max);
428 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
430 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
431 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
434 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
436 LPWSTR wstr = (LPWSTR)text;
438 if (!isW && is_textT(text, isW))
440 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
441 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
442 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
444 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
448 static inline void textfreeT(LPWSTR wstr, BOOL isW)
450 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
454 * dest is a pointer to a Unicode string
455 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
457 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
461 if (src == LPSTR_TEXTCALLBACKW)
463 if (is_textW(*dest)) COMCTL32_Free(*dest);
464 *dest = LPSTR_TEXTCALLBACKW;
468 LPWSTR pszText = textdupTtoW(src, isW);
469 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
470 bResult = Str_SetPtrW(dest, pszText);
471 textfreeT(pszText, isW);
477 * compares a Unicode to a Unicode/ANSI text string
479 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
481 if (!aw) return bt ? -1 : 0;
482 if (!bt) return aw ? 1 : 0;
483 if (aw == LPSTR_TEXTCALLBACKW)
484 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
485 if (bt != LPSTR_TEXTCALLBACKW)
487 LPWSTR bw = textdupTtoW(bt, isW);
488 int r = bw ? lstrcmpW(aw, bw) : 1;
496 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
500 n = min(min(n, strlenW(s1)), strlenW(s2));
501 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
502 return res ? res - sizeof(WCHAR) : res;
505 /******** Debugging functions *****************************************/
507 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
509 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
510 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
513 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
515 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
516 n = min(textlenT(text, isW), n);
517 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
520 static char* debug_getbuf()
522 static int index = 0;
523 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
524 return buffers[index++ % DEBUG_BUFFERS];
527 static inline char* debugrange(const RANGE *lprng)
531 char* buf = debug_getbuf();
532 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
534 } else return "(null)";
537 static inline char* debugpoint(const POINT *lppt)
541 char* buf = debug_getbuf();
542 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
544 } else return "(null)";
547 static inline char* debugrect(const RECT *rect)
551 char* buf = debug_getbuf();
552 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
553 rect->left, rect->top, rect->right, rect->bottom);
555 } else return "(null)";
558 static char * debugscrollinfo(const SCROLLINFO *pScrollInfo)
560 char* buf = debug_getbuf(), *text = buf;
561 int len, size = DEBUG_BUFFER_SIZE;
563 if (pScrollInfo == NULL) return "(null)";
564 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
565 if (len == -1) goto end; buf += len; size -= len;
566 if (pScrollInfo->fMask & SIF_RANGE)
567 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
569 if (len == -1) goto end; buf += len; size -= len;
570 if (pScrollInfo->fMask & SIF_PAGE)
571 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
573 if (len == -1) goto end; buf += len; size -= len;
574 if (pScrollInfo->fMask & SIF_POS)
575 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
577 if (len == -1) goto end; buf += len; size -= len;
578 if (pScrollInfo->fMask & SIF_TRACKPOS)
579 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
581 if (len == -1) goto end; buf += len; size -= len;
584 buf = text + strlen(text);
586 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
590 static char* debugnmlistview(const NMLISTVIEW *plvnm)
594 char* buf = debug_getbuf();
595 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
596 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
597 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
598 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
600 } else return "(null)";
603 static char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
605 char* buf = debug_getbuf(), *text = buf;
606 int len, size = DEBUG_BUFFER_SIZE;
608 if (lpLVItem == NULL) return "(null)";
609 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
610 if (len == -1) goto end; buf += len; size -= len;
611 if (lpLVItem->mask & LVIF_STATE)
612 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
614 if (len == -1) goto end; buf += len; size -= len;
615 if (lpLVItem->mask & LVIF_TEXT)
616 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
618 if (len == -1) goto end; buf += len; size -= len;
619 if (lpLVItem->mask & LVIF_IMAGE)
620 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
622 if (len == -1) goto end; buf += len; size -= len;
623 if (lpLVItem->mask & LVIF_PARAM)
624 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
626 if (len == -1) goto end; buf += len; size -= len;
627 if (lpLVItem->mask & LVIF_INDENT)
628 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
630 if (len == -1) goto end; buf += len; size -= len;
633 buf = text + strlen(text);
635 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
639 static char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
641 char* buf = debug_getbuf(), *text = buf;
642 int len, size = DEBUG_BUFFER_SIZE;
644 if (lpColumn == NULL) return "(null)";
645 len = snprintf(buf, size, "{");
646 if (len == -1) goto end; buf += len; size -= len;
647 if (lpColumn->mask & LVCF_SUBITEM)
648 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
650 if (len == -1) goto end; buf += len; size -= len;
651 if (lpColumn->mask & LVCF_FMT)
652 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
654 if (len == -1) goto end; buf += len; size -= len;
655 if (lpColumn->mask & LVCF_WIDTH)
656 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
658 if (len == -1) goto end; buf += len; size -= len;
659 if (lpColumn->mask & LVCF_TEXT)
660 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
662 if (len == -1) goto end; buf += len; size -= len;
663 if (lpColumn->mask & LVCF_IMAGE)
664 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
666 if (len == -1) goto end; buf += len; size -= len;
667 if (lpColumn->mask & LVCF_ORDER)
668 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
670 if (len == -1) goto end; buf += len; size -= len;
673 buf = text + strlen(text);
675 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
679 static char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
683 char* buf = debug_getbuf();
684 snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
685 debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
687 } else return "(null)";
690 /* Return the corresponding text for a given scroll value */
691 static inline LPCSTR debugscrollcode(int nScrollCode)
695 case SB_LINELEFT: return "SB_LINELEFT";
696 case SB_LINERIGHT: return "SB_LINERIGHT";
697 case SB_PAGELEFT: return "SB_PAGELEFT";
698 case SB_PAGERIGHT: return "SB_PAGERIGHT";
699 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
700 case SB_THUMBTRACK: return "SB_THUMBTRACK";
701 case SB_ENDSCROLL: return "SB_ENDSCROLL";
702 case SB_INTERNAL: return "SB_INTERNAL";
703 default: return "unknown";
708 /******** Notification functions i************************************/
710 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
714 TRACE("(code=%d)\n", code);
716 pnmh->hwndFrom = infoPtr->hwndSelf;
717 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
719 result = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
720 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
722 TRACE(" <= %ld\n", result);
727 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
730 return notify_hdr(infoPtr, code, &nmh);
733 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
735 notify(infoPtr, LVN_ITEMACTIVATE);
738 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
740 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
741 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
744 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
749 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
750 ZeroMemory(&nmlv, sizeof(nmlv));
751 nmlv.iItem = lvht->iItem;
752 nmlv.iSubItem = lvht->iSubItem;
753 nmlv.ptAction = lvht->pt;
754 item.mask = LVIF_PARAM;
755 item.iItem = lvht->iItem;
757 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
758 return notify_listview(infoPtr, code, &nmlv);
761 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
766 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
768 item.mask = LVIF_PARAM;
771 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
772 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
775 static int get_ansi_notification(INT unicodeNotificationCode)
777 switch (unicodeNotificationCode)
779 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
780 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
781 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
782 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
783 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
784 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
786 ERR("unknown notification %x\n", unicodeNotificationCode);
792 With testing on Windows 2000 it looks like the notify format
793 has nothing to do with this message. It ALWAYS seems to be
796 infoPtr : listview struct
797 notificationCode : *Unicode* notification code
798 pdi : dispinfo structure (can be unicode or ansi)
799 isW : TRUE if dispinfo is Unicode
801 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
803 BOOL bResult = FALSE;
804 BOOL convertToAnsi = FALSE;
805 INT cchTempBufMax = 0, savCchTextMax = 0;
806 LPWSTR pszTempBuf = NULL, savPszText = NULL;
808 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
813 if (notificationCode != LVN_GETDISPINFOW)
815 cchTempBufMax = WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText,
816 -1, NULL, 0, NULL, NULL);
820 cchTempBufMax = pdi->item.cchTextMax;
821 *pdi->item.pszText = 0; /* make sure we don't process garbage */
824 pszTempBuf = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) *
826 if (!pszTempBuf) return FALSE;
828 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR)
829 pszTempBuf, cchTempBufMax, NULL, NULL);
831 savCchTextMax = pdi->item.cchTextMax;
832 savPszText = pdi->item.pszText;
833 pdi->item.pszText = pszTempBuf;
834 pdi->item.cchTextMax = cchTempBufMax;
837 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat !=
840 bResult = notify_hdr(infoPtr, get_ansi_notification(notificationCode),
845 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
846 savPszText, savCchTextMax);
847 pdi->item.pszText = savPszText; /* restores our buffer */
848 pdi->item.cchTextMax = savCchTextMax;
849 HeapFree(GetProcessHeap(), 0, pszTempBuf);
854 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
855 const RECT *rcBounds, const LVITEMW *lplvItem)
857 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
858 lpnmlvcd->nmcd.hdc = hdc;
859 lpnmlvcd->nmcd.rc = *rcBounds;
860 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
861 lpnmlvcd->clrText = infoPtr->clrText;
862 if (!lplvItem) return;
863 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
864 lpnmlvcd->iSubItem = lplvItem->iSubItem;
865 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
866 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
867 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
868 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
871 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
873 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
876 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
877 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
878 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
879 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
880 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
881 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
885 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
887 /* apprently, for selected items, we have to override the returned values */
888 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
892 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
893 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
895 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
897 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
898 lpnmlvcd->clrText = comctl32_color.clrBtnText;
902 /* Set the text attributes */
903 if (lpnmlvcd->clrTextBk != CLR_NONE)
905 SetBkMode(hdc, OPAQUE);
906 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
907 SetBkColor(hdc, infoPtr->clrTextBkDefault);
909 SetBkColor(hdc,lpnmlvcd->clrTextBk);
912 SetBkMode(hdc, TRANSPARENT);
913 SetTextColor(hdc, lpnmlvcd->clrText);
916 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
918 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
921 /******** Item iterator functions **********************************/
923 static RANGES ranges_create(int count);
924 static void ranges_destroy(RANGES ranges);
925 static BOOL ranges_add(RANGES ranges, RANGE range);
926 static BOOL ranges_del(RANGES ranges, RANGE range);
927 static void ranges_dump(RANGES ranges);
929 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
931 RANGE range = { nItem, nItem + 1 };
933 return ranges_add(ranges, range);
936 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
938 RANGE range = { nItem, nItem + 1 };
940 return ranges_del(ranges, range);
944 * ITERATOR DOCUMENTATION
946 * The iterator functions allow for easy, and convenient iteration
947 * over items of iterest in the list. Typically, you create a
948 * iterator, use it, and destroy it, as such:
951 * iterator_xxxitems(&i, ...);
952 * while (iterator_{prev,next}(&i)
954 * //code which uses i.nItem
956 * iterator_destroy(&i);
958 * where xxx is either: framed, or visible.
959 * Note that it is important that the code destroys the iterator
960 * after it's done with it, as the creation of the iterator may
961 * allocate memory, which thus needs to be freed.
963 * You can iterate both forwards, and backwards through the list,
964 * by using iterator_next or iterator_prev respectively.
966 * Lower numbered items are draw on top of higher number items in
967 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
968 * items may overlap). So, to test items, you should use
970 * which lists the items top to bottom (in Z-order).
971 * For drawing items, you should use
973 * which lists the items bottom to top (in Z-order).
974 * If you keep iterating over the items after the end-of-items
975 * marker (-1) is returned, the iterator will start from the
976 * beginning. Typically, you don't need to test for -1,
977 * because iterator_{next,prev} will return TRUE if more items
978 * are to be iterated over, or FALSE otherwise.
980 * Note: the iterator is defined to be bidirectional. That is,
981 * any number of prev followed by any number of next, or
982 * five versa, should leave the iterator at the same item:
983 * prev * n, next * n = next * n, prev * n
985 * The iterator has a notion of a out-of-order, special item,
986 * which sits at the start of the list. This is used in
987 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
988 * which needs to be first, as it may overlap other items.
990 * The code is a bit messy because we have:
991 * - a special item to deal with
992 * - simple range, or composite range
994 * If you find bugs, or want to add features, please make sure you
995 * always check/modify *both* iterator_prev, and iterator_next.
999 * This function iterates through the items in increasing order,
1000 * but prefixed by the special item, then -1. That is:
1001 * special, 1, 2, 3, ..., n, -1.
1002 * Each item is listed only once.
1004 static inline BOOL iterator_next(ITERATOR* i)
1008 i->nItem = i->nSpecial;
1009 if (i->nItem != -1) return TRUE;
1011 if (i->nItem == i->nSpecial)
1013 if (i->ranges) i->index = 0;
1019 if (i->nItem == i->nSpecial) i->nItem++;
1020 if (i->nItem < i->range.upper) return TRUE;
1025 if (i->index < i->ranges->hdpa->nItemCount)
1026 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1029 else if (i->nItem >= i->range.upper) goto end;
1031 i->nItem = i->range.lower;
1032 if (i->nItem >= 0) goto testitem;
1039 * This function iterates through the items in decreasing order,
1040 * followed by the special item, then -1. That is:
1041 * n, n-1, ..., 3, 2, 1, special, -1.
1042 * Each item is listed only once.
1044 static inline BOOL iterator_prev(ITERATOR* i)
1051 if (i->ranges) i->index = i->ranges->hdpa->nItemCount;
1054 if (i->nItem == i->nSpecial)
1062 if (i->nItem == i->nSpecial) i->nItem--;
1063 if (i->nItem >= i->range.lower) return TRUE;
1069 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1072 else if (!start && i->nItem < i->range.lower) goto end;
1074 i->nItem = i->range.upper;
1075 if (i->nItem > 0) goto testitem;
1077 return (i->nItem = i->nSpecial) != -1;
1080 static RANGE iterator_range(ITERATOR* i)
1084 if (!i->ranges) return i->range;
1086 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1087 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->ranges->hdpa->nItemCount - 1)).upper;
1092 * Releases resources associated with this ierator.
1094 static inline void iterator_destroy(ITERATOR* i)
1096 ranges_destroy(i->ranges);
1100 * Create an empty iterator.
1102 static inline BOOL iterator_empty(ITERATOR* i)
1104 ZeroMemory(i, sizeof(*i));
1105 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1110 * Create an iterator over a range.
1112 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1120 * Create an iterator over a bunch of ranges.
1121 * Please note that the iterator will take ownership of the ranges,
1122 * and will free them upon destruction.
1124 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1132 * Creates an iterator over the items which intersect lprc.
1134 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1136 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1137 RECT frame = *lprc, rcItem, rcTemp;
1140 /* in case we fail, we want to return an empty iterator */
1141 if (!iterator_empty(i)) return FALSE;
1143 LISTVIEW_GetOrigin(infoPtr, &Origin);
1145 TRACE("(lprc=%s)\n", debugrect(lprc));
1146 OffsetRect(&frame, -Origin.x, -Origin.y);
1148 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1152 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1154 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1155 if (IntersectRect(&rcTemp, &rcItem, lprc))
1156 i->nSpecial = infoPtr->nFocusedItem;
1158 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1159 /* to do better here, we need to have PosX, and PosY sorted */
1160 TRACE("building icon ranges:\n");
1161 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1163 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1164 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1165 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1166 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1167 if (IntersectRect(&rcTemp, &rcItem, &frame))
1168 ranges_additem(i->ranges, nItem);
1172 else if (uView == LVS_REPORT)
1176 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1177 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1179 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1180 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1181 if (range.upper <= range.lower) return TRUE;
1182 if (!iterator_rangeitems(i, range)) return FALSE;
1183 TRACE(" report=%s\n", debugrange(&i->range));
1187 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1188 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1189 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1190 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1191 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1192 INT lower = nFirstCol * nPerCol + nFirstRow;
1196 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1197 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1199 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1201 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1202 TRACE("building list ranges:\n");
1203 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1205 item_range.lower = nCol * nPerCol + nFirstRow;
1206 if(item_range.lower >= infoPtr->nItemCount) break;
1207 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1208 TRACE(" list=%s\n", debugrange(&item_range));
1209 ranges_add(i->ranges, item_range);
1217 * Creates an iterator over the items which intersect the visible region of hdc.
1219 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1221 POINT Origin, Position;
1222 RECT rcItem, rcClip;
1225 rgntype = GetClipBox(hdc, &rcClip);
1226 if (rgntype == NULLREGION) return iterator_empty(i);
1227 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1228 if (rgntype == SIMPLEREGION) return TRUE;
1230 /* first deal with the special item */
1231 if (i->nSpecial != -1)
1233 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1234 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1237 /* if we can't deal with the region, we'll just go with the simple range */
1238 LISTVIEW_GetOrigin(infoPtr, &Origin);
1239 TRACE("building visible range:\n");
1240 if (!i->ranges && i->range.lower < i->range.upper)
1242 if (!(i->ranges = ranges_create(50))) return TRUE;
1243 if (!ranges_add(i->ranges, i->range))
1245 ranges_destroy(i->ranges);
1251 /* now delete the invisible items from the list */
1252 while(iterator_next(i))
1254 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1255 rcItem.left = Position.x + Origin.x;
1256 rcItem.top = Position.y + Origin.y;
1257 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1258 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1259 if (!RectVisible(hdc, &rcItem))
1260 ranges_delitem(i->ranges, i->nItem);
1262 /* the iterator should restart on the next iterator_next */
1268 /******** Misc helper functions ************************************/
1270 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1271 WPARAM wParam, LPARAM lParam, BOOL isW)
1273 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1274 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1277 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1279 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1281 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1282 (uView == LVS_ICON || uView == LVS_SMALLICON);
1285 /******** Internal API functions ************************************/
1287 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1289 static COLUMN_INFO mainItem;
1291 if (nSubItem == 0 && infoPtr->hdpaColumns->nItemCount == 0) return &mainItem;
1292 assert (nSubItem >= 0 && nSubItem < infoPtr->hdpaColumns->nItemCount);
1293 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1296 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1298 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1301 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1303 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1306 /* Listview invalidation functions: use _only_ these functions to invalidate */
1308 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1310 return infoPtr->bRedraw;
1313 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1315 if(!is_redrawing(infoPtr)) return;
1316 TRACE(" invalidating rect=%s\n", debugrect(rect));
1317 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1320 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1324 if(!is_redrawing(infoPtr)) return;
1325 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1326 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1329 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1331 POINT Origin, Position;
1334 if(!is_redrawing(infoPtr)) return;
1335 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1336 LISTVIEW_GetOrigin(infoPtr, &Origin);
1337 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1338 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1340 rcBox.bottom = infoPtr->nItemHeight;
1341 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1342 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1345 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1347 LISTVIEW_InvalidateRect(infoPtr, NULL);
1350 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1354 if(!is_redrawing(infoPtr)) return;
1355 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1356 rcCol.top = infoPtr->rcList.top;
1357 rcCol.bottom = infoPtr->rcList.bottom;
1358 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1363 * Retrieves the number of items that can fit vertically in the client area.
1366 * [I] infoPtr : valid pointer to the listview structure
1369 * Number of items per row.
1371 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1373 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1375 return max(nListWidth/infoPtr->nItemWidth, 1);
1380 * Retrieves the number of items that can fit horizontally in the client
1384 * [I] infoPtr : valid pointer to the listview structure
1387 * Number of items per column.
1389 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1391 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1393 return max(nListHeight / infoPtr->nItemHeight, 1);
1397 /*************************************************************************
1398 * LISTVIEW_ProcessLetterKeys
1400 * Processes keyboard messages generated by pressing the letter keys
1402 * What this does is perform a case insensitive search from the
1403 * current position with the following quirks:
1404 * - If two chars or more are pressed in quick succession we search
1405 * for the corresponding string (e.g. 'abc').
1406 * - If there is a delay we wipe away the current search string and
1407 * restart with just that char.
1408 * - If the user keeps pressing the same character, whether slowly or
1409 * fast, so that the search string is entirely composed of this
1410 * character ('aaaaa' for instance), then we search for first item
1411 * that starting with that character.
1412 * - If the user types the above character in quick succession, then
1413 * we must also search for the corresponding string ('aaaaa'), and
1414 * go to that string if there is a match.
1417 * [I] hwnd : handle to the window
1418 * [I] charCode : the character code, the actual character
1419 * [I] keyData : key data
1427 * - The current implementation has a list of characters it will
1428 * accept and it ignores averything else. In particular it will
1429 * ignore accentuated characters which seems to match what
1430 * Windows does. But I'm not sure it makes sense to follow
1432 * - We don't sound a beep when the search fails.
1436 * TREEVIEW_ProcessLetterKeys
1438 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1443 WCHAR buffer[MAX_PATH];
1444 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1446 /* simple parameter checking */
1447 if (!charCode || !keyData) return 0;
1449 /* only allow the valid WM_CHARs through */
1450 if (!isalnum(charCode) &&
1451 charCode != '.' && charCode != '`' && charCode != '!' &&
1452 charCode != '@' && charCode != '#' && charCode != '$' &&
1453 charCode != '%' && charCode != '^' && charCode != '&' &&
1454 charCode != '*' && charCode != '(' && charCode != ')' &&
1455 charCode != '-' && charCode != '_' && charCode != '+' &&
1456 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1457 charCode != '}' && charCode != '[' && charCode != '{' &&
1458 charCode != '/' && charCode != '?' && charCode != '>' &&
1459 charCode != '<' && charCode != ',' && charCode != '~')
1462 /* if there's one item or less, there is no where to go */
1463 if (infoPtr->nItemCount <= 1) return 0;
1465 /* update the search parameters */
1466 infoPtr->lastKeyPressTimestamp = GetTickCount();
1467 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1468 if (infoPtr->nSearchParamLength < MAX_PATH)
1469 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1470 if (infoPtr->charCode != charCode)
1471 infoPtr->charCode = charCode = 0;
1473 infoPtr->charCode=charCode;
1474 infoPtr->szSearchParam[0]=charCode;
1475 infoPtr->nSearchParamLength=1;
1476 /* Redundant with the 1 char string */
1480 /* and search from the current position */
1482 if (infoPtr->nFocusedItem >= 0) {
1483 endidx=infoPtr->nFocusedItem;
1485 /* if looking for single character match,
1486 * then we must always move forward
1488 if (infoPtr->nSearchParamLength == 1)
1491 endidx=infoPtr->nItemCount;
1495 if (idx == infoPtr->nItemCount) {
1496 if (endidx == infoPtr->nItemCount || endidx == 0)
1502 item.mask = LVIF_TEXT;
1505 item.pszText = buffer;
1506 item.cchTextMax = MAX_PATH;
1507 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1509 /* check for a match */
1510 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1513 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1514 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1515 /* This would work but we must keep looking for a longer match */
1519 } while (idx != endidx);
1522 LISTVIEW_KeySelection(infoPtr, nItem);
1527 /*************************************************************************
1528 * LISTVIEW_UpdateHeaderSize [Internal]
1530 * Function to resize the header control
1533 * [I] hwnd : handle to a window
1534 * [I] nNewScrollPos : scroll pos to set
1539 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1544 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1546 GetWindowRect(infoPtr->hwndHeader, &winRect);
1547 point[0].x = winRect.left;
1548 point[0].y = winRect.top;
1549 point[1].x = winRect.right;
1550 point[1].y = winRect.bottom;
1552 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1553 point[0].x = -nNewScrollPos;
1554 point[1].x += nNewScrollPos;
1556 SetWindowPos(infoPtr->hwndHeader,0,
1557 point[0].x,point[0].y,point[1].x,point[1].y,
1558 SWP_NOZORDER | SWP_NOACTIVATE);
1563 * Update the scrollbars. This functions should be called whenever
1564 * the content, size or view changes.
1567 * [I] infoPtr : valid pointer to the listview structure
1572 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1574 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1575 SCROLLINFO horzInfo, vertInfo;
1577 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1579 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1580 horzInfo.cbSize = sizeof(SCROLLINFO);
1581 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1583 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1584 if (uView == LVS_LIST)
1586 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1587 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1589 /* scroll by at least one column per page */
1590 if(horzInfo.nPage < infoPtr->nItemWidth)
1591 horzInfo.nPage = infoPtr->nItemWidth;
1593 horzInfo.nPage /= infoPtr->nItemWidth;
1595 else if (uView == LVS_REPORT)
1597 horzInfo.nMax = infoPtr->nItemWidth;
1599 else /* LVS_ICON, or LVS_SMALLICON */
1603 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1606 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1607 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1608 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1609 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1611 /* Setting the horizontal scroll can change the listview size
1612 * (and potentially everything else) so we need to recompute
1613 * everything again for the vertical scroll
1616 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1617 vertInfo.cbSize = sizeof(SCROLLINFO);
1618 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1620 if (uView == LVS_REPORT)
1622 vertInfo.nMax = infoPtr->nItemCount;
1624 /* scroll by at least one page */
1625 if(vertInfo.nPage < infoPtr->nItemHeight)
1626 vertInfo.nPage = infoPtr->nItemHeight;
1628 vertInfo.nPage /= infoPtr->nItemHeight;
1630 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1634 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1637 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1638 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1639 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1640 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1642 /* Update the Header Control */
1643 if (uView == LVS_REPORT)
1645 horzInfo.fMask = SIF_POS;
1646 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1647 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1654 * Shows/hides the focus rectangle.
1657 * [I] infoPtr : valid pointer to the listview structure
1658 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1663 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1665 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1668 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1670 if (infoPtr->nFocusedItem < 0) return;
1672 /* we need some gymnastics in ICON mode to handle large items */
1673 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1677 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1678 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1680 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1685 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1687 /* for some reason, owner draw should work only in report mode */
1688 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1693 item.iItem = infoPtr->nFocusedItem;
1695 item.mask = LVIF_PARAM;
1696 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1698 ZeroMemory(&dis, sizeof(dis));
1699 dis.CtlType = ODT_LISTVIEW;
1700 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1701 dis.itemID = item.iItem;
1702 dis.itemAction = ODA_FOCUS;
1703 if (fShow) dis.itemState |= ODS_FOCUS;
1704 dis.hwndItem = infoPtr->hwndSelf;
1706 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1707 dis.itemData = item.lParam;
1709 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1713 DrawFocusRect(hdc, &infoPtr->rcFocus);
1716 ReleaseDC(infoPtr->hwndSelf, hdc);
1720 * Invalidates all visible selected items.
1722 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1726 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1727 while(iterator_next(&i))
1729 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1730 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1732 iterator_destroy(&i);
1737 * DESCRIPTION: [INTERNAL]
1738 * Computes an item's (left,top) corner, relative to rcView.
1739 * That is, the position has NOT been made relative to the Origin.
1740 * This is deliberate, to avoid computing the Origin over, and
1741 * over again, when this function is call in a loop. Instead,
1742 * one ca factor the computation of the Origin before the loop,
1743 * and offset the value retured by this function, on every iteration.
1746 * [I] infoPtr : valid pointer to the listview structure
1747 * [I] nItem : item number
1748 * [O] lpptOrig : item top, left corner
1753 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1755 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1757 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1759 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1761 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1762 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1764 else if (uView == LVS_LIST)
1766 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1767 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1768 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1770 else /* LVS_REPORT */
1772 lpptPosition->x = 0;
1773 lpptPosition->y = nItem * infoPtr->nItemHeight;
1778 * DESCRIPTION: [INTERNAL]
1779 * Compute the rectangles of an item. This is to localize all
1780 * the computations in one place. If you are not interested in some
1781 * of these values, simply pass in a NULL -- the fucntion is smart
1782 * enough to compute only what's necessary. The function computes
1783 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1784 * one, the BOX rectangle. This rectangle is very cheap to compute,
1785 * and is guaranteed to contain all the other rectangles. Computing
1786 * the ICON rect is also cheap, but all the others are potentaily
1787 * expensive. This gives an easy and effective optimization when
1788 * searching (like point inclusion, or rectangle intersection):
1789 * first test against the BOX, and if TRUE, test agains the desired
1791 * If the function does not have all the necessary information
1792 * to computed the requested rectangles, will crash with a
1793 * failed assertion. This is done so we catch all programming
1794 * errors, given that the function is called only from our code.
1796 * We have the following 'special' meanings for a few fields:
1797 * * If LVIS_FOCUSED is set, we assume the item has the focus
1798 * This is important in ICON mode, where it might get a larger
1799 * then usual rectange
1801 * Please note that subitem support works only in REPORT mode.
1804 * [I] infoPtr : valid pointer to the listview structure
1805 * [I] lpLVItem : item to compute the measures for
1806 * [O] lprcBox : ptr to Box rectangle
1807 * The internal LVIR_BOX rectangle
1808 * [0] lprcState : ptr to State icon rectangle
1809 * The internal LVIR_STATE rectangle
1810 * [O] lprcIcon : ptr to Icon rectangle
1811 * Same as LVM_GETITEMRECT with LVIR_ICON
1812 * [O] lprcLabel : ptr to Label rectangle
1813 * Same as LVM_GETITEMRECT with LVIR_LABEL
1818 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1819 LPRECT lprcBox, LPRECT lprcState,
1820 LPRECT lprcIcon, LPRECT lprcLabel)
1822 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1823 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1824 RECT Box, State, Icon, Label;
1825 COLUMN_INFO *lpColumnInfo = NULL;
1827 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1829 /* Be smart and try to figure out the minimum we have to do */
1830 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1831 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1833 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1834 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1836 if (lprcLabel) doLabel = TRUE;
1837 if (doLabel || lprcIcon) doIcon = TRUE;
1838 if (doIcon || lprcState) doState = TRUE;
1840 /************************************************************/
1841 /* compute the box rectangle (it should be cheap to do) */
1842 /************************************************************/
1843 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1844 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1846 if (lpLVItem->iSubItem)
1848 Box = lpColumnInfo->rcHeader;
1853 Box.right = infoPtr->nItemWidth;
1856 Box.bottom = infoPtr->nItemHeight;
1858 /************************************************************/
1859 /* compute STATEICON bounding box */
1860 /************************************************************/
1863 if (uView == LVS_ICON)
1865 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1866 if (infoPtr->himlNormal)
1867 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1868 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1872 /* we need the ident in report mode, if we don't have it, we fail */
1873 State.left = Box.left;
1874 if (uView == LVS_REPORT)
1876 if (lpLVItem->iSubItem == 0)
1878 State.left += REPORT_MARGINX;
1879 assert(lpLVItem->mask & LVIF_INDENT);
1880 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1883 State.top = Box.top;
1885 State.right = State.left;
1886 State.bottom = State.top;
1887 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1889 State.right += infoPtr->iconStateSize.cx;
1890 State.bottom += infoPtr->iconStateSize.cy;
1892 if (lprcState) *lprcState = State;
1893 TRACE(" - state=%s\n", debugrect(&State));
1896 /************************************************************/
1897 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1898 /************************************************************/
1901 if (uView == LVS_ICON)
1903 Icon.left = Box.left;
1904 if (infoPtr->himlNormal)
1905 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1906 Icon.top = Box.top + ICON_TOP_PADDING;
1907 Icon.right = Icon.left;
1908 Icon.bottom = Icon.top;
1909 if (infoPtr->himlNormal)
1911 Icon.right += infoPtr->iconSize.cx;
1912 Icon.bottom += infoPtr->iconSize.cy;
1915 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1917 Icon.left = State.right;
1919 Icon.right = Icon.left;
1920 if (infoPtr->himlSmall && (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE)))
1921 Icon.right += infoPtr->iconSize.cx;
1922 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1924 if(lprcIcon) *lprcIcon = Icon;
1925 TRACE(" - icon=%s\n", debugrect(&Icon));
1928 /************************************************************/
1929 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1930 /************************************************************/
1933 SIZE labelSize = { 0, 0 };
1935 /* calculate how far to the right can the label strech */
1936 Label.right = Box.right;
1937 if (uView == LVS_REPORT)
1939 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1942 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1944 labelSize.cx = infoPtr->nItemWidth;
1945 labelSize.cy = infoPtr->nItemHeight;
1949 /* we need the text in non owner draw mode */
1950 assert(lpLVItem->mask & LVIF_TEXT);
1951 if (is_textT(lpLVItem->pszText, TRUE))
1953 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1954 HDC hdc = GetDC(infoPtr->hwndSelf);
1955 HFONT hOldFont = SelectObject(hdc, hFont);
1959 /* compute rough rectangle where the label will go */
1960 SetRectEmpty(&rcText);
1961 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
1962 rcText.bottom = infoPtr->nItemHeight;
1963 if (uView == LVS_ICON)
1964 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1966 /* now figure out the flags */
1967 if (uView == LVS_ICON)
1968 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1970 uFormat = LV_SL_DT_FLAGS;
1972 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1974 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
1975 labelSize.cy = rcText.bottom - rcText.top;
1977 SelectObject(hdc, hOldFont);
1978 ReleaseDC(infoPtr->hwndSelf, hdc);
1982 if (uView == LVS_ICON)
1984 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1985 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1986 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1987 Label.right = Label.left + labelSize.cx;
1988 Label.bottom = Label.top + infoPtr->nItemHeight;
1989 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1991 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1992 labelSize.cy /= infoPtr->ntmHeight;
1993 labelSize.cy = max(labelSize.cy, 1);
1994 labelSize.cy *= infoPtr->ntmHeight;
1996 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1998 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2000 Label.left = Icon.right;
2001 Label.top = Box.top;
2002 Label.right = min(Label.left + labelSize.cx, Label.right);
2003 Label.bottom = Label.top + infoPtr->nItemHeight;
2006 if (lprcLabel) *lprcLabel = Label;
2007 TRACE(" - label=%s\n", debugrect(&Label));
2010 /* Fix the Box if necessary */
2013 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2014 else *lprcBox = Box;
2016 TRACE(" - box=%s\n", debugrect(&Box));
2020 * DESCRIPTION: [INTERNAL]
2023 * [I] infoPtr : valid pointer to the listview structure
2024 * [I] nItem : item number
2025 * [O] lprcBox : ptr to Box rectangle
2030 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2032 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2033 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2034 POINT Position, Origin;
2037 LISTVIEW_GetOrigin(infoPtr, &Origin);
2038 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2040 /* Be smart and try to figure out the minimum we have to do */
2042 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2043 lvItem.mask |= LVIF_TEXT;
2044 lvItem.iItem = nItem;
2045 lvItem.iSubItem = 0;
2046 lvItem.pszText = szDispText;
2047 lvItem.cchTextMax = DISP_TEXT_SIZE;
2048 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2049 if (uView == LVS_ICON)
2051 lvItem.mask |= LVIF_STATE;
2052 lvItem.stateMask = LVIS_FOCUSED;
2053 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2055 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2057 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2063 * Returns the current icon position, and advances it along the top.
2064 * The returned position is not offset by Origin.
2067 * [I] infoPtr : valid pointer to the listview structure
2068 * [O] lpPos : will get the current icon position
2073 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2075 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2077 *lpPos = infoPtr->currIconPos;
2079 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2080 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2082 infoPtr->currIconPos.x = 0;
2083 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2089 * Returns the current icon position, and advances it down the left edge.
2090 * The returned position is not offset by Origin.
2093 * [I] infoPtr : valid pointer to the listview structure
2094 * [O] lpPos : will get the current icon position
2099 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2101 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2103 *lpPos = infoPtr->currIconPos;
2105 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2106 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2108 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2109 infoPtr->currIconPos.y = 0;
2115 * Moves an icon to the specified position.
2116 * It takes care of invalidating the item, etc.
2119 * [I] infoPtr : valid pointer to the listview structure
2120 * [I] nItem : the item to move
2121 * [I] lpPos : the new icon position
2122 * [I] isNew : flags the item as being new
2128 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2134 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2135 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2137 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2138 LISTVIEW_InvalidateItem(infoPtr, nItem);
2141 /* Allocating a POINTER for every item is too resource intensive,
2142 * so we'll keep the (x,y) in different arrays */
2143 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2144 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2146 LISTVIEW_InvalidateItem(infoPtr, nItem);
2153 * Arranges listview items in icon display mode.
2156 * [I] infoPtr : valid pointer to the listview structure
2157 * [I] nAlignCode : alignment code
2163 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2165 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2166 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2170 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2172 TRACE("nAlignCode=%d\n", nAlignCode);
2174 if (nAlignCode == LVA_DEFAULT)
2176 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2177 else nAlignCode = LVA_ALIGNTOP;
2182 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2183 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2184 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2185 default: return FALSE;
2188 infoPtr->bAutoarrange = TRUE;
2189 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2190 for (i = 0; i < infoPtr->nItemCount; i++)
2192 next_pos(infoPtr, &pos);
2193 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2201 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2204 * [I] infoPtr : valid pointer to the listview structure
2205 * [O] lprcView : bounding rectangle
2211 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2215 SetRectEmpty(lprcView);
2217 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2221 for (i = 0; i < infoPtr->nItemCount; i++)
2223 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2224 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2225 lprcView->right = max(lprcView->right, x);
2226 lprcView->bottom = max(lprcView->bottom, y);
2228 if (infoPtr->nItemCount > 0)
2230 lprcView->right += infoPtr->nItemWidth;
2231 lprcView->bottom += infoPtr->nItemHeight;
2236 y = LISTVIEW_GetCountPerColumn(infoPtr);
2237 x = infoPtr->nItemCount / y;
2238 if (infoPtr->nItemCount % y) x++;
2239 lprcView->right = x * infoPtr->nItemWidth;
2240 lprcView->bottom = y * infoPtr->nItemHeight;
2244 lprcView->right = infoPtr->nItemWidth;
2245 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2252 * Retrieves the bounding rectangle of all the items.
2255 * [I] infoPtr : valid pointer to the listview structure
2256 * [O] lprcView : bounding rectangle
2262 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2266 TRACE("(lprcView=%p)\n", lprcView);
2268 if (!lprcView) return FALSE;
2270 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2271 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2272 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2274 TRACE("lprcView=%s\n", debugrect(lprcView));
2281 * Retrieves the subitem pointer associated with the subitem index.
2284 * [I] hdpaSubItems : DPA handle for a specific item
2285 * [I] nSubItem : index of subitem
2288 * SUCCESS : subitem pointer
2291 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2293 SUBITEM_INFO *lpSubItem;
2296 /* we should binary search here if need be */
2297 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2299 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2300 if (lpSubItem->iSubItem == nSubItem)
2310 * Caclulates the desired item width.
2313 * [I] infoPtr : valid pointer to the listview structure
2316 * The desired item width.
2318 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2320 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2323 TRACE("uView=%d\n", uView);
2325 if (uView == LVS_ICON)
2326 nItemWidth = infoPtr->iconSpacing.cx;
2327 else if (uView == LVS_REPORT)
2331 if (infoPtr->hdpaColumns->nItemCount > 0)
2333 LISTVIEW_GetHeaderRect(infoPtr, infoPtr->hdpaColumns->nItemCount - 1, &rcHeader);
2334 nItemWidth = rcHeader.right;
2337 else /* LVS_SMALLICON, or LVS_LIST */
2341 for (i = 0; i < infoPtr->nItemCount; i++)
2342 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2344 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2345 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2347 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2350 return max(nItemWidth, 1);
2355 * Caclulates the desired item height.
2358 * [I] infoPtr : valid pointer to the listview structure
2361 * The desired item height.
2363 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2365 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2368 TRACE("uView=%d\n", uView);
2370 if (uView == LVS_ICON)
2371 nItemHeight = infoPtr->iconSpacing.cy;
2374 nItemHeight = infoPtr->ntmHeight;
2375 if (infoPtr->himlState)
2376 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2377 if (infoPtr->himlSmall)
2378 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2379 if (infoPtr->himlState || infoPtr->himlSmall)
2380 nItemHeight += HEIGHT_PADDING;
2383 return max(nItemHeight, 1);
2388 * Updates the width, and height of an item.
2391 * [I] infoPtr : valid pointer to the listview structure
2396 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2398 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2399 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2405 * Retrieves and saves important text metrics info for the current
2409 * [I] infoPtr : valid pointer to the listview structure
2412 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2414 HDC hdc = GetDC(infoPtr->hwndSelf);
2415 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2416 HFONT hOldFont = SelectObject(hdc, hFont);
2419 if (GetTextMetricsW(hdc, &tm))
2421 infoPtr->ntmHeight = tm.tmHeight;
2422 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2424 SelectObject(hdc, hOldFont);
2425 ReleaseDC(infoPtr->hwndSelf, hdc);
2427 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2432 * A compare function for ranges
2435 * [I] range1 : pointer to range 1;
2436 * [I] range2 : pointer to range 2;
2440 * > 0 : if range 1 > range 2
2441 * < 0 : if range 2 > range 1
2442 * = 0 : if range intersects range 2
2444 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2448 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2450 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2455 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2461 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2463 #define ranges_check(ranges, desc) do { } while(0)
2466 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2471 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2473 assert (ranges->hdpa->nItemCount >= 0);
2474 ranges_dump(ranges);
2475 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2476 if (ranges->hdpa->nItemCount > 0)
2477 assert (prev->lower >= 0 && prev->lower < prev->upper);
2478 for (i = 1; i < ranges->hdpa->nItemCount; i++)
2480 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2481 assert (prev->upper <= curr->lower);
2482 assert (curr->lower < curr->upper);
2485 TRACE("--- Done checking---\n");
2488 static RANGES ranges_create(int count)
2490 RANGES ranges = (RANGES)COMCTL32_Alloc(sizeof(struct tagRANGES));
2491 if (!ranges) return NULL;
2492 ranges->hdpa = DPA_Create(count);
2493 if (ranges->hdpa) return ranges;
2494 COMCTL32_Free(ranges);
2498 static void ranges_clear(RANGES ranges)
2502 for(i = 0; i < ranges->hdpa->nItemCount; i++)
2503 COMCTL32_Free(DPA_GetPtr(ranges->hdpa, i));
2504 DPA_DeleteAllPtrs(ranges->hdpa);
2508 static void ranges_destroy(RANGES ranges)
2510 if (!ranges) return;
2511 ranges_clear(ranges);
2512 DPA_Destroy(ranges->hdpa);
2513 COMCTL32_Free(ranges);
2516 static RANGES ranges_clone(RANGES ranges)
2521 if (!(clone = ranges_create(ranges->hdpa->nItemCount))) goto fail;
2523 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2525 RANGE *newrng = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2526 if (!newrng) goto fail;
2527 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2528 DPA_SetPtr(clone->hdpa, i, newrng);
2533 TRACE ("clone failed\n");
2534 ranges_destroy(clone);
2538 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2542 for (i = 0; i < sub->hdpa->nItemCount; i++)
2543 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2548 static void ranges_dump(RANGES ranges)
2552 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2553 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2556 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2558 RANGE srchrng = { nItem, nItem + 1 };
2560 TRACE("(nItem=%d)\n", nItem);
2561 ranges_check(ranges, "before contain");
2562 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2565 static INT ranges_itemcount(RANGES ranges)
2569 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2571 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2572 count += sel->upper - sel->lower;
2578 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2580 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2583 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2584 if (index == -1) return TRUE;
2586 for (; index < ranges->hdpa->nItemCount; index++)
2588 chkrng = DPA_GetPtr(ranges->hdpa, index);
2589 if (chkrng->lower >= nItem)
2590 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2591 if (chkrng->upper > nItem)
2592 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2597 static BOOL ranges_add(RANGES ranges, RANGE range)
2602 TRACE("(%s)\n", debugrange(&range));
2603 ranges_check(ranges, "before add");
2605 /* try find overlapping regions first */
2606 srchrgn.lower = range.lower - 1;
2607 srchrgn.upper = range.upper + 1;
2608 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2614 TRACE("Adding new range\n");
2616 /* create the brand new range to insert */
2617 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2618 if(!newrgn) goto fail;
2621 /* figure out where to insert it */
2622 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2623 TRACE("index=%d\n", index);
2624 if (index == -1) index = 0;
2626 /* and get it over with */
2627 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2629 COMCTL32_Free(newrgn);
2635 RANGE *chkrgn, *mrgrgn;
2636 INT fromindex, mergeindex;
2638 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2639 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2641 chkrgn->lower = min(range.lower, chkrgn->lower);
2642 chkrgn->upper = max(range.upper, chkrgn->upper);
2644 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2646 /* merge now common anges */
2648 srchrgn.lower = chkrgn->lower - 1;
2649 srchrgn.upper = chkrgn->upper + 1;
2653 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2654 if (mergeindex == -1) break;
2655 if (mergeindex == index)
2657 fromindex = index + 1;
2661 TRACE("Merge with index %i\n", mergeindex);
2663 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2664 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2665 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2666 COMCTL32_Free(mrgrgn);
2667 DPA_DeletePtr(ranges->hdpa, mergeindex);
2668 if (mergeindex < index) index --;
2672 ranges_check(ranges, "after add");
2676 ranges_check(ranges, "failed add");
2680 static BOOL ranges_del(RANGES ranges, RANGE range)
2685 TRACE("(%s)\n", debugrange(&range));
2686 ranges_check(ranges, "before del");
2688 /* we don't use DPAS_SORTED here, since we need *
2689 * to find the first overlapping range */
2690 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2693 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2695 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2697 /* case 1: Same range */
2698 if ( (chkrgn->upper == range.upper) &&
2699 (chkrgn->lower == range.lower) )
2701 DPA_DeletePtr(ranges->hdpa, index);
2704 /* case 2: engulf */
2705 else if ( (chkrgn->upper <= range.upper) &&
2706 (chkrgn->lower >= range.lower) )
2708 DPA_DeletePtr(ranges->hdpa, index);
2710 /* case 3: overlap upper */
2711 else if ( (chkrgn->upper <= range.upper) &&
2712 (chkrgn->lower < range.lower) )
2714 chkrgn->upper = range.lower;
2716 /* case 4: overlap lower */
2717 else if ( (chkrgn->upper > range.upper) &&
2718 (chkrgn->lower >= range.lower) )
2720 chkrgn->lower = range.upper;
2723 /* case 5: fully internal */
2726 RANGE tmprgn = *chkrgn, *newrgn;
2728 if (!(newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE)))) goto fail;
2729 newrgn->lower = chkrgn->lower;
2730 newrgn->upper = range.lower;
2731 chkrgn->lower = range.upper;
2732 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2734 COMCTL32_Free(newrgn);
2741 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2744 ranges_check(ranges, "after del");
2748 ranges_check(ranges, "failed del");
2754 * Removes all selection ranges
2757 * [I] infoPtr : valid pointer to the listview structure
2758 * [I] toSkip : item range to skip removing the selection
2764 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2773 lvItem.stateMask = LVIS_SELECTED;
2775 /* need to clone the DPA because callbacks can change it */
2776 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2777 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2778 while(iterator_next(&i))
2779 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2780 /* note that the iterator destructor will free the cloned range */
2781 iterator_destroy(&i);
2786 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2790 if (!(toSkip = ranges_create(1))) return FALSE;
2791 if (nItem != -1) ranges_additem(toSkip, nItem);
2792 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2793 ranges_destroy(toSkip);
2797 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2799 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2804 * Retrieves the number of items that are marked as selected.
2807 * [I] infoPtr : valid pointer to the listview structure
2810 * Number of items selected.
2812 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2814 INT nSelectedCount = 0;
2816 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2819 for (i = 0; i < infoPtr->nItemCount; i++)
2821 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2826 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2828 TRACE("nSelectedCount=%d\n", nSelectedCount);
2829 return nSelectedCount;
2834 * Manages the item focus.
2837 * [I] infoPtr : valid pointer to the listview structure
2838 * [I] nItem : item index
2841 * TRUE : focused item changed
2842 * FALSE : focused item has NOT changed
2844 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2846 INT oldFocus = infoPtr->nFocusedItem;
2849 if (nItem == infoPtr->nFocusedItem) return FALSE;
2851 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2852 lvItem.stateMask = LVIS_FOCUSED;
2853 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2855 return oldFocus != infoPtr->nFocusedItem;
2858 /* Helper function for LISTVIEW_ShiftIndices *only* */
2859 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2861 if (nShiftItem < nItem) return nShiftItem;
2863 if (nShiftItem > nItem) return nShiftItem + direction;
2865 if (direction > 0) return nShiftItem + direction;
2867 return min(nShiftItem, infoPtr->nItemCount - 1);
2872 * Updates the various indices after an item has been inserted or deleted.
2875 * [I] infoPtr : valid pointer to the listview structure
2876 * [I] nItem : item index
2877 * [I] direction : Direction of shift, +1 or -1.
2882 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2887 /* temporarily disable change notification while shifting items */
2888 bOldChange = infoPtr->bDoChangeNotify;
2889 infoPtr->bDoChangeNotify = FALSE;
2891 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2893 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2895 assert(abs(direction) == 1);
2897 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2899 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2900 if (nNewFocus != infoPtr->nFocusedItem)
2901 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2903 /* But we are not supposed to modify nHotItem! */
2905 infoPtr->bDoChangeNotify = bOldChange;
2911 * Adds a block of selections.
2914 * [I] infoPtr : valid pointer to the listview structure
2915 * [I] nItem : item index
2920 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2922 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2923 INT nLast = max(infoPtr->nSelectionMark, nItem);
2927 if (nFirst == -1) nFirst = nItem;
2929 item.state = LVIS_SELECTED;
2930 item.stateMask = LVIS_SELECTED;
2932 /* FIXME: this is not correct LVS_OWNERDATA
2933 * setting the item states individually will generate
2934 * a LVN_ITEMCHANGED notification for each one. Instead,
2935 * we have to send a LVN_ODSTATECHANGED notification.
2936 * See MSDN documentation for LVN_ITEMCHANGED.
2938 for (i = nFirst; i <= nLast; i++)
2939 LISTVIEW_SetItemState(infoPtr,i,&item);
2945 * Sets a single group selection.
2948 * [I] infoPtr : valid pointer to the listview structure
2949 * [I] nItem : item index
2954 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2956 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2961 if (!(selection = ranges_create(100))) return;
2963 item.state = LVIS_SELECTED;
2964 item.stateMask = LVIS_SELECTED;
2966 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2968 if (infoPtr->nSelectionMark == -1)
2970 infoPtr->nSelectionMark = nItem;
2971 ranges_additem(selection, nItem);
2977 sel.lower = min(infoPtr->nSelectionMark, nItem);
2978 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
2979 ranges_add(selection, sel);
2984 RECT rcItem, rcSel, rcSelMark;
2987 rcItem.left = LVIR_BOUNDS;
2988 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2989 rcSelMark.left = LVIR_BOUNDS;
2990 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2991 UnionRect(&rcSel, &rcItem, &rcSelMark);
2992 iterator_frameditems(&i, infoPtr, &rcSel);
2993 while(iterator_next(&i))
2995 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
2996 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
2998 iterator_destroy(&i);
3001 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3002 iterator_rangesitems(&i, selection);
3003 while(iterator_next(&i))
3004 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3005 /* this will also destroy the selection */
3006 iterator_destroy(&i);
3008 LISTVIEW_SetItemFocus(infoPtr, nItem);
3013 * Sets a single selection.
3016 * [I] infoPtr : valid pointer to the listview structure
3017 * [I] nItem : item index
3022 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3026 TRACE("nItem=%d\n", nItem);
3028 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3030 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3031 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3032 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3034 infoPtr->nSelectionMark = nItem;
3039 * Set selection(s) with keyboard.
3042 * [I] infoPtr : valid pointer to the listview structure
3043 * [I] nItem : item index
3046 * SUCCESS : TRUE (needs to be repainted)
3047 * FAILURE : FALSE (nothing has changed)
3049 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3051 /* FIXME: pass in the state */
3052 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3053 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3054 BOOL bResult = FALSE;
3056 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3058 if (infoPtr->dwStyle & LVS_SINGLESEL)
3061 LISTVIEW_SetSelection(infoPtr, nItem);
3068 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3072 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3077 LISTVIEW_SetSelection(infoPtr, nItem);
3080 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3083 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3090 * Called when the mouse is being actively tracked and has hovered for a specified
3094 * [I] infoPtr : valid pointer to the listview structure
3095 * [I] fwKeys : key indicator
3096 * [I] pts : mouse position
3099 * 0 if the message was processed, non-zero if there was an error
3102 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3103 * over the item for a certain period of time.
3106 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
3108 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3109 /* FIXME: select the item!!! */
3110 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3117 * Called whenever WM_MOUSEMOVE is received.
3120 * [I] infoPtr : valid pointer to the listview structure
3121 * [I] fwKeys : key indicator
3122 * [I] pts : mouse position
3125 * 0 if the message is processed, non-zero if there was an error
3127 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
3129 TRACKMOUSEEVENT trackinfo;
3131 /* see if we are supposed to be tracking mouse hovering */
3132 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3133 /* fill in the trackinfo struct */
3134 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3135 trackinfo.dwFlags = TME_QUERY;
3136 trackinfo.hwndTrack = infoPtr->hwndSelf;
3137 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3139 /* see if we are already tracking this hwnd */
3140 _TrackMouseEvent(&trackinfo);
3142 if(!(trackinfo.dwFlags & TME_HOVER)) {
3143 trackinfo.dwFlags = TME_HOVER;
3145 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3146 _TrackMouseEvent(&trackinfo);
3155 * Tests wheather the item is assignable to a list with style lStyle
3157 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3159 if ( (lpLVItem->mask & LVIF_TEXT) &&
3160 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3161 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3169 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3172 * [I] infoPtr : valid pointer to the listview structure
3173 * [I] lpLVItem : valid pointer to new item atttributes
3174 * [I] isNew : the item being set is being inserted
3175 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3176 * [O] bChanged : will be set to TRUE if the item really changed
3182 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3184 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3192 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3194 if (lpLVItem->mask == 0) return TRUE;
3196 if (infoPtr->dwStyle & LVS_OWNERDATA)
3198 /* a virtual listview we stores only selection and focus */
3199 if (lpLVItem->mask & ~LVIF_STATE)
3205 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3206 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3210 /* we need to get the lParam and state of the item */
3211 item.iItem = lpLVItem->iItem;
3212 item.iSubItem = lpLVItem->iSubItem;
3213 item.mask = LVIF_STATE | LVIF_PARAM;
3214 item.stateMask = ~0;
3217 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3219 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3220 /* determine what fields will change */
3221 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3222 uChanged |= LVIF_STATE;
3224 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3225 uChanged |= LVIF_IMAGE;
3227 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3228 uChanged |= LVIF_PARAM;
3230 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3231 uChanged |= LVIF_INDENT;
3233 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3234 uChanged |= LVIF_TEXT;
3236 TRACE("uChanged=0x%x\n", uChanged);
3237 if (!uChanged) return TRUE;
3240 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3241 nmlv.iItem = lpLVItem->iItem;
3242 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3243 nmlv.uOldState = item.state;
3244 nmlv.uChanged = uChanged;
3245 nmlv.lParam = item.lParam;
3247 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3248 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3250 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3251 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3254 /* copy information */
3255 if (lpLVItem->mask & LVIF_TEXT)
3256 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3258 if (lpLVItem->mask & LVIF_IMAGE)
3259 lpItem->hdr.iImage = lpLVItem->iImage;
3261 if (lpLVItem->mask & LVIF_PARAM)
3262 lpItem->lParam = lpLVItem->lParam;
3264 if (lpLVItem->mask & LVIF_INDENT)
3265 lpItem->iIndent = lpLVItem->iIndent;
3267 if (uChanged & LVIF_STATE)
3269 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3271 lpItem->state &= ~lpLVItem->stateMask;
3272 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3274 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3276 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3277 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3279 else if (lpLVItem->stateMask & LVIS_SELECTED)
3280 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3282 /* if we are asked to change focus, and we manage it, do it */
3283 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3285 if (lpLVItem->state & LVIS_FOCUSED)
3287 LISTVIEW_SetItemFocus(infoPtr, -1);
3288 infoPtr->nFocusedItem = lpLVItem->iItem;
3289 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3291 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3292 infoPtr->nFocusedItem = -1;
3296 /* if we're inserting the item, we're done */
3297 if (isNew) return TRUE;
3299 /* send LVN_ITEMCHANGED notification */
3300 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3301 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3308 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3311 * [I] infoPtr : valid pointer to the listview structure
3312 * [I] lpLVItem : valid pointer to new subitem atttributes
3313 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3314 * [O] bChanged : will be set to TRUE if the item really changed
3320 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3323 SUBITEM_INFO *lpSubItem;
3325 /* we do not support subitems for virtual listviews */
3326 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3328 /* set subitem only if column is present */
3329 if (lpLVItem->iSubItem >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3331 /* First do some sanity checks */
3332 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3333 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3335 /* get the subitem structure, and create it if not there */
3336 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3337 assert (hdpaSubItems);
3339 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3342 SUBITEM_INFO *tmpSubItem;
3345 lpSubItem = (SUBITEM_INFO *)COMCTL32_Alloc(sizeof(SUBITEM_INFO));
3346 if (!lpSubItem) return FALSE;
3347 /* we could binary search here, if need be...*/
3348 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3350 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3351 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3353 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3355 COMCTL32_Free(lpSubItem);
3358 lpSubItem->iSubItem = lpLVItem->iSubItem;
3362 if (lpLVItem->mask & LVIF_IMAGE)
3363 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3365 lpSubItem->hdr.iImage = lpLVItem->iImage;
3369 if (lpLVItem->mask & LVIF_TEXT)
3370 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3372 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3381 * Sets item attributes.
3384 * [I] infoPtr : valid pointer to the listview structure
3385 * [I] lpLVItem : new item atttributes
3386 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3392 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3394 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3395 LPWSTR pszText = NULL;
3396 BOOL bResult, bChanged = FALSE;
3398 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3400 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3403 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3404 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3406 pszText = lpLVItem->pszText;
3407 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3410 /* actually set the fields */
3411 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3413 if (lpLVItem->iSubItem)
3414 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3416 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3418 /* redraw item, if necessary */
3419 if (bChanged && !infoPtr->bIsDrawing)
3421 /* this little optimization eliminates some nasty flicker */
3422 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3423 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3424 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3426 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3431 textfreeT(lpLVItem->pszText, isW);
3432 ((LVITEMW *)lpLVItem)->pszText = pszText;
3440 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3443 * [I] infoPtr : valid pointer to the listview structure
3448 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3450 LONG lStyle = infoPtr->dwStyle;
3451 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3453 SCROLLINFO scrollInfo;
3455 scrollInfo.cbSize = sizeof(SCROLLINFO);
3456 scrollInfo.fMask = SIF_POS;
3458 if (uView == LVS_LIST)
3460 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3461 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3463 else if (uView == LVS_REPORT)
3465 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3466 nItem = scrollInfo.nPos;
3470 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3471 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3474 TRACE("nItem=%d\n", nItem);
3482 * Erases the background of the given rectangle
3485 * [I] infoPtr : valid pointer to the listview structure
3486 * [I] hdc : device context handle
3487 * [I] lprcBox : clipping rectangle
3493 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3495 if (!infoPtr->hBkBrush) return FALSE;
3497 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3499 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3507 * [I] infoPtr : valid pointer to the listview structure
3508 * [I] hdc : device context handle
3509 * [I] nItem : item index
3510 * [I] nSubItem : subitem index
3511 * [I] pos : item position in client coordinates
3512 * [I] cdmode : custom draw mode
3518 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3520 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3521 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3522 WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3523 DWORD cdsubitemmode = CDRF_DODEFAULT;
3524 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3525 NMLVCUSTOMDRAW nmlvcd;
3529 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3531 /* get information needed for drawing the item */
3532 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3533 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3534 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3535 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3536 lvItem.iItem = nItem;
3537 lvItem.iSubItem = nSubItem;
3540 lvItem.cchTextMax = DISP_TEXT_SIZE;
3541 lvItem.pszText = szDispText;
3542 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3543 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3544 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3545 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3546 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3548 /* now check if we need to update the focus rectangle */
3549 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3551 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3552 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3553 OffsetRect(&rcBox, pos.x, pos.y);
3554 OffsetRect(&rcState, pos.x, pos.y);
3555 OffsetRect(&rcIcon, pos.x, pos.y);
3556 OffsetRect(&rcLabel, pos.x, pos.y);
3557 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3558 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3560 /* fill in the custom draw structure */
3561 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3563 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3564 if (cdmode & CDRF_NOTIFYITEMDRAW)
3565 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3566 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3567 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3568 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3569 prepaint_setup(infoPtr, hdc, &nmlvcd);
3571 /* in full row select, subitems, will just use main item's colors */
3572 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3573 nmlvcd.clrTextBk = CLR_NONE;
3576 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3578 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3581 TRACE("uStateImage=%d\n", uStateImage);
3582 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3587 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3588 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3590 TRACE("iImage=%d\n", lvItem.iImage);
3591 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3592 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3595 /* Don't bother painting item being edited */
3596 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3598 /* draw the selection background, if we're drawing the main item */
3602 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3603 rcSelect.right = rcBox.right;
3605 if (nmlvcd.clrTextBk != CLR_NONE)
3606 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3607 if(lprcFocus) *lprcFocus = rcSelect;
3610 /* figure out the text drawing flags */
3611 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3612 if (uView == LVS_ICON)
3613 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3616 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3618 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3619 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3620 default: uFormat |= DT_LEFT;
3623 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3625 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3626 else rcLabel.left += LABEL_HOR_PADDING;
3628 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3629 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3632 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3633 notify_postpaint(infoPtr, &nmlvcd);
3639 * Draws listview items when in owner draw mode.
3642 * [I] infoPtr : valid pointer to the listview structure
3643 * [I] hdc : device context handle
3648 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3650 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3651 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3652 DWORD cditemmode = CDRF_DODEFAULT;
3653 NMLVCUSTOMDRAW nmlvcd;
3654 POINT Origin, Position;
3660 ZeroMemory(&dis, sizeof(dis));
3662 /* Get scroll info once before loop */
3663 LISTVIEW_GetOrigin(infoPtr, &Origin);
3665 /* iterate through the invalidated rows */
3666 while(iterator_next(i))
3668 item.iItem = i->nItem;
3670 item.mask = LVIF_PARAM | LVIF_STATE;
3671 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3672 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3674 dis.CtlType = ODT_LISTVIEW;
3676 dis.itemID = item.iItem;
3677 dis.itemAction = ODA_DRAWENTIRE;
3679 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3680 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3681 dis.hwndItem = infoPtr->hwndSelf;
3683 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3684 dis.rcItem.left = Position.x + Origin.x;
3685 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3686 dis.rcItem.top = Position.y + Origin.y;
3687 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3688 dis.itemData = item.lParam;
3690 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3692 if (cdmode & CDRF_NOTIFYITEMDRAW)
3694 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3695 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3698 if (!(cditemmode & CDRF_SKIPDEFAULT))
3700 prepaint_setup (infoPtr, hdc, &nmlvcd);
3701 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3704 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3705 notify_postpaint(infoPtr, &nmlvcd);
3711 * Draws listview items when in report display mode.
3714 * [I] infoPtr : valid pointer to the listview structure
3715 * [I] hdc : device context handle
3716 * [I] cdmode : custom draw mode
3721 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3724 RECT rcClip, rcItem;
3725 POINT Origin, Position;
3731 /* figure out what to draw */
3732 rgntype = GetClipBox(hdc, &rcClip);
3733 if (rgntype == NULLREGION) return;
3735 /* Get scroll info once before loop */
3736 LISTVIEW_GetOrigin(infoPtr, &Origin);
3738 /* narrow down the columns we need to paint */
3739 for(colRange.lower = 0; colRange.lower < infoPtr->hdpaColumns->nItemCount; colRange.lower++)
3741 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3742 if (rcItem.right + Origin.x >= rcClip.left) break;
3744 for(colRange.upper = infoPtr->hdpaColumns->nItemCount; colRange.upper > 0; colRange.upper--)
3746 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3747 if (rcItem.left + Origin.x < rcClip.right) break;
3749 iterator_rangeitems(&j, colRange);
3751 /* in full row select, we _have_ to draw the main item */
3752 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3755 /* iterate through the invalidated rows */
3756 while(iterator_next(i))
3758 /* iterate through the invalidated columns */
3759 while(iterator_next(&j))
3761 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3762 Position.x += Origin.x;
3763 Position.y += Origin.y;
3765 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3767 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3769 rcItem.bottom = infoPtr->nItemHeight;
3770 OffsetRect(&rcItem, Position.x, Position.y);
3771 if (!RectVisible(hdc, &rcItem)) continue;
3774 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3777 iterator_destroy(&j);
3782 * Draws listview items when in list display mode.
3785 * [I] infoPtr : valid pointer to the listview structure
3786 * [I] hdc : device context handle
3787 * [I] cdmode : custom draw mode
3792 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3794 POINT Origin, Position;
3796 /* Get scroll info once before loop */
3797 LISTVIEW_GetOrigin(infoPtr, &Origin);
3799 while(iterator_prev(i))
3801 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3802 Position.x += Origin.x;
3803 Position.y += Origin.y;
3805 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3812 * Draws listview items.
3815 * [I] infoPtr : valid pointer to the listview structure
3816 * [I] hdc : device context handle
3821 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3823 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3824 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3825 NMLVCUSTOMDRAW nmlvcd;
3832 LISTVIEW_DUMP(infoPtr);
3834 infoPtr->bIsDrawing = TRUE;
3836 /* save dc values we're gonna trash while drawing */
3837 hOldFont = SelectObject(hdc, infoPtr->hFont);
3838 oldBkMode = GetBkMode(hdc);
3839 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3840 oldTextColor = GetTextColor(hdc);
3842 oldClrTextBk = infoPtr->clrTextBk;
3843 oldClrText = infoPtr->clrText;
3845 infoPtr->cditemmode = CDRF_DODEFAULT;
3847 GetClientRect(infoPtr->hwndSelf, &rcClient);
3848 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3849 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3850 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3851 prepaint_setup(infoPtr, hdc, &nmlvcd);
3853 /* Use these colors to draw the items */
3854 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3855 infoPtr->clrText = nmlvcd.clrText;
3857 /* nothing to draw */
3858 if(infoPtr->nItemCount == 0) goto enddraw;
3860 /* figure out what we need to draw */
3861 iterator_visibleitems(&i, infoPtr, hdc);
3863 /* send cache hint notification */
3864 if (infoPtr->dwStyle & LVS_OWNERDATA)
3866 RANGE range = iterator_range(&i);
3869 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3870 nmlv.iFrom = range.lower;
3871 nmlv.iTo = range.upper - 1;
3872 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3875 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3876 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3879 if (uView == LVS_REPORT)
3880 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3881 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3882 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3884 /* if we have a focus rect, draw it */
3885 if (infoPtr->bFocus)
3886 DrawFocusRect(hdc, &infoPtr->rcFocus);
3888 iterator_destroy(&i);
3891 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3892 notify_postpaint(infoPtr, &nmlvcd);
3894 infoPtr->clrTextBk = oldClrTextBk;
3895 infoPtr->clrText = oldClrText;
3897 SelectObject(hdc, hOldFont);
3898 SetBkMode(hdc, oldBkMode);
3899 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3900 SetTextColor(hdc, oldTextColor);
3901 infoPtr->bIsDrawing = FALSE;
3907 * Calculates the approximate width and height of a given number of items.
3910 * [I] infoPtr : valid pointer to the listview structure
3911 * [I] nItemCount : number of items
3912 * [I] wWidth : width
3913 * [I] wHeight : height
3916 * Returns a DWORD. The width in the low word and the height in high word.
3918 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3919 WORD wWidth, WORD wHeight)
3921 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3922 INT nItemCountPerColumn = 1;
3923 INT nColumnCount = 0;
3924 DWORD dwViewRect = 0;
3926 if (nItemCount == -1)
3927 nItemCount = infoPtr->nItemCount;
3929 if (uView == LVS_LIST)
3931 if (wHeight == 0xFFFF)
3933 /* use current height */
3934 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3937 if (wHeight < infoPtr->nItemHeight)
3938 wHeight = infoPtr->nItemHeight;
3942 if (infoPtr->nItemHeight > 0)
3944 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3945 if (nItemCountPerColumn == 0)
3946 nItemCountPerColumn = 1;
3948 if (nItemCount % nItemCountPerColumn != 0)
3949 nColumnCount = nItemCount / nItemCountPerColumn;
3951 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3955 /* Microsoft padding magic */
3956 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3957 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3959 dwViewRect = MAKELONG(wWidth, wHeight);
3961 else if (uView == LVS_REPORT)
3962 FIXME("uView == LVS_REPORT: not implemented\n");
3963 else if (uView == LVS_SMALLICON)
3964 FIXME("uView == LVS_SMALLICON: not implemented\n");
3965 else if (uView == LVS_ICON)
3966 FIXME("uView == LVS_ICON: not implemented\n");
3971 /* << LISTVIEW_CreateDragImage >> */
3976 * Removes all listview items and subitems.
3979 * [I] infoPtr : valid pointer to the listview structure
3985 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3988 HDPA hdpaSubItems = NULL;
3995 /* we do it directly, to avoid notifications */
3996 ranges_clear(infoPtr->selectionRanges);
3997 infoPtr->nSelectionMark = -1;
3998 infoPtr->nFocusedItem = -1;
3999 SetRectEmpty(&infoPtr->rcFocus);
4000 /* But we are supposed to leave nHotItem as is! */
4003 /* send LVN_DELETEALLITEMS notification */
4004 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4006 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4008 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4010 /* send LVN_DELETEITEM notification, if not supressed */
4011 if (!bSuppress) notify_deleteitem(infoPtr, i);
4012 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4014 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4015 for (j = 0; j < hdpaSubItems->nItemCount; j++)
4017 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4018 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
4019 COMCTL32_Free(hdrItem);
4021 DPA_Destroy(hdpaSubItems);
4022 DPA_DeletePtr(infoPtr->hdpaItems, i);
4024 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4025 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4026 infoPtr->nItemCount --;
4029 LISTVIEW_UpdateScroll(infoPtr);
4031 LISTVIEW_InvalidateList(infoPtr);
4038 * Scrolls, and updates the columns, when a column is changing width.
4041 * [I] infoPtr : valid pointer to the listview structure
4042 * [I] nColumn : column to scroll
4043 * [I] dx : amount of scroll, in pixels
4048 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4050 COLUMN_INFO *lpColumnInfo;
4054 if (nColumn < 0 || infoPtr->hdpaColumns->nItemCount < 1) return;
4055 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, infoPtr->hdpaColumns->nItemCount - 1));
4056 rcCol = lpColumnInfo->rcHeader;
4057 if (nColumn >= infoPtr->hdpaColumns->nItemCount)
4058 rcCol.left = rcCol.right;
4060 /* ajust the other columns */
4061 for (nCol = nColumn; nCol < infoPtr->hdpaColumns->nItemCount; nCol++)
4063 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4064 lpColumnInfo->rcHeader.left += dx;
4065 lpColumnInfo->rcHeader.right += dx;
4068 /* do not update screen if not in report mode */
4069 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4071 /* if we have a focus, must first erase the focus rect */
4072 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4074 /* Need to reset the item width when inserting a new column */
4075 infoPtr->nItemWidth += dx;
4077 LISTVIEW_UpdateScroll(infoPtr);
4079 /* scroll to cover the deleted column, and invalidate for redraw */
4080 rcOld = infoPtr->rcList;
4081 rcOld.left = rcCol.left;
4082 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4084 /* we can restore focus now */
4085 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4090 * Removes a column from the listview control.
4093 * [I] infoPtr : valid pointer to the listview structure
4094 * [I] nColumn : column index
4100 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4104 TRACE("nColumn=%d\n", nColumn);
4106 if (nColumn < 0 || infoPtr->hdpaColumns->nItemCount == 0
4107 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
4109 /* While the MSDN specifically says that column zero should not be deleted,
4110 it does in fact work on WinNT, and at least one app depends on it. On
4111 WinNT, deleting column zero deletes the last column of items but the
4112 first header. Since no app will ever depend on that bizarre behavior,
4113 we just delete the last column including the header.
4116 nColumn = infoPtr->hdpaColumns->nItemCount - 1;
4118 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4120 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4123 COMCTL32_Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4124 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4126 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4128 SUBITEM_INFO *lpSubItem, *lpDelItem;
4130 INT nItem, nSubItem, i;
4133 return LISTVIEW_DeleteAllItems(infoPtr);
4135 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4137 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4140 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4142 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4143 if (lpSubItem->iSubItem == nColumn)
4146 lpDelItem = lpSubItem;
4148 else if (lpSubItem->iSubItem > nColumn)
4150 lpSubItem->iSubItem--;
4154 /* if we found our subitem, zapp it */
4158 if (is_textW(lpDelItem->hdr.pszText))
4159 COMCTL32_Free(lpDelItem->hdr.pszText);
4162 COMCTL32_Free(lpDelItem);
4164 /* free dpa memory */
4165 DPA_DeletePtr(hdpaSubItems, nSubItem);
4170 /* update the other column info */
4171 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4178 * Invalidates the listview after an item's insertion or deletion.
4181 * [I] infoPtr : valid pointer to the listview structure
4182 * [I] nItem : item index
4183 * [I] dir : -1 if deleting, 1 if inserting
4188 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4190 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4191 INT nPerCol, nItemCol, nItemRow;
4195 /* if we don't refresh, what's the point of scrolling? */
4196 if (!is_redrawing(infoPtr)) return;
4198 assert (abs(dir) == 1);
4200 /* arrange icons if autoarrange is on */
4201 if (is_autoarrange(infoPtr))
4203 BOOL arrange = TRUE;
4204 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4205 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4206 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4209 /* scrollbars need updating */
4210 LISTVIEW_UpdateScroll(infoPtr);
4212 /* figure out the item's position */
4213 if (uView == LVS_REPORT)
4214 nPerCol = infoPtr->nItemCount + 1;
4215 else if (uView == LVS_LIST)
4216 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4217 else /* LVS_ICON, or LVS_SMALLICON */
4220 nItemCol = nItem / nPerCol;
4221 nItemRow = nItem % nPerCol;
4222 LISTVIEW_GetOrigin(infoPtr, &Origin);
4224 /* move the items below up a slot */
4225 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4226 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4227 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4228 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4229 OffsetRect(&rcScroll, Origin.x, Origin.y);
4230 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4231 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4233 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4234 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4235 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4238 /* report has only that column, so we're done */
4239 if (uView == LVS_REPORT) return;
4241 /* now for LISTs, we have to deal with the columns to the right */
4242 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4244 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4245 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4246 OffsetRect(&rcScroll, Origin.x, Origin.y);
4247 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4248 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4249 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4254 * Removes an item from the listview control.
4257 * [I] infoPtr : valid pointer to the listview structure
4258 * [I] nItem : item index
4264 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4266 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4269 TRACE("(nItem=%d)\n", nItem);
4271 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4273 /* remove selection, and focus */
4275 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4276 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4278 /* send LVN_DELETEITEM notification. */
4279 notify_deleteitem(infoPtr, nItem);
4281 /* we need to do this here, because we'll be deleting stuff */
4282 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4283 LISTVIEW_InvalidateItem(infoPtr, nItem);
4285 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4291 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4292 for (i = 0; i < hdpaSubItems->nItemCount; i++)
4294 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4295 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
4296 COMCTL32_Free(hdrItem);
4298 DPA_Destroy(hdpaSubItems);
4301 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4303 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4304 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4307 infoPtr->nItemCount--;
4308 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4310 /* now is the invalidation fun */
4311 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4318 * Callback implementation for editlabel control
4321 * [I] infoPtr : valid pointer to the listview structure
4322 * [I] pszText : modified text
4323 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4329 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4331 NMLVDISPINFOW dispInfo;
4333 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4335 ZeroMemory(&dispInfo, sizeof(dispInfo));
4336 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4337 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4338 dispInfo.item.iSubItem = 0;
4339 dispInfo.item.stateMask = ~0;
4340 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4341 /* add the text from the edit in */
4342 dispInfo.item.mask |= LVIF_TEXT;
4343 dispInfo.item.pszText = pszText;
4344 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4346 /* Do we need to update the Item Text */
4347 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4348 if (!pszText) return TRUE;
4350 ZeroMemory(&dispInfo, sizeof(dispInfo));
4351 dispInfo.item.mask = LVIF_TEXT;
4352 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4353 dispInfo.item.iSubItem = 0;
4354 dispInfo.item.pszText = pszText;
4355 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4356 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4361 * Begin in place editing of specified list view item
4364 * [I] infoPtr : valid pointer to the listview structure
4365 * [I] nItem : item index
4366 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4372 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4374 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4375 NMLVDISPINFOW dispInfo;
4378 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4380 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4381 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4383 infoPtr->nEditLabelItem = nItem;
4385 /* Is the EditBox still there, if so remove it */
4386 if(infoPtr->hwndEdit != 0)
4388 SetFocus(infoPtr->hwndSelf);
4389 infoPtr->hwndEdit = 0;
4392 LISTVIEW_SetSelection(infoPtr, nItem);
4393 LISTVIEW_SetItemFocus(infoPtr, nItem);
4394 LISTVIEW_InvalidateItem(infoPtr, nItem);
4396 rect.left = LVIR_LABEL;
4397 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4399 ZeroMemory(&dispInfo, sizeof(dispInfo));
4400 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4401 dispInfo.item.iItem = nItem;
4402 dispInfo.item.iSubItem = 0;
4403 dispInfo.item.stateMask = ~0;
4404 dispInfo.item.pszText = szDispText;
4405 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4406 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4408 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4409 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4410 if (!infoPtr->hwndEdit) return 0;
4412 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4414 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4415 infoPtr->hwndEdit = 0;
4419 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4420 SetFocus(infoPtr->hwndEdit);
4421 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4422 return infoPtr->hwndEdit;
4428 * Ensures the specified item is visible, scrolling into view if necessary.
4431 * [I] infoPtr : valid pointer to the listview structure
4432 * [I] nItem : item index
4433 * [I] bPartial : partially or entirely visible
4439 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4441 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4442 INT nScrollPosHeight = 0;
4443 INT nScrollPosWidth = 0;
4444 INT nHorzAdjust = 0;
4445 INT nVertAdjust = 0;
4448 RECT rcItem, rcTemp;
4450 rcItem.left = LVIR_BOUNDS;
4451 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4453 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4455 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4457 /* scroll left/right, but in LVS_REPORT mode */
4458 if (uView == LVS_LIST)
4459 nScrollPosWidth = infoPtr->nItemWidth;
4460 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4461 nScrollPosWidth = 1;
4463 if (rcItem.left < infoPtr->rcList.left)
4466 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4471 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4475 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4477 /* scroll up/down, but not in LVS_LIST mode */
4478 if (uView == LVS_REPORT)
4479 nScrollPosHeight = infoPtr->nItemHeight;
4480 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4481 nScrollPosHeight = 1;
4483 if (rcItem.top < infoPtr->rcList.top)
4486 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4491 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4495 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4497 if (nScrollPosWidth)
4499 INT diff = nHorzDiff / nScrollPosWidth;
4500 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4501 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4504 if (nScrollPosHeight)
4506 INT diff = nVertDiff / nScrollPosHeight;
4507 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4508 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4516 * Searches for an item with specific characteristics.
4519 * [I] hwnd : window handle
4520 * [I] nStart : base item index
4521 * [I] lpFindInfo : item information to look for
4524 * SUCCESS : index of item
4527 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4528 const LVFINDINFOW *lpFindInfo)
4530 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4531 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4532 BOOL bWrap = FALSE, bNearest = FALSE;
4533 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4534 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4535 POINT Position, Destination;
4538 if (!lpFindInfo || nItem < 0) return -1;
4541 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4543 lvItem.mask |= LVIF_TEXT;
4544 lvItem.pszText = szDispText;
4545 lvItem.cchTextMax = DISP_TEXT_SIZE;
4548 if (lpFindInfo->flags & LVFI_WRAP)
4551 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4552 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4557 LISTVIEW_GetOrigin(infoPtr, &Origin);
4558 Destination.x = lpFindInfo->pt.x - Origin.x;
4559 Destination.y = lpFindInfo->pt.y - Origin.y;
4560 switch(lpFindInfo->vkDirection)
4562 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4563 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4564 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4565 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4566 case VK_HOME: Destination.x = Destination.y = 0; break;
4567 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4568 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4570 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4571 Destination.x = rcArea.right;
4572 Destination.y = rcArea.bottom;
4574 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4579 /* if LVFI_PARAM is specified, all other flags are ignored */
4580 if (lpFindInfo->flags & LVFI_PARAM)
4582 lvItem.mask |= LVIF_PARAM;
4584 lvItem.mask &= ~LVIF_TEXT;
4588 for (; nItem < nLast; nItem++)
4590 lvItem.iItem = nItem;
4591 lvItem.iSubItem = 0;
4592 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4594 if (lvItem.mask & LVIF_PARAM)
4596 if (lpFindInfo->lParam == lvItem.lParam)
4602 if (lvItem.mask & LVIF_TEXT)
4604 if (lpFindInfo->flags & LVFI_PARTIAL)
4606 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4610 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4614 if (!bNearest) return nItem;
4616 /* This is very inefficient. To do a good job here,
4617 * we need a sorted array of (x,y) item positions */
4618 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4620 /* compute the distance^2 to the destination */
4621 xdist = Destination.x - Position.x;
4622 ydist = Destination.y - Position.y;
4623 dist = xdist * xdist + ydist * ydist;
4625 /* remember the distance, and item if it's closer */
4629 nNearestItem = nItem;
4636 nLast = min(nStart + 1, infoPtr->nItemCount);
4641 return nNearestItem;
4646 * Searches for an item with specific characteristics.
4649 * [I] hwnd : window handle
4650 * [I] nStart : base item index
4651 * [I] lpFindInfo : item information to look for
4654 * SUCCESS : index of item
4657 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4658 const LVFINDINFOA *lpFindInfo)
4660 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4664 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4665 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4666 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4667 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4673 * Retrieves the background image of the listview control.
4676 * [I] infoPtr : valid pointer to the listview structure
4677 * [O] lpBkImage : background image attributes
4683 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4685 /* FIXME (listview, "empty stub!\n"); */
4691 * Retrieves column attributes.
4694 * [I] infoPtr : valid pointer to the listview structure
4695 * [I] nColumn : column index
4696 * [IO] lpColumn : column information
4697 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4698 * otherwise it is in fact a LPLVCOLUMNA
4704 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4706 COLUMN_INFO *lpColumnInfo;
4709 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
4710 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4712 /* initialize memory */
4713 ZeroMemory(&hdi, sizeof(hdi));
4715 if (lpColumn->mask & LVCF_TEXT)
4717 hdi.mask |= HDI_TEXT;
4718 hdi.pszText = lpColumn->pszText;
4719 hdi.cchTextMax = lpColumn->cchTextMax;
4722 if (lpColumn->mask & LVCF_IMAGE)
4723 hdi.mask |= HDI_IMAGE;
4725 if (lpColumn->mask & LVCF_ORDER)
4726 hdi.mask |= HDI_ORDER;
4728 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4730 if (lpColumn->mask & LVCF_FMT)
4731 lpColumn->fmt = lpColumnInfo->fmt;
4733 if (lpColumn->mask & LVCF_WIDTH)
4734 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4736 if (lpColumn->mask & LVCF_IMAGE)
4737 lpColumn->iImage = hdi.iImage;
4739 if (lpColumn->mask & LVCF_ORDER)
4740 lpColumn->iOrder = hdi.iOrder;
4746 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4753 /* FIXME: little hack */
4754 for (i = 0; i < iCount; i++)
4762 * Retrieves the column width.
4765 * [I] infoPtr : valid pointer to the listview structure
4766 * [I] int : column index
4769 * SUCCESS : column width
4772 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4774 INT nColumnWidth = 0;
4777 TRACE("nColumn=%d\n", nColumn);
4779 /* we have a 'column' in LIST and REPORT mode only */
4780 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4783 nColumnWidth = infoPtr->nItemWidth;
4786 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return 0;
4787 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4788 nColumnWidth = rcHeader.right - rcHeader.left;
4792 TRACE("nColumnWidth=%d\n", nColumnWidth);
4793 return nColumnWidth;
4798 * In list or report display mode, retrieves the number of items that can fit
4799 * vertically in the visible area. In icon or small icon display mode,
4800 * retrieves the total number of visible items.
4803 * [I] infoPtr : valid pointer to the listview structure
4806 * Number of fully visible items.
4808 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4810 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4814 return infoPtr->nItemCount;
4816 return LISTVIEW_GetCountPerColumn(infoPtr);
4818 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4826 * Retrieves an image list handle.
4829 * [I] infoPtr : valid pointer to the listview structure
4830 * [I] nImageList : image list identifier
4833 * SUCCESS : image list handle
4836 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4840 case LVSIL_NORMAL: return infoPtr->himlNormal;
4841 case LVSIL_SMALL: return infoPtr->himlSmall;
4842 case LVSIL_STATE: return infoPtr->himlState;
4847 /* LISTVIEW_GetISearchString */
4851 * Retrieves item attributes.
4854 * [I] hwnd : window handle
4855 * [IO] lpLVItem : item info
4856 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4857 * if FALSE, the lpLVItem is a LPLVITEMA.
4860 * This is the internal 'GetItem' interface -- it tries to
4861 * be smart, and avoids text copies, if possible, by modifing
4862 * lpLVItem->pszText to point to the text string. Please note
4863 * that this is not always possible (e.g. OWNERDATA), so on
4864 * entry you *must* supply valid values for pszText, and cchTextMax.
4865 * The only difference to the documented interface is that upon
4866 * return, you should use *only* the lpLVItem->pszText, rather than
4867 * the buffer pointer you provided on input. Most code already does
4868 * that, so it's not a problem.
4869 * For the two cases when the text must be copied (that is,
4870 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4876 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4878 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
4879 NMLVDISPINFOW dispInfo;
4884 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4886 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4889 if (lpLVItem->mask == 0) return TRUE;
4891 /* a quick optimization if all we're asked is the focus state
4892 * these queries are worth optimising since they are common,
4893 * and can be answered in constant time, without the heavy accesses */
4894 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
4895 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4897 lpLVItem->state = 0;
4898 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4899 lpLVItem->state |= LVIS_FOCUSED;
4903 ZeroMemory(&dispInfo, sizeof(dispInfo));
4905 /* if the app stores all the data, handle it separately */
4906 if (infoPtr->dwStyle & LVS_OWNERDATA)
4908 dispInfo.item.state = 0;
4910 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
4911 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
4913 /* NOTE: copy only fields which we _know_ are initialized, some apps
4914 * depend on the uninitialized fields being 0 */
4915 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
4916 dispInfo.item.iItem = lpLVItem->iItem;
4917 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4918 if (lpLVItem->mask & LVIF_TEXT)
4920 dispInfo.item.pszText = lpLVItem->pszText;
4921 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4923 if (lpLVItem->mask & LVIF_STATE)
4924 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4925 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4926 dispInfo.item.stateMask = lpLVItem->stateMask;
4927 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
4929 /* full size structure expected - _WIN32IE >= 0x560 */
4930 *lpLVItem = dispInfo.item;
4932 else if (lpLVItem->mask & LVIF_INDENT)
4934 /* indent member expected - _WIN32IE >= 0x300 */
4935 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
4939 /* minimal structure expected */
4940 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
4942 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4945 /* make sure lParam is zeroed out */
4946 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
4948 /* we store only a little state, so if we're not asked, we're done */
4949 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4951 /* if focus is handled by us, report it */
4952 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4954 lpLVItem->state &= ~LVIS_FOCUSED;
4955 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4956 lpLVItem->state |= LVIS_FOCUSED;
4959 /* and do the same for selection, if we handle it */
4960 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4962 lpLVItem->state &= ~LVIS_SELECTED;
4963 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4964 lpLVItem->state |= LVIS_SELECTED;
4970 /* find the item and subitem structures before we proceed */
4971 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4972 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4975 if (lpLVItem->iSubItem)
4977 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4978 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
4981 pItemHdr = &lpItem->hdr;
4983 /* Do we need to query the state from the app? */
4984 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4986 dispInfo.item.mask |= LVIF_STATE;
4987 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4990 /* Do we need to enquire about the image? */
4991 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK)
4992 dispInfo.item.mask |= LVIF_IMAGE;
4994 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
4995 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4997 dispInfo.item.mask |= LVIF_TEXT;
4998 dispInfo.item.pszText = lpLVItem->pszText;
4999 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5000 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5001 *dispInfo.item.pszText = '\0';
5004 /* If we don't have all the requested info, query the application */
5005 if (dispInfo.item.mask != 0)
5007 dispInfo.item.iItem = lpLVItem->iItem;
5008 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5009 dispInfo.item.lParam = lpItem->lParam;
5010 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5011 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5014 /* we should not store values for subitems */
5015 if (lpLVItem->iSubItem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5017 /* Now, handle the iImage field */
5018 if (dispInfo.item.mask & LVIF_IMAGE)
5020 lpLVItem->iImage = dispInfo.item.iImage;
5021 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5022 pItemHdr->iImage = dispInfo.item.iImage;
5024 else if (lpLVItem->mask & LVIF_IMAGE)
5025 lpLVItem->iImage = pItemHdr->iImage;
5027 /* The pszText field */
5028 if (dispInfo.item.mask & LVIF_TEXT)
5030 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5031 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5033 lpLVItem->pszText = dispInfo.item.pszText;
5035 else if (lpLVItem->mask & LVIF_TEXT)
5037 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5038 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5041 /* if this is a subitem, we're done */
5042 if (lpLVItem->iSubItem) return TRUE;
5044 /* Next is the lParam field */
5045 if (dispInfo.item.mask & LVIF_PARAM)
5047 lpLVItem->lParam = dispInfo.item.lParam;
5048 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5049 lpItem->lParam = dispInfo.item.lParam;
5051 else if (lpLVItem->mask & LVIF_PARAM)
5052 lpLVItem->lParam = lpItem->lParam;
5054 /* ... the state field (this one is different due to uCallbackmask) */
5055 if (lpLVItem->mask & LVIF_STATE)
5057 lpLVItem->state = lpItem->state;
5058 if (dispInfo.item.mask & LVIF_STATE)
5060 lpLVItem->state &= ~dispInfo.item.stateMask;
5061 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5063 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5065 lpLVItem->state &= ~LVIS_FOCUSED;
5066 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5067 lpLVItem->state |= LVIS_FOCUSED;
5069 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5071 lpLVItem->state &= ~LVIS_SELECTED;
5072 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5073 lpLVItem->state |= LVIS_SELECTED;
5077 /* and last, but not least, the indent field */
5078 if (lpLVItem->mask & LVIF_INDENT)
5079 lpLVItem->iIndent = lpItem->iIndent;
5086 * Retrieves item attributes.
5089 * [I] hwnd : window handle
5090 * [IO] lpLVItem : item info
5091 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5092 * if FALSE, the lpLVItem is a LPLVITEMA.
5095 * This is the external 'GetItem' interface -- it properly copies
5096 * the text in the provided buffer.
5102 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5107 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5110 pszText = lpLVItem->pszText;
5111 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5112 if (bResult && lpLVItem->pszText != pszText)
5113 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5114 lpLVItem->pszText = pszText;
5122 * Retrieves the position (upper-left) of the listview control item.
5123 * Note that for LVS_ICON style, the upper-left is that of the icon
5124 * and not the bounding box.
5127 * [I] infoPtr : valid pointer to the listview structure
5128 * [I] nItem : item index
5129 * [O] lpptPosition : coordinate information
5135 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5137 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5140 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5142 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5144 LISTVIEW_GetOrigin(infoPtr, &Origin);
5145 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5147 if (uView == LVS_ICON)
5149 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5150 lpptPosition->y += ICON_TOP_PADDING;
5152 lpptPosition->x += Origin.x;
5153 lpptPosition->y += Origin.y;
5155 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5162 * Retrieves the bounding rectangle for a listview control item.
5165 * [I] infoPtr : valid pointer to the listview structure
5166 * [I] nItem : item index
5167 * [IO] lprc : bounding rectangle coordinates
5168 * lprc->left specifies the portion of the item for which the bounding
5169 * rectangle will be retrieved.
5171 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5172 * including the icon and label.
5175 * * Experiment shows that native control returns:
5176 * * width = min (48, length of text line)
5177 * * .left = position.x - (width - iconsize.cx)/2
5178 * * .right = .left + width
5179 * * height = #lines of text * ntmHeight + icon height + 8
5180 * * .top = position.y - 2
5181 * * .bottom = .top + height
5182 * * separation between items .y = itemSpacing.cy - height
5183 * * .x = itemSpacing.cx - width
5184 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5187 * * Experiment shows that native control returns:
5188 * * width = iconSize.cx + 16
5189 * * .left = position.x - (width - iconsize.cx)/2
5190 * * .right = .left + width
5191 * * height = iconSize.cy + 4
5192 * * .top = position.y - 2
5193 * * .bottom = .top + height
5194 * * separation between items .y = itemSpacing.cy - height
5195 * * .x = itemSpacing.cx - width
5196 * LVIR_LABEL Returns the bounding rectangle of the item text.
5199 * * Experiment shows that native control returns:
5200 * * width = text length
5201 * * .left = position.x - width/2
5202 * * .right = .left + width
5203 * * height = ntmH * linecount + 2
5204 * * .top = position.y + iconSize.cy + 6
5205 * * .bottom = .top + height
5206 * * separation between items .y = itemSpacing.cy - height
5207 * * .x = itemSpacing.cx - width
5208 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5209 * rectangles, but excludes columns in report view.
5216 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5217 * upon whether the window has the focus currently and on whether the item
5218 * is the one with the focus. Ensure that the control's record of which
5219 * item has the focus agrees with the items' records.
5221 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5223 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5224 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5225 BOOL doLabel = TRUE, oversizedBox = FALSE;
5226 POINT Position, Origin;
5230 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5232 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5234 LISTVIEW_GetOrigin(infoPtr, &Origin);
5235 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5237 /* Be smart and try to figure out the minimum we have to do */
5238 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5239 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5240 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5241 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5242 oversizedBox = TRUE;
5244 /* get what we need from the item before hand, so we make
5245 * only one request. This can speed up things, if data
5246 * is stored on the app side */
5248 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5249 if (doLabel) lvItem.mask |= LVIF_TEXT;
5250 lvItem.iItem = nItem;
5251 lvItem.iSubItem = 0;
5252 lvItem.pszText = szDispText;
5253 lvItem.cchTextMax = DISP_TEXT_SIZE;
5254 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5255 /* we got the state already up, simulate it here, to avoid a reget */
5256 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5258 lvItem.mask |= LVIF_STATE;
5259 lvItem.stateMask = LVIS_FOCUSED;
5260 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5263 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5264 lprc->left = LVIR_BOUNDS;
5268 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5272 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5276 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5279 case LVIR_SELECTBOUNDS:
5280 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5281 UnionRect(lprc, lprc, &label_rect);
5285 WARN("Unknown value: %ld\n", lprc->left);
5289 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5291 TRACE(" rect=%s\n", debugrect(lprc));
5298 * Retrieves the spacing between listview control items.
5301 * [I] infoPtr : valid pointer to the listview structure
5302 * [IO] lprc : rectangle to receive the output
5303 * on input, lprc->top = nSubItem
5304 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5306 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5307 * not only those of the first column.
5308 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5314 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5319 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5321 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5322 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5324 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5326 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5328 lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5329 lvItem.iItem = nItem;
5330 lvItem.iSubItem = lprc->top;
5332 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5336 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5341 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5345 ERR("Unknown bounds=%ld\n", lprc->left);
5349 OffsetRect(lprc, Position.x, Position.y);
5356 * Retrieves the width of a label.
5359 * [I] infoPtr : valid pointer to the listview structure
5362 * SUCCESS : string width (in pixels)
5365 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5367 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5370 TRACE("(nItem=%d)\n", nItem);
5372 lvItem.mask = LVIF_TEXT;
5373 lvItem.iItem = nItem;
5374 lvItem.iSubItem = 0;
5375 lvItem.pszText = szDispText;
5376 lvItem.cchTextMax = DISP_TEXT_SIZE;
5377 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5379 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5384 * Retrieves the spacing between listview control items.
5387 * [I] infoPtr : valid pointer to the listview structure
5388 * [I] bSmall : flag for small or large icon
5391 * Horizontal + vertical spacing
5393 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5399 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5403 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5404 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5406 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5413 * Retrieves the state of a listview control item.
5416 * [I] infoPtr : valid pointer to the listview structure
5417 * [I] nItem : item index
5418 * [I] uMask : state mask
5421 * State specified by the mask.
5423 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5427 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5429 lvItem.iItem = nItem;
5430 lvItem.iSubItem = 0;
5431 lvItem.mask = LVIF_STATE;
5432 lvItem.stateMask = uMask;
5433 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5435 return lvItem.state & uMask;
5440 * Retrieves the text of a listview control item or subitem.
5443 * [I] hwnd : window handle
5444 * [I] nItem : item index
5445 * [IO] lpLVItem : item information
5446 * [I] isW : TRUE if lpLVItem is Unicode
5449 * SUCCESS : string length
5452 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5454 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5456 lpLVItem->mask = LVIF_TEXT;
5457 lpLVItem->iItem = nItem;
5458 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5460 return textlenT(lpLVItem->pszText, isW);
5465 * Searches for an item based on properties + relationships.
5468 * [I] infoPtr : valid pointer to the listview structure
5469 * [I] nItem : item index
5470 * [I] uFlags : relationship flag
5473 * SUCCESS : item index
5476 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5478 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5480 LVFINDINFOW lvFindInfo;
5481 INT nCountPerColumn;
5485 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5486 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5488 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5490 if (uFlags & LVNI_CUT)
5493 if (uFlags & LVNI_DROPHILITED)
5494 uMask |= LVIS_DROPHILITED;
5496 if (uFlags & LVNI_FOCUSED)
5497 uMask |= LVIS_FOCUSED;
5499 if (uFlags & LVNI_SELECTED)
5500 uMask |= LVIS_SELECTED;
5502 /* if we're asked for the focused item, that's only one,
5503 * so it's worth optimizing */
5504 if (uFlags & LVNI_FOCUSED)
5506 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5507 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5510 if (uFlags & LVNI_ABOVE)
5512 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5517 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5523 /* Special case for autoarrange - move 'til the top of a list */
5524 if (is_autoarrange(infoPtr))
5526 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5527 while (nItem - nCountPerRow >= 0)
5529 nItem -= nCountPerRow;
5530 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5535 lvFindInfo.flags = LVFI_NEARESTXY;
5536 lvFindInfo.vkDirection = VK_UP;
5537 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5538 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5540 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5545 else if (uFlags & LVNI_BELOW)
5547 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5549 while (nItem < infoPtr->nItemCount)
5552 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5558 /* Special case for autoarrange - move 'til the bottom of a list */
5559 if (is_autoarrange(infoPtr))
5561 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5562 while (nItem + nCountPerRow < infoPtr->nItemCount )
5564 nItem += nCountPerRow;
5565 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5570 lvFindInfo.flags = LVFI_NEARESTXY;
5571 lvFindInfo.vkDirection = VK_DOWN;
5572 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5573 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5575 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5580 else if (uFlags & LVNI_TOLEFT)
5582 if (uView == LVS_LIST)
5584 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5585 while (nItem - nCountPerColumn >= 0)
5587 nItem -= nCountPerColumn;
5588 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5592 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5594 /* Special case for autoarrange - move 'ti the beginning of a row */
5595 if (is_autoarrange(infoPtr))
5597 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5598 while (nItem % nCountPerRow > 0)
5601 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5606 lvFindInfo.flags = LVFI_NEARESTXY;
5607 lvFindInfo.vkDirection = VK_LEFT;
5608 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5609 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5611 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5616 else if (uFlags & LVNI_TORIGHT)
5618 if (uView == LVS_LIST)
5620 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5621 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5623 nItem += nCountPerColumn;
5624 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5628 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5630 /* Special case for autoarrange - move 'til the end of a row */
5631 if (is_autoarrange(infoPtr))
5633 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5634 while (nItem % nCountPerRow < nCountPerRow - 1 )
5637 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5642 lvFindInfo.flags = LVFI_NEARESTXY;
5643 lvFindInfo.vkDirection = VK_RIGHT;
5644 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5645 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5647 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5656 /* search by index */
5657 for (i = nItem; i < infoPtr->nItemCount; i++)
5659 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5667 /* LISTVIEW_GetNumberOfWorkAreas */
5671 * Retrieves the origin coordinates when in icon or small icon display mode.
5674 * [I] infoPtr : valid pointer to the listview structure
5675 * [O] lpptOrigin : coordinate information
5680 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5682 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5683 INT nHorzPos = 0, nVertPos = 0;
5684 SCROLLINFO scrollInfo;
5686 scrollInfo.cbSize = sizeof(SCROLLINFO);
5687 scrollInfo.fMask = SIF_POS;
5689 if ((infoPtr->dwStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5690 nHorzPos = scrollInfo.nPos;
5691 if ((infoPtr->dwStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5692 nVertPos = scrollInfo.nPos;
5694 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5696 lpptOrigin->x = infoPtr->rcList.left;
5697 lpptOrigin->y = infoPtr->rcList.top;
5698 if (uView == LVS_LIST)
5699 nHorzPos *= infoPtr->nItemWidth;
5700 else if (uView == LVS_REPORT)
5701 nVertPos *= infoPtr->nItemHeight;
5703 lpptOrigin->x -= nHorzPos;
5704 lpptOrigin->y -= nVertPos;
5706 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5711 * Retrieves the width of a string.
5714 * [I] hwnd : window handle
5715 * [I] lpszText : text string to process
5716 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5719 * SUCCESS : string width (in pixels)
5722 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5727 if (is_textT(lpszText, isW))
5729 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5730 HDC hdc = GetDC(infoPtr->hwndSelf);
5731 HFONT hOldFont = SelectObject(hdc, hFont);
5734 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5736 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5737 SelectObject(hdc, hOldFont);
5738 ReleaseDC(infoPtr->hwndSelf, hdc);
5740 return stringSize.cx;
5745 * Determines which listview item is located at the specified position.
5748 * [I] infoPtr : valid pointer to the listview structure
5749 * [IO] lpht : hit test information
5750 * [I] subitem : fill out iSubItem.
5751 * [I] select : return the index only if the hit selects the item
5754 * (mm 20001022): We must not allow iSubItem to be touched, for
5755 * an app might pass only a structure with space up to iItem!
5756 * (MS Office 97 does that for instance in the file open dialog)
5759 * SUCCESS : item index
5762 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5764 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5765 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5766 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5767 POINT Origin, Position, opt;
5772 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5776 if (subitem) lpht->iSubItem = 0;
5778 if (infoPtr->rcList.left > lpht->pt.x)
5779 lpht->flags |= LVHT_TOLEFT;
5780 else if (infoPtr->rcList.right < lpht->pt.x)
5781 lpht->flags |= LVHT_TORIGHT;
5783 if (infoPtr->rcList.top > lpht->pt.y)
5784 lpht->flags |= LVHT_ABOVE;
5785 else if (infoPtr->rcList.bottom < lpht->pt.y)
5786 lpht->flags |= LVHT_BELOW;
5788 TRACE("lpht->flags=0x%x\n", lpht->flags);
5789 if (lpht->flags) return -1;
5791 lpht->flags |= LVHT_NOWHERE;
5793 LISTVIEW_GetOrigin(infoPtr, &Origin);
5795 /* first deal with the large items */
5796 rcSearch.left = lpht->pt.x;
5797 rcSearch.top = lpht->pt.y;
5798 rcSearch.right = rcSearch.left + 1;
5799 rcSearch.bottom = rcSearch.top + 1;
5801 iterator_frameditems(&i, infoPtr, &rcSearch);
5802 iterator_next(&i); /* go to first item in the sequence */
5804 iterator_destroy(&i);
5806 TRACE("lpht->iItem=%d\n", iItem);
5807 if (iItem == -1) return -1;
5809 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5810 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5811 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5812 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5813 lvItem.iItem = iItem;
5814 lvItem.iSubItem = 0;
5815 lvItem.pszText = szDispText;
5816 lvItem.cchTextMax = DISP_TEXT_SIZE;
5817 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5818 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5820 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5821 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
5822 opt.x = lpht->pt.x - Position.x - Origin.x;
5823 opt.y = lpht->pt.y - Position.y - Origin.y;
5825 if (uView == LVS_REPORT)
5828 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5829 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5830 if (!PtInRect(&rcBounds, opt)) return -1;
5832 if (PtInRect(&rcIcon, opt))
5833 lpht->flags |= LVHT_ONITEMICON;
5834 else if (PtInRect(&rcLabel, opt))
5835 lpht->flags |= LVHT_ONITEMLABEL;
5836 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5837 lpht->flags |= LVHT_ONITEMSTATEICON;
5838 if (lpht->flags & LVHT_ONITEM)
5839 lpht->flags &= ~LVHT_NOWHERE;
5841 TRACE("lpht->flags=0x%x\n", lpht->flags);
5842 if (uView == LVS_REPORT && subitem)
5846 rcBounds.right = rcBounds.left;
5847 for (j = 0; j < infoPtr->hdpaColumns->nItemCount; j++)
5849 rcBounds.left = rcBounds.right;
5850 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5851 if (PtInRect(&rcBounds, opt))
5859 if (select && !(uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)))
5861 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5862 if (!PtInRect(&rcBounds, opt)) iItem = -1;
5864 return lpht->iItem = iItem;
5868 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5869 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5870 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5871 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5872 their own sort proc. when sending LVM_SORTITEMS.
5875 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5877 LVS_SORTXXX must be specified,
5878 LVS_OWNERDRAW is not set,
5879 <item>.pszText is not LPSTR_TEXTCALLBACK.
5881 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5882 are sorted based on item text..."
5884 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5886 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
5887 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
5888 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5890 /* if we're sorting descending, negate the return value */
5891 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5896 * Inserts a new item in the listview control.
5899 * [I] infoPtr : valid pointer to the listview structure
5900 * [I] lpLVItem : item information
5901 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5904 * SUCCESS : new item index
5907 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
5909 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5914 BOOL is_sorted, has_changed;
5917 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5919 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
5921 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5922 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
5924 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
5926 if ( !(lpItem = (ITEM_INFO *)COMCTL32_Alloc(sizeof(ITEM_INFO))) )
5929 /* insert item in listview control data structure */
5930 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
5931 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
5933 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5934 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5936 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
5937 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
5938 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
5939 if (nItem == -1) goto fail;
5940 infoPtr->nItemCount++;
5942 /* set the item attributes */
5943 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5945 /* full size structure expected - _WIN32IE >= 0x560 */
5948 else if (lpLVItem->mask & LVIF_INDENT)
5950 /* indent member expected - _WIN32IE >= 0x300 */
5951 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
5955 /* minimal structure expected */
5956 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
5959 item.state &= ~LVIS_STATEIMAGEMASK;
5960 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
5962 /* if we're sorted, sort the list, and update the index */
5965 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5966 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5967 assert(nItem != -1);
5970 /* make room for the position, if we are in the right mode */
5971 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5973 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5975 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5977 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5982 /* Add the subitem list to the items array. Do this last in case we go to
5983 * fail during the above.
5985 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5987 /* send LVN_INSERTITEM notification */
5988 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5990 nmlv.lParam = lpItem->lParam;
5991 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5993 /* align items (set position of each item) */
5994 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
5998 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
5999 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6001 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6003 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6006 /* now is the invalidation fun */
6007 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6011 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6012 infoPtr->nItemCount--;
6014 DPA_DeletePtr(hdpaSubItems, 0);
6015 DPA_Destroy (hdpaSubItems);
6016 COMCTL32_Free (lpItem);
6022 * Redraws a range of items.
6025 * [I] infoPtr : valid pointer to the listview structure
6026 * [I] nFirst : first item
6027 * [I] nLast : last item
6033 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6037 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6038 max(nFirst, nLast) >= infoPtr->nItemCount)
6041 for (i = nFirst; i <= nLast; i++)
6042 LISTVIEW_InvalidateItem(infoPtr, i);
6049 * Scroll the content of a listview.
6052 * [I] infoPtr : valid pointer to the listview structure
6053 * [I] dx : horizontal scroll amount in pixels
6054 * [I] dy : vertical scroll amount in pixels
6061 * If the control is in report mode (LVS_REPORT) the control can
6062 * be scrolled only in line increments. "dy" will be rounded to the
6063 * nearest number of pixels that are a whole line. Ex: if line height
6064 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6065 * is passed the the scroll will be 0. (per MSDN 7/2002)
6067 * For: (per experimentaion with native control and CSpy ListView)
6068 * LVS_ICON dy=1 = 1 pixel (vertical only)
6070 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6072 * LVS_LIST dx=1 = 1 column (horizontal only)
6073 * but will only scroll 1 column per message
6074 * no matter what the value.
6075 * dy must be 0 or FALSE returned.
6076 * LVS_REPORT dx=1 = 1 pixel
6080 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6082 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6084 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6085 dy /= infoPtr->nItemHeight;
6088 if (dy != 0) return FALSE;
6095 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6096 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6103 * Sets the background color.
6106 * [I] infoPtr : valid pointer to the listview structure
6107 * [I] clrBk : background color
6113 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6115 TRACE("(clrBk=%lx)\n", clrBk);
6117 if(infoPtr->clrBk != clrBk) {
6118 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6119 infoPtr->clrBk = clrBk;
6120 if (clrBk == CLR_NONE)
6121 infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6123 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6124 LISTVIEW_InvalidateList(infoPtr);
6130 /* LISTVIEW_SetBkImage */
6132 /*** Helper for {Insert,Set}ColumnT *only* */
6133 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6135 if (lpColumn->mask & LVCF_FMT)
6137 /* format member is valid */
6138 lphdi->mask |= HDI_FORMAT;
6140 /* set text alignment (leftmost column must be left-aligned) */
6141 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
6142 lphdi->fmt |= HDF_LEFT;
6143 else if (lpColumn->fmt & LVCFMT_RIGHT)
6144 lphdi->fmt |= HDF_RIGHT;
6145 else if (lpColumn->fmt & LVCFMT_CENTER)
6146 lphdi->fmt |= HDF_CENTER;
6148 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6149 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6151 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6153 lphdi->fmt |= HDF_IMAGE;
6154 lphdi->iImage = I_IMAGECALLBACK;
6158 if (lpColumn->mask & LVCF_WIDTH)
6160 lphdi->mask |= HDI_WIDTH;
6161 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6163 /* make it fill the remainder of the controls width */
6167 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6169 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6170 lphdi->cxy += rcHeader.right - rcHeader.left;
6173 /* retrieve the layout of the header */
6174 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6175 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6177 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6180 lphdi->cxy = lpColumn->cx;
6183 if (lpColumn->mask & LVCF_TEXT)
6185 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6186 lphdi->fmt |= HDF_STRING;
6187 lphdi->pszText = lpColumn->pszText;
6188 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6191 if (lpColumn->mask & LVCF_IMAGE)
6193 lphdi->mask |= HDI_IMAGE;
6194 lphdi->iImage = lpColumn->iImage;
6197 if (lpColumn->mask & LVCF_ORDER)
6199 lphdi->mask |= HDI_ORDER;
6200 lphdi->iOrder = lpColumn->iOrder;
6207 * Inserts a new column.
6210 * [I] infoPtr : valid pointer to the listview structure
6211 * [I] nColumn : column index
6212 * [I] lpColumn : column information
6213 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6216 * SUCCESS : new column index
6219 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6220 const LVCOLUMNW *lpColumn, BOOL isW)
6222 COLUMN_INFO *lpColumnInfo;
6226 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6228 if (!lpColumn || nColumn < 0) return -1;
6229 nColumn = min(nColumn, infoPtr->hdpaColumns->nItemCount);
6231 ZeroMemory(&hdi, sizeof(HDITEMW));
6232 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6234 /* insert item in header control */
6235 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6236 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6237 (WPARAM)nColumn, (LPARAM)&hdi);
6238 if (nNewColumn == -1) return -1;
6239 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6241 /* create our own column info */
6242 if (!(lpColumnInfo = COMCTL32_Alloc(sizeof(COLUMN_INFO)))) goto fail;
6243 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6245 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6246 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6248 /* now we have to actually adjust the data */
6249 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6251 SUBITEM_INFO *lpSubItem;
6255 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6257 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6258 for (i = 1; i < hdpaSubItems->nItemCount; i++)
6260 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6261 if (lpSubItem->iSubItem >= nNewColumn)
6262 lpSubItem->iSubItem++;
6267 /* make space for the new column */
6268 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6273 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6276 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6277 COMCTL32_Free(lpColumnInfo);
6284 * Sets the attributes of a header item.
6287 * [I] infoPtr : valid pointer to the listview structure
6288 * [I] nColumn : column index
6289 * [I] lpColumn : column attributes
6290 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6296 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6297 const LVCOLUMNW *lpColumn, BOOL isW)
6299 HDITEMW hdi, hdiget;
6302 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6304 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6306 ZeroMemory(&hdi, sizeof(HDITEMW));
6307 if (lpColumn->mask & LVCF_FMT)
6309 hdi.mask |= HDI_FORMAT;
6310 hdiget.mask = HDI_FORMAT;
6311 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6312 hdi.fmt = hdiget.fmt & HDF_STRING;
6314 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6316 /* set header item attributes */
6317 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6318 if (!bResult) return FALSE;
6320 if (lpColumn->mask & LVCF_FMT)
6322 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6323 int oldFmt = lpColumnInfo->fmt;
6325 lpColumnInfo->fmt = lpColumn->fmt;
6326 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6328 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6329 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6338 * Sets the column order array
6341 * [I] infoPtr : valid pointer to the listview structure
6342 * [I] iCount : number of elements in column order array
6343 * [I] lpiArray : pointer to column order array
6349 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6351 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6362 * Sets the width of a column
6365 * [I] infoPtr : valid pointer to the listview structure
6366 * [I] nColumn : column index
6367 * [I] cx : column width
6373 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6375 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6376 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6380 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6382 /* set column width only if in report or list mode */
6383 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6385 /* take care of invalid cx values */
6386 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6387 else if (uView == LVS_LIST && cx < 1) return FALSE;
6389 /* resize all columns if in LVS_LIST mode */
6390 if(uView == LVS_LIST)
6392 infoPtr->nItemWidth = cx;
6393 LISTVIEW_InvalidateList(infoPtr);
6397 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6399 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < infoPtr->hdpaColumns->nItemCount -1))
6404 lvItem.mask = LVIF_TEXT;
6406 lvItem.iSubItem = nColumn;
6407 lvItem.pszText = szDispText;
6408 lvItem.cchTextMax = DISP_TEXT_SIZE;
6409 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6411 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6412 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6413 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6415 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6416 max_cx += infoPtr->iconSize.cx;
6417 max_cx += TRAILING_LABEL_PADDING;
6420 /* autosize based on listview items width */
6421 if(cx == LVSCW_AUTOSIZE)
6423 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6425 /* if iCol is the last column make it fill the remainder of the controls width */
6426 if(nColumn == infoPtr->hdpaColumns->nItemCount - 1)
6431 LISTVIEW_GetOrigin(infoPtr, &Origin);
6432 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6434 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6438 /* Despite what the MS docs say, if this is not the last
6439 column, then MS resizes the column to the width of the
6440 largest text string in the column, including headers
6441 and items. This is different from LVSCW_AUTOSIZE in that
6442 LVSCW_AUTOSIZE ignores the header string length. */
6445 /* retrieve header text */
6446 hdi.mask = HDI_TEXT;
6447 hdi.cchTextMax = DISP_TEXT_SIZE;
6448 hdi.pszText = szDispText;
6449 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6451 HDC hdc = GetDC(infoPtr->hwndSelf);
6452 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6455 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6456 cx = size.cx + TRAILING_HEADER_PADDING;
6457 /* FIXME: Take into account the header image, if one is present */
6458 SelectObject(hdc, old_font);
6459 ReleaseDC(infoPtr->hwndSelf, hdc);
6461 cx = max (cx, max_cx);
6465 if (cx < 0) return FALSE;
6467 /* call header to update the column change */
6468 hdi.mask = HDI_WIDTH;
6470 TRACE("hdi.cxy=%d\n", hdi.cxy);
6471 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6476 * Sets the extended listview style.
6479 * [I] infoPtr : valid pointer to the listview structure
6481 * [I] dwStyle : style
6484 * SUCCESS : previous style
6487 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6489 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6493 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6495 infoPtr->dwLvExStyle = dwStyle;
6502 * Sets the new hot cursor used during hot tracking and hover selection.
6505 * [I] infoPtr : valid pointer to the listview structure
6506 * [I} hCurosr : the new hot cursor handle
6509 * Returns the previous hot cursor
6511 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6513 HCURSOR oldCursor = infoPtr->hHotCursor;
6515 infoPtr->hHotCursor = hCursor;
6523 * Sets the hot item index.
6526 * [I] infoPtr : valid pointer to the listview structure
6527 * [I] iIndex : index
6530 * SUCCESS : previous hot item index
6531 * FAILURE : -1 (no hot item)
6533 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6535 INT iOldIndex = infoPtr->nHotItem;
6537 infoPtr->nHotItem = iIndex;
6545 * Sets the amount of time the cursor must hover over an item before it is selected.
6548 * [I] infoPtr : valid pointer to the listview structure
6549 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6552 * Returns the previous hover time
6554 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6556 DWORD oldHoverTime = infoPtr->dwHoverTime;
6558 infoPtr->dwHoverTime = dwHoverTime;
6560 return oldHoverTime;
6565 * Sets spacing for icons of LVS_ICON style.
6568 * [I] infoPtr : valid pointer to the listview structure
6569 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6570 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6573 * MAKELONG(oldcx, oldcy)
6575 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6577 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6578 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6580 TRACE("requested=(%d,%d)\n", cx, cy);
6582 /* this is supported only for LVS_ICON style */
6583 if (uView != LVS_ICON) return oldspacing;
6585 /* set to defaults, if instructed to */
6586 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6587 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6589 /* if 0 then compute width
6590 * FIXME: Should scan each item and determine max width of
6591 * icon or label, then make that the width */
6593 cx = infoPtr->iconSpacing.cx;
6595 /* if 0 then compute height */
6597 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6598 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6601 infoPtr->iconSpacing.cx = cx;
6602 infoPtr->iconSpacing.cy = cy;
6604 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6605 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6606 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6607 infoPtr->ntmHeight);
6609 /* these depend on the iconSpacing */
6610 LISTVIEW_UpdateItemSize(infoPtr);
6615 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6619 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6626 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6627 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6636 * [I] infoPtr : valid pointer to the listview structure
6637 * [I] nType : image list type
6638 * [I] himl : image list handle
6641 * SUCCESS : old image list
6644 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6646 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6647 INT oldHeight = infoPtr->nItemHeight;
6648 HIMAGELIST himlOld = 0;
6650 TRACE("(nType=%d, himl=%p\n", nType, himl);
6655 himlOld = infoPtr->himlNormal;
6656 infoPtr->himlNormal = himl;
6657 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6658 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6662 himlOld = infoPtr->himlSmall;
6663 infoPtr->himlSmall = himl;
6664 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6668 himlOld = infoPtr->himlState;
6669 infoPtr->himlState = himl;
6670 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6671 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6675 ERR("Unknown icon type=%d\n", nType);
6679 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6680 if (infoPtr->nItemHeight != oldHeight)
6681 LISTVIEW_UpdateScroll(infoPtr);
6688 * Preallocates memory (does *not* set the actual count of items !)
6691 * [I] infoPtr : valid pointer to the listview structure
6692 * [I] nItems : item count (projected number of items to allocate)
6693 * [I] dwFlags : update flags
6699 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6701 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6703 if (infoPtr->dwStyle & LVS_OWNERDATA)
6705 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6706 INT nOldCount = infoPtr->nItemCount;
6708 if (nItems < nOldCount)
6710 RANGE range = { nItems, nOldCount };
6711 ranges_del(infoPtr->selectionRanges, range);
6712 if (infoPtr->nFocusedItem >= nItems)
6714 infoPtr->nFocusedItem = -1;
6715 SetRectEmpty(&infoPtr->rcFocus);
6719 infoPtr->nItemCount = nItems;
6720 LISTVIEW_UpdateScroll(infoPtr);
6722 /* the flags are valid only in ownerdata report and list modes */
6723 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6725 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6726 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6728 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6729 LISTVIEW_InvalidateList(infoPtr);
6736 LISTVIEW_GetOrigin(infoPtr, &Origin);
6737 nFrom = min(nOldCount, nItems);
6738 nTo = max(nOldCount, nItems);
6740 if (uView == LVS_REPORT)
6743 rcErase.top = nFrom * infoPtr->nItemHeight;
6744 rcErase.right = infoPtr->nItemWidth;
6745 rcErase.bottom = nTo * infoPtr->nItemHeight;
6746 OffsetRect(&rcErase, Origin.x, Origin.y);
6747 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6748 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6752 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6754 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6755 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6756 rcErase.right = rcErase.left + infoPtr->nItemWidth;
6757 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6758 OffsetRect(&rcErase, Origin.x, Origin.y);
6759 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6760 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6762 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6764 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
6765 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6766 OffsetRect(&rcErase, Origin.x, Origin.y);
6767 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6768 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6774 /* According to MSDN for non-LVS_OWNERDATA this is just
6775 * a performance issue. The control allocates its internal
6776 * data structures for the number of items specified. It
6777 * cuts down on the number of memory allocations. Therefore
6778 * we will just issue a WARN here
6780 WARN("for non-ownerdata performance option not implemented.\n");
6788 * Sets the position of an item.
6791 * [I] infoPtr : valid pointer to the listview structure
6792 * [I] nItem : item index
6793 * [I] pt : coordinate
6799 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6801 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6804 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6806 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6807 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6809 LISTVIEW_GetOrigin(infoPtr, &Origin);
6811 /* This point value seems to be an undocumented feature.
6812 * The best guess is that it means either at the origin,
6813 * or at true beginning of the list. I will assume the origin. */
6814 if ((pt.x == -1) && (pt.y == -1))
6817 if (uView == LVS_ICON)
6819 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6820 pt.y -= ICON_TOP_PADDING;
6825 infoPtr->bAutoarrange = FALSE;
6827 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
6832 * Sets the state of one or many items.
6835 * [I] infoPtr : valid pointer to the listview structure
6836 * [I] nItem : item index
6837 * [I] lpLVItem : item or subitem info
6843 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
6845 BOOL bResult = TRUE;
6848 lvItem.iItem = nItem;
6849 lvItem.iSubItem = 0;
6850 lvItem.mask = LVIF_STATE;
6851 lvItem.state = lpLVItem->state;
6852 lvItem.stateMask = lpLVItem->stateMask;
6853 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6857 /* apply to all items */
6858 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6859 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6862 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6869 * Sets the text of an item or subitem.
6872 * [I] hwnd : window handle
6873 * [I] nItem : item index
6874 * [I] lpLVItem : item or subitem info
6875 * [I] isW : TRUE if input is Unicode
6881 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
6885 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6887 lvItem.iItem = nItem;
6888 lvItem.iSubItem = lpLVItem->iSubItem;
6889 lvItem.mask = LVIF_TEXT;
6890 lvItem.pszText = lpLVItem->pszText;
6891 lvItem.cchTextMax = lpLVItem->cchTextMax;
6893 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6895 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6900 * Set item index that marks the start of a multiple selection.
6903 * [I] infoPtr : valid pointer to the listview structure
6904 * [I] nIndex : index
6907 * Index number or -1 if there is no selection mark.
6909 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6911 INT nOldIndex = infoPtr->nSelectionMark;
6913 TRACE("(nIndex=%d)\n", nIndex);
6915 infoPtr->nSelectionMark = nIndex;
6922 * Sets the text background color.
6925 * [I] infoPtr : valid pointer to the listview structure
6926 * [I] clrTextBk : text background color
6932 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6934 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6936 if (infoPtr->clrTextBk != clrTextBk)
6938 infoPtr->clrTextBk = clrTextBk;
6939 LISTVIEW_InvalidateList(infoPtr);
6947 * Sets the text foreground color.
6950 * [I] infoPtr : valid pointer to the listview structure
6951 * [I] clrText : text color
6957 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6959 TRACE("(clrText=%lx)\n", clrText);
6961 if (infoPtr->clrText != clrText)
6963 infoPtr->clrText = clrText;
6964 LISTVIEW_InvalidateList(infoPtr);
6972 * Determines which listview item is located at the specified position.
6975 * [I] infoPtr : valid pointer to the listview structure
6976 * [I] hwndNewToolTip : handle to new ToolTip
6981 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
6983 HWND hwndOldToolTip = infoPtr->hwndToolTip;
6984 infoPtr->hwndToolTip = hwndNewToolTip;
6985 return hwndOldToolTip;
6988 /* LISTVIEW_SetUnicodeFormat */
6989 /* LISTVIEW_SetWorkAreas */
6993 * Callback internally used by LISTVIEW_SortItems()
6996 * [I] first : pointer to first ITEM_INFO to compare
6997 * [I] second : pointer to second ITEM_INFO to compare
6998 * [I] lParam : HWND of control
7001 * if first comes before second : negative
7002 * if first comes after second : positive
7003 * if first and second are equivalent : zero
7005 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7007 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7008 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7009 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7011 /* Forward the call to the client defined callback */
7012 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7017 * Sorts the listview items.
7020 * [I] infoPtr : valid pointer to the listview structure
7021 * [I] pfnCompare : application-defined value
7022 * [I] lParamSort : pointer to comparision callback
7028 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7030 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7033 LPVOID selectionMarkItem;
7037 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7039 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7041 if (!infoPtr->hdpaItems) return FALSE;
7043 /* if there are 0 or 1 items, there is no need to sort */
7044 if (infoPtr->nItemCount < 2) return TRUE;
7046 if (infoPtr->nFocusedItem >= 0)
7048 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7049 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7050 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7052 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7053 /* clear the lpItem->state for non-selected ones */
7054 /* remove the selection ranges */
7056 infoPtr->pfnCompare = pfnCompare;
7057 infoPtr->lParamSort = lParamSort;
7058 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
7060 /* Adjust selections and indices so that they are the way they should
7061 * be after the sort (otherwise, the list items move around, but
7062 * whatever is at the item's previous original position will be
7065 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7066 for (i=0; i < infoPtr->nItemCount; i++)
7068 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7069 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7071 if (lpItem->state & LVIS_SELECTED)
7073 item.state = LVIS_SELECTED;
7074 item.stateMask = LVIS_SELECTED;
7075 LISTVIEW_SetItemState(infoPtr, i, &item);
7077 if (lpItem->state & LVIS_FOCUSED)
7079 infoPtr->nFocusedItem = i;
7080 lpItem->state &= ~LVIS_FOCUSED;
7083 if (selectionMarkItem != NULL)
7084 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7085 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7087 /* refresh the display */
7088 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7089 LISTVIEW_InvalidateList(infoPtr);
7096 * Updates an items or rearranges the listview control.
7099 * [I] infoPtr : valid pointer to the listview structure
7100 * [I] nItem : item index
7106 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7108 TRACE("(nItem=%d)\n", nItem);
7110 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7112 /* rearrange with default alignment style */
7113 if (is_autoarrange(infoPtr))
7114 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7116 LISTVIEW_InvalidateItem(infoPtr, nItem);
7124 * Creates the listview control.
7127 * [I] hwnd : window handle
7128 * [I] lpcs : the create parameters
7134 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7136 LISTVIEW_INFO *infoPtr;
7137 UINT uView = lpcs->style & LVS_TYPEMASK;
7140 TRACE("(lpcs=%p)\n", lpcs);
7142 /* initialize info pointer */
7143 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7144 if (!infoPtr) return -1;
7146 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7148 infoPtr->hwndSelf = hwnd;
7149 infoPtr->dwStyle = lpcs->style;
7150 /* determine the type of structures to use */
7151 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7152 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7154 /* initialize color information */
7155 infoPtr->clrBk = CLR_NONE;
7156 infoPtr->clrText = comctl32_color.clrWindowText;
7157 infoPtr->clrTextBk = CLR_DEFAULT;
7158 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7160 /* set default values */
7161 infoPtr->nFocusedItem = -1;
7162 infoPtr->nSelectionMark = -1;
7163 infoPtr->nHotItem = -1;
7164 infoPtr->bRedraw = TRUE;
7165 infoPtr->bNoItemMetrics = TRUE;
7166 infoPtr->bDoChangeNotify = TRUE;
7167 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7168 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7169 infoPtr->nEditLabelItem = -1;
7170 infoPtr->dwHoverTime = -1; /* default system hover time */
7172 /* get default font (icon title) */
7173 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7174 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7175 infoPtr->hFont = infoPtr->hDefaultFont;
7176 LISTVIEW_SaveTextMetrics(infoPtr);
7179 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7180 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7181 0, 0, 0, 0, hwnd, NULL,
7182 lpcs->hInstance, NULL);
7183 if (!infoPtr->hwndHeader) goto fail;
7185 /* set header unicode format */
7186 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7188 /* set header font */
7189 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7191 /* allocate memory for the data structure */
7192 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7193 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7194 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7195 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7196 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7198 /* initialize the icon sizes */
7199 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7200 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7202 /* init item size to avoid division by 0 */
7203 LISTVIEW_UpdateItemSize (infoPtr);
7205 if (uView == LVS_REPORT)
7207 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7209 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7213 /* set HDS_HIDDEN flag to hide the header bar */
7214 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7215 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7222 DestroyWindow(infoPtr->hwndHeader);
7223 ranges_destroy(infoPtr->selectionRanges);
7224 DPA_Destroy(infoPtr->hdpaItems);
7225 DPA_Destroy(infoPtr->hdpaPosX);
7226 DPA_Destroy(infoPtr->hdpaPosY);
7227 DPA_Destroy(infoPtr->hdpaColumns);
7228 COMCTL32_Free(infoPtr);
7234 * Erases the background of the listview control.
7237 * [I] infoPtr : valid pointer to the listview structure
7238 * [I] hdc : device context handle
7244 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7248 TRACE("(hdc=%p)\n", hdc);
7250 if (!GetClipBox(hdc, &rc)) return FALSE;
7252 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7258 * Helper function for LISTVIEW_[HV]Scroll *only*.
7259 * Performs vertical/horizontal scrolling by a give amount.
7262 * [I] infoPtr : valid pointer to the listview structure
7263 * [I] dx : amount of horizontal scroll
7264 * [I] dy : amount of vertical scroll
7266 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7268 /* now we can scroll the list */
7269 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7270 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7271 /* if we have focus, adjust rect */
7272 OffsetRect(&infoPtr->rcFocus, dx, dy);
7273 UpdateWindow(infoPtr->hwndSelf);
7278 * Performs vertical scrolling.
7281 * [I] infoPtr : valid pointer to the listview structure
7282 * [I] nScrollCode : scroll code
7283 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7284 * [I] hScrollWnd : scrollbar control window handle
7290 * SB_LINEUP/SB_LINEDOWN:
7291 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7292 * for LVS_REPORT is 1 line
7293 * for LVS_LIST cannot occur
7296 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7297 INT nScrollDiff, HWND hScrollWnd)
7299 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7300 INT nOldScrollPos, nNewScrollPos;
7301 SCROLLINFO scrollInfo;
7304 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7305 debugscrollcode(nScrollCode), nScrollDiff);
7307 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7309 scrollInfo.cbSize = sizeof(SCROLLINFO);
7310 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7312 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7314 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7316 nOldScrollPos = scrollInfo.nPos;
7317 switch (nScrollCode)
7323 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7327 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7331 nScrollDiff = -scrollInfo.nPage;
7335 nScrollDiff = scrollInfo.nPage;
7338 case SB_THUMBPOSITION:
7340 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7347 /* quit right away if pos isn't changing */
7348 if (nScrollDiff == 0) return 0;
7350 /* calculate new position, and handle overflows */
7351 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7352 if (nScrollDiff > 0) {
7353 if (nNewScrollPos < nOldScrollPos ||
7354 nNewScrollPos > scrollInfo.nMax)
7355 nNewScrollPos = scrollInfo.nMax;
7357 if (nNewScrollPos > nOldScrollPos ||
7358 nNewScrollPos < scrollInfo.nMin)
7359 nNewScrollPos = scrollInfo.nMin;
7362 /* set the new position, and reread in case it changed */
7363 scrollInfo.fMask = SIF_POS;
7364 scrollInfo.nPos = nNewScrollPos;
7365 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7367 /* carry on only if it really changed */
7368 if (nNewScrollPos == nOldScrollPos) return 0;
7370 /* now adjust to client coordinates */
7371 nScrollDiff = nOldScrollPos - nNewScrollPos;
7372 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7374 /* and scroll the window */
7375 scroll_list(infoPtr, 0, nScrollDiff);
7382 * Performs horizontal scrolling.
7385 * [I] infoPtr : valid pointer to the listview structure
7386 * [I] nScrollCode : scroll code
7387 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7388 * [I] hScrollWnd : scrollbar control window handle
7394 * SB_LINELEFT/SB_LINERIGHT:
7395 * for LVS_ICON, LVS_SMALLICON 1 pixel
7396 * for LVS_REPORT is 1 pixel
7397 * for LVS_LIST is 1 column --> which is a 1 because the
7398 * scroll is based on columns not pixels
7401 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7402 INT nScrollDiff, HWND hScrollWnd)
7404 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7405 INT nOldScrollPos, nNewScrollPos;
7406 SCROLLINFO scrollInfo;
7408 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7409 debugscrollcode(nScrollCode), nScrollDiff);
7411 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7413 scrollInfo.cbSize = sizeof(SCROLLINFO);
7414 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7416 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7418 nOldScrollPos = scrollInfo.nPos;
7420 switch (nScrollCode)
7434 nScrollDiff = -scrollInfo.nPage;
7438 nScrollDiff = scrollInfo.nPage;
7441 case SB_THUMBPOSITION:
7443 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7450 /* quit right away if pos isn't changing */
7451 if (nScrollDiff == 0) return 0;
7453 /* calculate new position, and handle overflows */
7454 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7455 if (nScrollDiff > 0) {
7456 if (nNewScrollPos < nOldScrollPos ||
7457 nNewScrollPos > scrollInfo.nMax)
7458 nNewScrollPos = scrollInfo.nMax;
7460 if (nNewScrollPos > nOldScrollPos ||
7461 nNewScrollPos < scrollInfo.nMin)
7462 nNewScrollPos = scrollInfo.nMin;
7465 /* set the new position, and reread in case it changed */
7466 scrollInfo.fMask = SIF_POS;
7467 scrollInfo.nPos = nNewScrollPos;
7468 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7470 /* carry on only if it really changed */
7471 if (nNewScrollPos == nOldScrollPos) return 0;
7473 if(uView == LVS_REPORT)
7474 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7476 /* now adjust to client coordinates */
7477 nScrollDiff = nOldScrollPos - nNewScrollPos;
7478 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7480 /* and scroll the window */
7481 scroll_list(infoPtr, nScrollDiff, 0);
7486 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7488 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7489 INT gcWheelDelta = 0;
7490 UINT pulScrollLines = 3;
7491 SCROLLINFO scrollInfo;
7493 TRACE("(wheelDelta=%d)\n", wheelDelta);
7495 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7496 gcWheelDelta -= wheelDelta;
7498 scrollInfo.cbSize = sizeof(SCROLLINFO);
7499 scrollInfo.fMask = SIF_POS;
7506 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7507 * should be fixed in the future.
7509 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7510 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7514 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7516 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7517 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7518 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7523 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7534 * [I] infoPtr : valid pointer to the listview structure
7535 * [I] nVirtualKey : virtual key
7536 * [I] lKeyData : key data
7541 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7543 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7545 NMLVKEYDOWN nmKeyDown;
7547 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7549 /* send LVN_KEYDOWN notification */
7550 nmKeyDown.wVKey = nVirtualKey;
7551 nmKeyDown.flags = 0;
7552 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7554 switch (nVirtualKey)
7557 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7559 notify(infoPtr, NM_RETURN);
7560 notify(infoPtr, LVN_ITEMACTIVATE);
7565 if (infoPtr->nItemCount > 0)
7570 if (infoPtr->nItemCount > 0)
7571 nItem = infoPtr->nItemCount - 1;
7575 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7579 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7583 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7587 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7591 if (uView == LVS_REPORT)
7592 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7594 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7595 * LISTVIEW_GetCountPerRow(infoPtr);
7596 if(nItem < 0) nItem = 0;
7600 if (uView == LVS_REPORT)
7601 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7603 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7604 * LISTVIEW_GetCountPerRow(infoPtr);
7605 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7609 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7610 LISTVIEW_KeySelection(infoPtr, nItem);
7620 * [I] infoPtr : valid pointer to the listview structure
7625 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7629 /* if we did not have the focus, there's nothing to do */
7630 if (!infoPtr->bFocus) return 0;
7632 /* send NM_KILLFOCUS notification */
7633 notify(infoPtr, NM_KILLFOCUS);
7635 /* if we have a focus rectagle, get rid of it */
7636 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7638 /* set window focus flag */
7639 infoPtr->bFocus = FALSE;
7641 /* invalidate the selected items before reseting focus flag */
7642 LISTVIEW_InvalidateSelectedItems(infoPtr);
7649 * Processes double click messages (left mouse button).
7652 * [I] infoPtr : valid pointer to the listview structure
7653 * [I] wKey : key flag
7654 * [I] pts : mouse coordinate
7659 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7661 LVHITTESTINFO htInfo;
7663 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7665 /* send NM_RELEASEDCAPTURE notification */
7666 notify(infoPtr, NM_RELEASEDCAPTURE);
7668 htInfo.pt.x = pts.x;
7669 htInfo.pt.y = pts.y;
7671 /* send NM_DBLCLK notification */
7672 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7673 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7675 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7676 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7683 * Processes mouse down messages (left mouse button).
7686 * [I] infoPtr : valid pointer to the listview structure
7687 * [I] wKey : key flag
7688 * [I] pts : mouse coordinate
7693 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7695 LVHITTESTINFO lvHitTestInfo;
7696 static BOOL bGroupSelect = TRUE;
7697 POINT pt = { pts.x, pts.y };
7700 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7702 /* send NM_RELEASEDCAPTURE notification */
7703 notify(infoPtr, NM_RELEASEDCAPTURE);
7705 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7707 /* set left button down flag */
7708 infoPtr->bLButtonDown = TRUE;
7710 lvHitTestInfo.pt.x = pts.x;
7711 lvHitTestInfo.pt.y = pts.y;
7713 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7714 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7715 infoPtr->nEditLabelItem = -1;
7716 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7718 if (infoPtr->dwStyle & LVS_SINGLESEL)
7720 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7721 infoPtr->nEditLabelItem = nItem;
7723 LISTVIEW_SetSelection(infoPtr, nItem);
7727 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7731 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7732 LISTVIEW_SetItemFocus(infoPtr, nItem);
7733 infoPtr->nSelectionMark = nItem;
7739 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7740 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7742 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7743 infoPtr->nSelectionMark = nItem;
7746 else if (wKey & MK_CONTROL)
7750 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7752 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7753 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7754 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7755 infoPtr->nSelectionMark = nItem;
7757 else if (wKey & MK_SHIFT)
7759 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7763 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7764 infoPtr->nEditLabelItem = nItem;
7766 /* set selection (clears other pre-existing selections) */
7767 LISTVIEW_SetSelection(infoPtr, nItem);
7773 /* remove all selections */
7774 LISTVIEW_DeselectAll(infoPtr);
7782 * Processes mouse up messages (left mouse button).
7785 * [I] infoPtr : valid pointer to the listview structure
7786 * [I] wKey : key flag
7787 * [I] pts : mouse coordinate
7792 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7794 LVHITTESTINFO lvHitTestInfo;
7796 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7798 if (!infoPtr->bLButtonDown) return 0;
7800 lvHitTestInfo.pt.x = pts.x;
7801 lvHitTestInfo.pt.y = pts.y;
7803 /* send NM_CLICK notification */
7804 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7805 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7807 /* set left button flag */
7808 infoPtr->bLButtonDown = FALSE;
7810 /* if we clicked on a selected item, edit the label */
7811 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7812 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7819 * Destroys the listview control (called after WM_DESTROY).
7822 * [I] infoPtr : valid pointer to the listview structure
7827 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7831 /* delete all items */
7832 LISTVIEW_DeleteAllItems(infoPtr);
7834 /* destroy data structure */
7835 DPA_Destroy(infoPtr->hdpaItems);
7836 DPA_Destroy(infoPtr->hdpaPosX);
7837 DPA_Destroy(infoPtr->hdpaPosY);
7838 DPA_Destroy(infoPtr->hdpaColumns);
7839 ranges_destroy(infoPtr->selectionRanges);
7841 /* destroy image lists */
7842 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
7844 if (infoPtr->himlNormal)
7845 ImageList_Destroy(infoPtr->himlNormal);
7846 if (infoPtr->himlSmall)
7847 ImageList_Destroy(infoPtr->himlSmall);
7848 if (infoPtr->himlState)
7849 ImageList_Destroy(infoPtr->himlState);
7852 /* destroy font, bkgnd brush */
7854 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7855 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7857 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7859 /* free listview info pointer*/
7860 COMCTL32_Free(infoPtr);
7867 * Handles notifications from header.
7870 * [I] infoPtr : valid pointer to the listview structure
7871 * [I] nCtrlId : control identifier
7872 * [I] lpnmh : notification information
7877 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
7879 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7881 TRACE("(lpnmh=%p)\n", lpnmh);
7883 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= infoPtr->hdpaColumns->nItemCount) return 0;
7885 switch (lpnmh->hdr.code)
7889 case HDN_ITEMCHANGEDW:
7890 case HDN_ITEMCHANGEDA:
7892 COLUMN_INFO *lpColumnInfo;
7895 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
7899 hdi.mask = HDI_WIDTH;
7900 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
7904 cxy = lpnmh->pitem->cxy;
7906 /* determine how much we change since the last know position */
7907 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
7908 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7911 RECT rcCol = lpColumnInfo->rcHeader;
7913 lpColumnInfo->rcHeader.right += dx;
7914 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
7915 if (uView == LVS_REPORT && is_redrawing(infoPtr))
7917 /* this trick works for left aligned columns only */
7918 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
7920 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
7921 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
7923 rcCol.top = infoPtr->rcList.top;
7924 rcCol.bottom = infoPtr->rcList.bottom;
7925 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
7931 case HDN_ITEMCLICKW:
7932 case HDN_ITEMCLICKA:
7934 /* Handle sorting by Header Column */
7937 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7939 nmlv.iSubItem = lpnmh->iItem;
7940 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7950 * Determines the type of structure to use.
7953 * [I] infoPtr : valid pointer to the listview structureof the sender
7954 * [I] hwndFrom : listview window handle
7955 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
7960 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7962 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
7964 if (nCommand != NF_REQUERY) return 0;
7966 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
7973 * Paints/Repaints the listview control.
7976 * [I] infoPtr : valid pointer to the listview structure
7977 * [I] hdc : device context handle
7982 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7984 TRACE("(hdc=%p)\n", hdc);
7986 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
7988 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7990 infoPtr->bNoItemMetrics = FALSE;
7991 LISTVIEW_UpdateItemSize(infoPtr);
7992 if (uView == LVS_ICON || uView == LVS_SMALLICON)
7993 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7994 LISTVIEW_UpdateScroll(infoPtr);
7997 LISTVIEW_Refresh(infoPtr, hdc);
8002 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8004 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8005 LISTVIEW_Refresh(infoPtr, hdc);
8006 EndPaint(infoPtr->hwndSelf, &ps);
8014 * Processes double click messages (right mouse button).
8017 * [I] infoPtr : valid pointer to the listview structure
8018 * [I] wKey : key flag
8019 * [I] pts : mouse coordinate
8024 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8026 LVHITTESTINFO lvHitTestInfo;
8028 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8030 /* send NM_RELEASEDCAPTURE notification */
8031 notify(infoPtr, NM_RELEASEDCAPTURE);
8033 /* send NM_RDBLCLK notification */
8034 lvHitTestInfo.pt.x = pts.x;
8035 lvHitTestInfo.pt.y = pts.y;
8036 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8037 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8044 * Processes mouse down messages (right mouse button).
8047 * [I] infoPtr : valid pointer to the listview structure
8048 * [I] wKey : key flag
8049 * [I] pts : mouse coordinate
8054 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8056 LVHITTESTINFO lvHitTestInfo;
8059 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8061 /* send NM_RELEASEDCAPTURE notification */
8062 notify(infoPtr, NM_RELEASEDCAPTURE);
8064 /* make sure the listview control window has the focus */
8065 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8067 /* set right button down flag */
8068 infoPtr->bRButtonDown = TRUE;
8070 /* determine the index of the selected item */
8071 lvHitTestInfo.pt.x = pts.x;
8072 lvHitTestInfo.pt.y = pts.y;
8073 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8075 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8077 LISTVIEW_SetItemFocus(infoPtr, nItem);
8078 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8079 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8080 LISTVIEW_SetSelection(infoPtr, nItem);
8084 LISTVIEW_DeselectAll(infoPtr);
8092 * Processes mouse up messages (right mouse button).
8095 * [I] infoPtr : valid pointer to the listview structure
8096 * [I] wKey : key flag
8097 * [I] pts : mouse coordinate
8102 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8104 LVHITTESTINFO lvHitTestInfo;
8107 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8109 if (!infoPtr->bRButtonDown) return 0;
8111 /* set button flag */
8112 infoPtr->bRButtonDown = FALSE;
8114 /* Send NM_RClICK notification */
8115 lvHitTestInfo.pt.x = pts.x;
8116 lvHitTestInfo.pt.y = pts.y;
8117 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8118 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8120 /* Change to screen coordinate for WM_CONTEXTMENU */
8121 pt = lvHitTestInfo.pt;
8122 ClientToScreen(infoPtr->hwndSelf, &pt);
8124 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8125 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8126 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8137 * [I] infoPtr : valid pointer to the listview structure
8138 * [I] hwnd : window handle of window containing the cursor
8139 * [I] nHittest : hit-test code
8140 * [I] wMouseMsg : ideintifier of the mouse message
8143 * TRUE if cursor is set
8146 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8148 LVHITTESTINFO lvHitTestInfo;
8150 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8152 if(!infoPtr->hHotCursor) return FALSE;
8154 GetCursorPos(&lvHitTestInfo.pt);
8155 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8157 SetCursor(infoPtr->hHotCursor);
8167 * [I] infoPtr : valid pointer to the listview structure
8168 * [I] hwndLoseFocus : handle of previously focused window
8173 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8175 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8177 /* if we have the focus already, there's nothing to do */
8178 if (infoPtr->bFocus) return 0;
8180 /* send NM_SETFOCUS notification */
8181 notify(infoPtr, NM_SETFOCUS);
8183 /* set window focus flag */
8184 infoPtr->bFocus = TRUE;
8186 /* put the focus rect back on */
8187 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8189 /* redraw all visible selected items */
8190 LISTVIEW_InvalidateSelectedItems(infoPtr);
8200 * [I] infoPtr : valid pointer to the listview structure
8201 * [I] fRedraw : font handle
8202 * [I] fRedraw : redraw flag
8207 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8209 HFONT oldFont = infoPtr->hFont;
8211 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8213 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8214 if (infoPtr->hFont == oldFont) return 0;
8216 LISTVIEW_SaveTextMetrics(infoPtr);
8218 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8219 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8221 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8228 * Message handling for WM_SETREDRAW.
8229 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8232 * [I] infoPtr : valid pointer to the listview structure
8233 * [I] bRedraw: state of redraw flag
8236 * DefWinProc return value
8238 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8240 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8242 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8243 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8245 infoPtr->bRedraw = bRedraw;
8247 if(!bRedraw) return 0;
8249 if (is_autoarrange(infoPtr))
8250 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8251 LISTVIEW_UpdateScroll(infoPtr);
8253 /* despite what the WM_SETREDRAW docs says, apps expect us
8254 * to invalidate the listview here... stupid! */
8255 LISTVIEW_InvalidateList(infoPtr);
8262 * Resizes the listview control. This function processes WM_SIZE
8263 * messages. At this time, the width and height are not used.
8266 * [I] infoPtr : valid pointer to the listview structure
8267 * [I] Width : new width
8268 * [I] Height : new height
8273 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8275 RECT rcOld = infoPtr->rcList;
8277 TRACE("(width=%d, height=%d)\n", Width, Height);
8279 LISTVIEW_UpdateSize(infoPtr);
8280 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8282 /* do not bother with display related stuff if we're not redrawing */
8283 if (!is_redrawing(infoPtr)) return 0;
8285 if (is_autoarrange(infoPtr))
8286 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8288 LISTVIEW_UpdateScroll(infoPtr);
8290 /* refresh all only for lists whose height changed significantly */
8291 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8292 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8293 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8294 LISTVIEW_InvalidateList(infoPtr);
8301 * Sets the size information.
8304 * [I] infoPtr : valid pointer to the listview structure
8309 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8311 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8313 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8315 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8317 if (uView == LVS_LIST)
8319 /* Apparently the "LIST" style is supposed to have the same
8320 * number of items in a column even if there is no scroll bar.
8321 * Since if a scroll bar already exists then the bottom is already
8322 * reduced, only reduce if the scroll bar does not currently exist.
8323 * The "2" is there to mimic the native control. I think it may be
8324 * related to either padding or edges. (GLA 7/2002)
8326 if (!(infoPtr->dwStyle & WS_HSCROLL))
8327 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8328 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8330 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8335 hl.prc = &infoPtr->rcList;
8337 Header_Layout(infoPtr->hwndHeader, &hl);
8339 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8341 infoPtr->rcList.top = max(wp.cy, 0);
8344 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8349 * Processes WM_STYLECHANGED messages.
8352 * [I] infoPtr : valid pointer to the listview structure
8353 * [I] wStyleType : window style type (normal or extended)
8354 * [I] lpss : window style information
8359 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8360 const STYLESTRUCT *lpss)
8362 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8363 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8365 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8366 wStyleType, lpss->styleOld, lpss->styleNew);
8368 if (wStyleType != GWL_STYLE) return 0;
8370 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8371 /* what if LVS_OWNERDATA changed? */
8372 /* or LVS_SINGLESEL */
8373 /* or LVS_SORT{AS,DES}CENDING */
8375 infoPtr->dwStyle = lpss->styleNew;
8377 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8378 ((lpss->styleNew & WS_HSCROLL) == 0))
8379 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8381 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8382 ((lpss->styleNew & WS_VSCROLL) == 0))
8383 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8385 if (uNewView != uOldView)
8387 SIZE oldIconSize = infoPtr->iconSize;
8390 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8391 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8393 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8394 SetRectEmpty(&infoPtr->rcFocus);
8396 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8397 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8399 if (uNewView == LVS_ICON)
8401 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8403 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8404 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8405 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8408 else if (uNewView == LVS_REPORT)
8413 hl.prc = &infoPtr->rcList;
8415 Header_Layout(infoPtr->hwndHeader, &hl);
8416 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8419 LISTVIEW_UpdateItemSize(infoPtr);
8422 if (uNewView == LVS_REPORT)
8423 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8425 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8426 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8427 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8429 /* update the size of the client area */
8430 LISTVIEW_UpdateSize(infoPtr);
8432 /* add scrollbars if needed */
8433 LISTVIEW_UpdateScroll(infoPtr);
8435 /* invalidate client area + erase background */
8436 LISTVIEW_InvalidateList(infoPtr);
8443 * Window procedure of the listview control.
8446 static LRESULT WINAPI
8447 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8449 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8451 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8453 if (!infoPtr && (uMsg != WM_CREATE))
8454 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8458 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8463 case LVM_APPROXIMATEVIEWRECT:
8464 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8465 LOWORD(lParam), HIWORD(lParam));
8467 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8469 /* case LVM_CANCELEDITLABEL: */
8471 /* case LVM_CREATEDRAGIMAGE: */
8473 case LVM_DELETEALLITEMS:
8474 return LISTVIEW_DeleteAllItems(infoPtr);
8476 case LVM_DELETECOLUMN:
8477 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8479 case LVM_DELETEITEM:
8480 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8482 case LVM_EDITLABELW:
8483 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8485 case LVM_EDITLABELA:
8486 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8488 /* case LVM_ENABLEGROUPVIEW: */
8490 case LVM_ENSUREVISIBLE:
8491 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8494 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8497 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8499 case LVM_GETBKCOLOR:
8500 return infoPtr->clrBk;
8502 /* case LVM_GETBKIMAGE: */
8504 case LVM_GETCALLBACKMASK:
8505 return infoPtr->uCallbackMask;
8507 case LVM_GETCOLUMNA:
8508 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8510 case LVM_GETCOLUMNW:
8511 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8513 case LVM_GETCOLUMNORDERARRAY:
8514 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8516 case LVM_GETCOLUMNWIDTH:
8517 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8519 case LVM_GETCOUNTPERPAGE:
8520 return LISTVIEW_GetCountPerPage(infoPtr);
8522 case LVM_GETEDITCONTROL:
8523 return (LRESULT)infoPtr->hwndEdit;
8525 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8526 return infoPtr->dwLvExStyle;
8528 /* case LVM_GETGROUPINFO: */
8530 /* case LVM_GETGROUPMETRICS: */
8533 return (LRESULT)infoPtr->hwndHeader;
8535 case LVM_GETHOTCURSOR:
8536 return (LRESULT)infoPtr->hHotCursor;
8538 case LVM_GETHOTITEM:
8539 return infoPtr->nHotItem;
8541 case LVM_GETHOVERTIME:
8542 return infoPtr->dwHoverTime;
8544 case LVM_GETIMAGELIST:
8545 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8547 /* case LVM_GETINSERTMARK: */
8549 /* case LVM_GETINSERTMARKCOLOR: */
8551 /* case LVM_GETINSERTMARKRECT: */
8553 case LVM_GETISEARCHSTRINGA:
8554 case LVM_GETISEARCHSTRINGW:
8555 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8559 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8562 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8564 case LVM_GETITEMCOUNT:
8565 return infoPtr->nItemCount;
8567 case LVM_GETITEMPOSITION:
8568 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8570 case LVM_GETITEMRECT:
8571 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8573 case LVM_GETITEMSPACING:
8574 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8576 case LVM_GETITEMSTATE:
8577 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8579 case LVM_GETITEMTEXTA:
8580 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8582 case LVM_GETITEMTEXTW:
8583 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8585 case LVM_GETNEXTITEM:
8586 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8588 case LVM_GETNUMBEROFWORKAREAS:
8589 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8593 if (!lParam) return FALSE;
8594 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8597 /* case LVM_GETOUTLINECOLOR: */
8599 /* case LVM_GETSELECTEDCOLUMN: */
8601 case LVM_GETSELECTEDCOUNT:
8602 return LISTVIEW_GetSelectedCount(infoPtr);
8604 case LVM_GETSELECTIONMARK:
8605 return infoPtr->nSelectionMark;
8607 case LVM_GETSTRINGWIDTHA:
8608 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8610 case LVM_GETSTRINGWIDTHW:
8611 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8613 case LVM_GETSUBITEMRECT:
8614 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8616 case LVM_GETTEXTBKCOLOR:
8617 return infoPtr->clrTextBk;
8619 case LVM_GETTEXTCOLOR:
8620 return infoPtr->clrText;
8622 /* case LVM_GETTILEINFO: */
8624 /* case LVM_GETTILEVIEWINFO: */
8626 case LVM_GETTOOLTIPS:
8627 return (LRESULT)infoPtr->hwndToolTip;
8629 case LVM_GETTOPINDEX:
8630 return LISTVIEW_GetTopIndex(infoPtr);
8632 /*case LVM_GETUNICODEFORMAT:
8633 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8636 /* case LVM_GETVIEW: */
8638 case LVM_GETVIEWRECT:
8639 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8641 case LVM_GETWORKAREAS:
8642 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8645 /* case LVM_HASGROUP: */
8648 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8650 case LVM_INSERTCOLUMNA:
8651 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8653 case LVM_INSERTCOLUMNW:
8654 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8656 /* case LVM_INSERTGROUP: */
8658 /* case LVM_INSERTGROUPSORTED: */
8660 case LVM_INSERTITEMA:
8661 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8663 case LVM_INSERTITEMW:
8664 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8666 /* case LVM_INSERTMARKHITTEST: */
8668 /* case LVM_ISGROUPVIEWENABLED: */
8670 /* case LVM_MAPIDTOINDEX: */
8672 /* case LVM_MAPINDEXTOID: */
8674 /* case LVM_MOVEGROUP: */
8676 /* case LVM_MOVEITEMTOGROUP: */
8678 case LVM_REDRAWITEMS:
8679 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8681 /* case LVM_REMOVEALLGROUPS: */
8683 /* case LVM_REMOVEGROUP: */
8686 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8688 case LVM_SETBKCOLOR:
8689 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8691 /* case LVM_SETBKIMAGE: */
8693 case LVM_SETCALLBACKMASK:
8694 infoPtr->uCallbackMask = (UINT)wParam;
8697 case LVM_SETCOLUMNA:
8698 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8700 case LVM_SETCOLUMNW:
8701 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8703 case LVM_SETCOLUMNORDERARRAY:
8704 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8706 case LVM_SETCOLUMNWIDTH:
8707 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8709 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8710 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8712 /* case LVM_SETGROUPINFO: */
8714 /* case LVM_SETGROUPMETRICS: */
8716 case LVM_SETHOTCURSOR:
8717 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8719 case LVM_SETHOTITEM:
8720 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8722 case LVM_SETHOVERTIME:
8723 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8725 case LVM_SETICONSPACING:
8726 return LISTVIEW_SetIconSpacing(infoPtr, SLOWORD(lParam), SHIWORD(lParam));
8728 case LVM_SETIMAGELIST:
8729 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8731 /* case LVM_SETINFOTIP: */
8733 /* case LVM_SETINSERTMARK: */
8735 /* case LVM_SETINSERTMARKCOLOR: */
8738 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8741 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8743 case LVM_SETITEMCOUNT:
8744 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8746 case LVM_SETITEMPOSITION:
8748 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8749 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8752 case LVM_SETITEMPOSITION32:
8753 if (lParam == 0) return FALSE;
8754 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8756 case LVM_SETITEMSTATE:
8757 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8759 case LVM_SETITEMTEXTA:
8760 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8762 case LVM_SETITEMTEXTW:
8763 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8765 /* case LVM_SETOUTLINECOLOR: */
8767 /* case LVM_SETSELECTEDCOLUMN: */
8769 case LVM_SETSELECTIONMARK:
8770 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8772 case LVM_SETTEXTBKCOLOR:
8773 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8775 case LVM_SETTEXTCOLOR:
8776 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8778 /* case LVM_SETTILEINFO: */
8780 /* case LVM_SETTILEVIEWINFO: */
8782 /* case LVM_SETTILEWIDTH: */
8784 case LVM_SETTOOLTIPS:
8785 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
8787 /* case LVM_SETUNICODEFORMAT: */
8789 /* case LVM_SETVIEW: */
8791 /* case LVM_SETWORKAREAS: */
8793 /* case LVM_SORTGROUPS: */
8796 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8798 /* LVM_SORTITEMSEX: */
8800 case LVM_SUBITEMHITTEST:
8801 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8804 return LISTVIEW_Update(infoPtr, (INT)wParam);
8807 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8810 return LISTVIEW_Command(infoPtr, wParam, lParam);
8813 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8816 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8819 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8822 return (LRESULT)infoPtr->hFont;
8825 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8828 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8831 return LISTVIEW_KillFocus(infoPtr);
8833 case WM_LBUTTONDBLCLK:
8834 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8836 case WM_LBUTTONDOWN:
8837 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8840 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8843 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8846 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8849 return LISTVIEW_NCDestroy(infoPtr);
8852 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
8853 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
8856 case WM_NOTIFYFORMAT:
8857 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8860 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8862 case WM_RBUTTONDBLCLK:
8863 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8865 case WM_RBUTTONDOWN:
8866 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8869 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8872 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8877 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8880 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8883 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8886 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8888 case WM_STYLECHANGED:
8889 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8891 case WM_SYSCOLORCHANGE:
8892 COMCTL32_RefreshSysColors();
8895 /* case WM_TIMER: */
8898 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8901 if (wParam & (MK_SHIFT | MK_CONTROL))
8902 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8903 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8905 case WM_WINDOWPOSCHANGED:
8906 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
8908 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8909 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8910 LISTVIEW_UpdateSize(infoPtr);
8911 LISTVIEW_UpdateScroll(infoPtr);
8913 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8915 /* case WM_WININICHANGE: */
8918 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8919 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8922 /* call default window procedure */
8923 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8931 * Registers the window class.
8939 void LISTVIEW_Register(void)
8943 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8944 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8945 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8946 wndClass.cbClsExtra = 0;
8947 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8948 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8949 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8950 wndClass.lpszClassName = WC_LISTVIEWW;
8951 RegisterClassW(&wndClass);
8956 * Unregisters the window class.
8964 void LISTVIEW_Unregister(void)
8966 UnregisterClassW(WC_LISTVIEWW, NULL);
8971 * Handle any WM_COMMAND messages
8974 * [I] infoPtr : valid pointer to the listview structure
8975 * [I] wParam : the first message parameter
8976 * [I] lParam : the second message parameter
8981 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8983 switch (HIWORD(wParam))
8988 * Adjust the edit window size
8991 HDC hdc = GetDC(infoPtr->hwndEdit);
8992 HFONT hFont, hOldFont = 0;
8997 if (!infoPtr->hwndEdit || !hdc) return 0;
8998 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8999 GetWindowRect(infoPtr->hwndEdit, &rect);
9001 /* Select font to get the right dimension of the string */
9002 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9005 hOldFont = SelectObject(hdc, hFont);
9008 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9010 TEXTMETRICW textMetric;
9012 /* Add Extra spacing for the next character */
9013 GetTextMetricsW(hdc, &textMetric);
9014 sz.cx += (textMetric.tmMaxCharWidth * 2);
9022 rect.bottom - rect.top,
9023 SWP_DRAWFRAME|SWP_NOMOVE);
9026 SelectObject(hdc, hOldFont);
9028 ReleaseDC(infoPtr->hwndSelf, hdc);
9034 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
9043 * Subclassed edit control windproc function
9046 * [I] hwnd : the edit window handle
9047 * [I] uMsg : the message that is to be processed
9048 * [I] wParam : first message parameter
9049 * [I] lParam : second message parameter
9050 * [I] isW : TRUE if input is Unicode
9055 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9057 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9058 BOOL cancel = FALSE;
9060 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9061 hwnd, uMsg, wParam, lParam, isW);
9066 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9073 WNDPROC editProc = infoPtr->EditWndProc;
9074 infoPtr->EditWndProc = 0;
9075 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9076 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9080 if (VK_ESCAPE == (INT)wParam)
9085 else if (VK_RETURN == (INT)wParam)
9089 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9093 if (infoPtr->hwndEdit)
9095 LPWSTR buffer = NULL;
9097 infoPtr->hwndEdit = 0;
9100 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9104 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9106 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9107 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9111 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9113 if (buffer) COMCTL32_Free(buffer);
9117 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9123 * Subclassed edit control Unicode windproc function
9126 * [I] hwnd : the edit window handle
9127 * [I] uMsg : the message that is to be processed
9128 * [I] wParam : first message parameter
9129 * [I] lParam : second message parameter
9133 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9135 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9140 * Subclassed edit control ANSI windproc function
9143 * [I] hwnd : the edit window handle
9144 * [I] uMsg : the message that is to be processed
9145 * [I] wParam : first message parameter
9146 * [I] lParam : second message parameter
9150 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9152 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9157 * Creates a subclassed edit cotrol
9160 * [I] infoPtr : valid pointer to the listview structure
9161 * [I] text : initial text for the edit
9162 * [I] style : the window style
9163 * [I] isW : TRUE if input is Unicode
9167 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9168 INT x, INT y, INT width, INT height, BOOL isW)
9170 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9175 TEXTMETRICW textMetric;
9176 HINSTANCE hinst = (HINSTANCE)GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9178 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9180 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9181 hdc = GetDC(infoPtr->hwndSelf);
9183 /* Select the font to get appropriate metric dimensions */
9184 if(infoPtr->hFont != 0)
9185 hOldFont = SelectObject(hdc, infoPtr->hFont);
9187 /*Get String Length in pixels */
9188 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9190 /*Add Extra spacing for the next character */
9191 GetTextMetricsW(hdc, &textMetric);
9192 sz.cx += (textMetric.tmMaxCharWidth * 2);
9194 if(infoPtr->hFont != 0)
9195 SelectObject(hdc, hOldFont);
9197 ReleaseDC(infoPtr->hwndSelf, hdc);
9199 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9201 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9203 if (!hedit) return 0;
9205 infoPtr->EditWndProc = (WNDPROC)
9206 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9207 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9209 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);