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 DWORD notify_prepaint (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
887 BOOL isSelected = lpnmlvcd->nmcd.uItemState & CDIS_SELECTED;
888 DWORD cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, lpnmlvcd);
890 if (cditemmode & CDRF_SKIPDEFAULT) return cditemmode;
892 /* apprently, for selected items, we have to override the returned values */
897 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
898 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
900 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
902 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
903 lpnmlvcd->clrText = comctl32_color.clrBtnText;
907 /* Set the text attributes */
908 if (lpnmlvcd->clrTextBk != CLR_NONE)
910 SetBkMode(hdc, OPAQUE);
911 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
912 SetBkColor(hdc, infoPtr->clrTextBkDefault);
914 SetBkColor(hdc,lpnmlvcd->clrTextBk);
917 SetBkMode(hdc, TRANSPARENT);
918 SetTextColor(hdc, lpnmlvcd->clrText);
923 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
925 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
928 /******** Item iterator functions **********************************/
930 static RANGES ranges_create(int count);
931 static void ranges_destroy(RANGES ranges);
932 static BOOL ranges_add(RANGES ranges, RANGE range);
933 static BOOL ranges_del(RANGES ranges, RANGE range);
934 static void ranges_dump(RANGES ranges);
936 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
938 RANGE range = { nItem, nItem + 1 };
940 return ranges_add(ranges, range);
943 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
945 RANGE range = { nItem, nItem + 1 };
947 return ranges_del(ranges, range);
951 * ITERATOR DOCUMENTATION
953 * The iterator functions allow for easy, and convenient iteration
954 * over items of iterest in the list. Typically, you create a
955 * iterator, use it, and destroy it, as such:
958 * iterator_xxxitems(&i, ...);
959 * while (iterator_{prev,next}(&i)
961 * //code which uses i.nItem
963 * iterator_destroy(&i);
965 * where xxx is either: framed, or visible.
966 * Note that it is important that the code destroys the iterator
967 * after it's done with it, as the creation of the iterator may
968 * allocate memory, which thus needs to be freed.
970 * You can iterate both forwards, and backwards through the list,
971 * by using iterator_next or iterator_prev respectively.
973 * Lower numbered items are draw on top of higher number items in
974 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
975 * items may overlap). So, to test items, you should use
977 * which lists the items top to bottom (in Z-order).
978 * For drawing items, you should use
980 * which lists the items bottom to top (in Z-order).
981 * If you keep iterating over the items after the end-of-items
982 * marker (-1) is returned, the iterator will start from the
983 * beginning. Typically, you don't need to test for -1,
984 * because iterator_{next,prev} will return TRUE if more items
985 * are to be iterated over, or FALSE otherwise.
987 * Note: the iterator is defined to be bidirectional. That is,
988 * any number of prev followed by any number of next, or
989 * five versa, should leave the iterator at the same item:
990 * prev * n, next * n = next * n, prev * n
992 * The iterator has a notion of a out-of-order, special item,
993 * which sits at the start of the list. This is used in
994 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
995 * which needs to be first, as it may overlap other items.
997 * The code is a bit messy because we have:
998 * - a special item to deal with
999 * - simple range, or composite range
1001 * If you find bugs, or want to add features, please make sure you
1002 * always check/modify *both* iterator_prev, and iterator_next.
1006 * This function iterates through the items in increasing order,
1007 * but prefixed by the special item, then -1. That is:
1008 * special, 1, 2, 3, ..., n, -1.
1009 * Each item is listed only once.
1011 static inline BOOL iterator_next(ITERATOR* i)
1015 i->nItem = i->nSpecial;
1016 if (i->nItem != -1) return TRUE;
1018 if (i->nItem == i->nSpecial)
1020 if (i->ranges) i->index = 0;
1026 if (i->nItem == i->nSpecial) i->nItem++;
1027 if (i->nItem < i->range.upper) return TRUE;
1032 if (i->index < i->ranges->hdpa->nItemCount)
1033 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1036 else if (i->nItem >= i->range.upper) goto end;
1038 i->nItem = i->range.lower;
1039 if (i->nItem >= 0) goto testitem;
1046 * This function iterates through the items in decreasing order,
1047 * followed by the special item, then -1. That is:
1048 * n, n-1, ..., 3, 2, 1, special, -1.
1049 * Each item is listed only once.
1051 static inline BOOL iterator_prev(ITERATOR* i)
1058 if (i->ranges) i->index = i->ranges->hdpa->nItemCount;
1061 if (i->nItem == i->nSpecial)
1069 if (i->nItem == i->nSpecial) i->nItem--;
1070 if (i->nItem >= i->range.lower) return TRUE;
1076 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1079 else if (!start && i->nItem < i->range.lower) goto end;
1081 i->nItem = i->range.upper;
1082 if (i->nItem > 0) goto testitem;
1084 return (i->nItem = i->nSpecial) != -1;
1087 static RANGE iterator_range(ITERATOR* i)
1091 if (!i->ranges) return i->range;
1093 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1094 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->ranges->hdpa->nItemCount - 1)).upper;
1099 * Releases resources associated with this ierator.
1101 static inline void iterator_destroy(ITERATOR* i)
1103 ranges_destroy(i->ranges);
1107 * Create an empty iterator.
1109 static inline BOOL iterator_empty(ITERATOR* i)
1111 ZeroMemory(i, sizeof(*i));
1112 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1117 * Create an iterator over a range.
1119 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1127 * Create an iterator over a bunch of ranges.
1128 * Please note that the iterator will take ownership of the ranges,
1129 * and will free them upon destruction.
1131 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1139 * Creates an iterator over the items which intersect lprc.
1141 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1143 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1144 RECT frame = *lprc, rcItem, rcTemp;
1147 /* in case we fail, we want to return an empty iterator */
1148 if (!iterator_empty(i)) return FALSE;
1150 LISTVIEW_GetOrigin(infoPtr, &Origin);
1152 TRACE("(lprc=%s)\n", debugrect(lprc));
1153 OffsetRect(&frame, -Origin.x, -Origin.y);
1155 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1159 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1161 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1162 if (IntersectRect(&rcTemp, &rcItem, lprc))
1163 i->nSpecial = infoPtr->nFocusedItem;
1165 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1166 /* to do better here, we need to have PosX, and PosY sorted */
1167 TRACE("building icon ranges:\n");
1168 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1170 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1171 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1172 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1173 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1174 if (IntersectRect(&rcTemp, &rcItem, &frame))
1175 ranges_additem(i->ranges, nItem);
1179 else if (uView == LVS_REPORT)
1183 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1184 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1186 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1187 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1188 if (range.upper <= range.lower) return TRUE;
1189 if (!iterator_rangeitems(i, range)) return FALSE;
1190 TRACE(" report=%s\n", debugrange(&i->range));
1194 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1195 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1196 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1197 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1198 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1199 INT lower = nFirstCol * nPerCol + nFirstRow;
1203 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1204 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1206 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1208 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1209 TRACE("building list ranges:\n");
1210 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1212 item_range.lower = nCol * nPerCol + nFirstRow;
1213 if(item_range.lower >= infoPtr->nItemCount) break;
1214 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1215 TRACE(" list=%s\n", debugrange(&item_range));
1216 ranges_add(i->ranges, item_range);
1224 * Creates an iterator over the items which intersect the visible region of hdc.
1226 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1228 POINT Origin, Position;
1229 RECT rcItem, rcClip;
1232 rgntype = GetClipBox(hdc, &rcClip);
1233 if (rgntype == NULLREGION) return iterator_empty(i);
1234 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1235 if (rgntype == SIMPLEREGION) return TRUE;
1237 /* first deal with the special item */
1238 if (i->nSpecial != -1)
1240 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1241 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1244 /* if we can't deal with the region, we'll just go with the simple range */
1245 LISTVIEW_GetOrigin(infoPtr, &Origin);
1246 TRACE("building visible range:\n");
1247 if (!i->ranges && i->range.lower < i->range.upper)
1249 if (!(i->ranges = ranges_create(50))) return TRUE;
1250 if (!ranges_add(i->ranges, i->range))
1252 ranges_destroy(i->ranges);
1258 /* now delete the invisible items from the list */
1259 while(iterator_next(i))
1261 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1262 rcItem.left = Position.x + Origin.x;
1263 rcItem.top = Position.y + Origin.y;
1264 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1265 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1266 if (!RectVisible(hdc, &rcItem))
1267 ranges_delitem(i->ranges, i->nItem);
1269 /* the iterator should restart on the next iterator_next */
1275 /******** Misc helper functions ************************************/
1277 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1278 WPARAM wParam, LPARAM lParam, BOOL isW)
1280 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1281 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1284 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1286 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1288 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1289 (uView == LVS_ICON || uView == LVS_SMALLICON);
1292 /******** Internal API functions ************************************/
1294 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1296 static COLUMN_INFO mainItem;
1298 if (nSubItem == 0 && infoPtr->hdpaColumns->nItemCount == 0) return &mainItem;
1299 assert (nSubItem >= 0 && nSubItem < infoPtr->hdpaColumns->nItemCount);
1300 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1303 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1305 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1308 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1310 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1313 /* Listview invalidation functions: use _only_ these functions to invalidate */
1315 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1317 return infoPtr->bRedraw;
1320 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1322 if(!is_redrawing(infoPtr)) return;
1323 TRACE(" invalidating rect=%s\n", debugrect(rect));
1324 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1327 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1331 if(!is_redrawing(infoPtr)) return;
1332 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1333 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1336 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1338 POINT Origin, Position;
1341 if(!is_redrawing(infoPtr)) return;
1342 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1343 LISTVIEW_GetOrigin(infoPtr, &Origin);
1344 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1345 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1347 rcBox.bottom = infoPtr->nItemHeight;
1348 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1349 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1352 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1354 LISTVIEW_InvalidateRect(infoPtr, NULL);
1357 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1361 if(!is_redrawing(infoPtr)) return;
1362 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1363 rcCol.top = infoPtr->rcList.top;
1364 rcCol.bottom = infoPtr->rcList.bottom;
1365 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1370 * Retrieves the number of items that can fit vertically in the client area.
1373 * [I] infoPtr : valid pointer to the listview structure
1376 * Number of items per row.
1378 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1380 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1382 return max(nListWidth/infoPtr->nItemWidth, 1);
1387 * Retrieves the number of items that can fit horizontally in the client
1391 * [I] infoPtr : valid pointer to the listview structure
1394 * Number of items per column.
1396 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1398 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1400 return max(nListHeight / infoPtr->nItemHeight, 1);
1404 /*************************************************************************
1405 * LISTVIEW_ProcessLetterKeys
1407 * Processes keyboard messages generated by pressing the letter keys
1409 * What this does is perform a case insensitive search from the
1410 * current position with the following quirks:
1411 * - If two chars or more are pressed in quick succession we search
1412 * for the corresponding string (e.g. 'abc').
1413 * - If there is a delay we wipe away the current search string and
1414 * restart with just that char.
1415 * - If the user keeps pressing the same character, whether slowly or
1416 * fast, so that the search string is entirely composed of this
1417 * character ('aaaaa' for instance), then we search for first item
1418 * that starting with that character.
1419 * - If the user types the above character in quick succession, then
1420 * we must also search for the corresponding string ('aaaaa'), and
1421 * go to that string if there is a match.
1424 * [I] hwnd : handle to the window
1425 * [I] charCode : the character code, the actual character
1426 * [I] keyData : key data
1434 * - The current implementation has a list of characters it will
1435 * accept and it ignores averything else. In particular it will
1436 * ignore accentuated characters which seems to match what
1437 * Windows does. But I'm not sure it makes sense to follow
1439 * - We don't sound a beep when the search fails.
1443 * TREEVIEW_ProcessLetterKeys
1445 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1450 WCHAR buffer[MAX_PATH];
1451 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1453 /* simple parameter checking */
1454 if (!charCode || !keyData) return 0;
1456 /* only allow the valid WM_CHARs through */
1457 if (!isalnum(charCode) &&
1458 charCode != '.' && charCode != '`' && charCode != '!' &&
1459 charCode != '@' && charCode != '#' && charCode != '$' &&
1460 charCode != '%' && charCode != '^' && charCode != '&' &&
1461 charCode != '*' && charCode != '(' && charCode != ')' &&
1462 charCode != '-' && charCode != '_' && charCode != '+' &&
1463 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1464 charCode != '}' && charCode != '[' && charCode != '{' &&
1465 charCode != '/' && charCode != '?' && charCode != '>' &&
1466 charCode != '<' && charCode != ',' && charCode != '~')
1469 /* if there's one item or less, there is no where to go */
1470 if (infoPtr->nItemCount <= 1) return 0;
1472 /* update the search parameters */
1473 infoPtr->lastKeyPressTimestamp = GetTickCount();
1474 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1475 if (infoPtr->nSearchParamLength < MAX_PATH)
1476 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1477 if (infoPtr->charCode != charCode)
1478 infoPtr->charCode = charCode = 0;
1480 infoPtr->charCode=charCode;
1481 infoPtr->szSearchParam[0]=charCode;
1482 infoPtr->nSearchParamLength=1;
1483 /* Redundant with the 1 char string */
1487 /* and search from the current position */
1489 if (infoPtr->nFocusedItem >= 0) {
1490 endidx=infoPtr->nFocusedItem;
1492 /* if looking for single character match,
1493 * then we must always move forward
1495 if (infoPtr->nSearchParamLength == 1)
1498 endidx=infoPtr->nItemCount;
1502 if (idx == infoPtr->nItemCount) {
1503 if (endidx == infoPtr->nItemCount || endidx == 0)
1509 item.mask = LVIF_TEXT;
1512 item.pszText = buffer;
1513 item.cchTextMax = MAX_PATH;
1514 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1516 /* check for a match */
1517 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1520 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1521 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1522 /* This would work but we must keep looking for a longer match */
1526 } while (idx != endidx);
1529 LISTVIEW_KeySelection(infoPtr, nItem);
1534 /*************************************************************************
1535 * LISTVIEW_UpdateHeaderSize [Internal]
1537 * Function to resize the header control
1540 * [I] hwnd : handle to a window
1541 * [I] nNewScrollPos : scroll pos to set
1546 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1551 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1553 GetWindowRect(infoPtr->hwndHeader, &winRect);
1554 point[0].x = winRect.left;
1555 point[0].y = winRect.top;
1556 point[1].x = winRect.right;
1557 point[1].y = winRect.bottom;
1559 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1560 point[0].x = -nNewScrollPos;
1561 point[1].x += nNewScrollPos;
1563 SetWindowPos(infoPtr->hwndHeader,0,
1564 point[0].x,point[0].y,point[1].x,point[1].y,
1565 SWP_NOZORDER | SWP_NOACTIVATE);
1570 * Update the scrollbars. This functions should be called whenever
1571 * the content, size or view changes.
1574 * [I] infoPtr : valid pointer to the listview structure
1579 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1581 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1582 SCROLLINFO horzInfo, vertInfo;
1584 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1586 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1587 horzInfo.cbSize = sizeof(SCROLLINFO);
1588 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1590 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1591 if (uView == LVS_LIST)
1593 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1594 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1596 /* scroll by at least one column per page */
1597 if(horzInfo.nPage < infoPtr->nItemWidth)
1598 horzInfo.nPage = infoPtr->nItemWidth;
1600 horzInfo.nPage /= infoPtr->nItemWidth;
1602 else if (uView == LVS_REPORT)
1604 horzInfo.nMax = infoPtr->nItemWidth;
1606 else /* LVS_ICON, or LVS_SMALLICON */
1610 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1613 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1614 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1615 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1616 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1618 /* Setting the horizontal scroll can change the listview size
1619 * (and potentially everything else) so we need to recompute
1620 * everything again for the vertical scroll
1623 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1624 vertInfo.cbSize = sizeof(SCROLLINFO);
1625 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1627 if (uView == LVS_REPORT)
1629 vertInfo.nMax = infoPtr->nItemCount;
1631 /* scroll by at least one page */
1632 if(vertInfo.nPage < infoPtr->nItemHeight)
1633 vertInfo.nPage = infoPtr->nItemHeight;
1635 vertInfo.nPage /= infoPtr->nItemHeight;
1637 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1641 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1644 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1645 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1646 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1647 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1649 /* Update the Header Control */
1650 if (uView == LVS_REPORT)
1652 horzInfo.fMask = SIF_POS;
1653 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1654 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1661 * Shows/hides the focus rectangle.
1664 * [I] infoPtr : valid pointer to the listview structure
1665 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1670 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1672 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1675 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1677 if (infoPtr->nFocusedItem < 0) return;
1679 /* we need some gymnastics in ICON mode to handle large items */
1680 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1684 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1685 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1687 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1692 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1694 /* for some reason, owner draw should work only in report mode */
1695 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1700 item.iItem = infoPtr->nFocusedItem;
1702 item.mask = LVIF_PARAM;
1703 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1705 ZeroMemory(&dis, sizeof(dis));
1706 dis.CtlType = ODT_LISTVIEW;
1707 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1708 dis.itemID = item.iItem;
1709 dis.itemAction = ODA_FOCUS;
1710 if (fShow) dis.itemState |= ODS_FOCUS;
1711 dis.hwndItem = infoPtr->hwndSelf;
1713 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1714 dis.itemData = item.lParam;
1716 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1720 DrawFocusRect(hdc, &infoPtr->rcFocus);
1723 ReleaseDC(infoPtr->hwndSelf, hdc);
1727 * Invalidates all visible selected items.
1729 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1733 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1734 while(iterator_next(&i))
1736 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1737 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1739 iterator_destroy(&i);
1744 * DESCRIPTION: [INTERNAL]
1745 * Computes an item's (left,top) corner, relative to rcView.
1746 * That is, the position has NOT been made relative to the Origin.
1747 * This is deliberate, to avoid computing the Origin over, and
1748 * over again, when this function is call in a loop. Instead,
1749 * one ca factor the computation of the Origin before the loop,
1750 * and offset the value retured by this function, on every iteration.
1753 * [I] infoPtr : valid pointer to the listview structure
1754 * [I] nItem : item number
1755 * [O] lpptOrig : item top, left corner
1760 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1762 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1764 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1766 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1768 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1769 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1771 else if (uView == LVS_LIST)
1773 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1774 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1775 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1777 else /* LVS_REPORT */
1779 lpptPosition->x = 0;
1780 lpptPosition->y = nItem * infoPtr->nItemHeight;
1785 * DESCRIPTION: [INTERNAL]
1786 * Compute the rectangles of an item. This is to localize all
1787 * the computations in one place. If you are not interested in some
1788 * of these values, simply pass in a NULL -- the fucntion is smart
1789 * enough to compute only what's necessary. The function computes
1790 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1791 * one, the BOX rectangle. This rectangle is very cheap to compute,
1792 * and is guaranteed to contain all the other rectangles. Computing
1793 * the ICON rect is also cheap, but all the others are potentaily
1794 * expensive. This gives an easy and effective optimization when
1795 * searching (like point inclusion, or rectangle intersection):
1796 * first test against the BOX, and if TRUE, test agains the desired
1798 * If the function does not have all the necessary information
1799 * to computed the requested rectangles, will crash with a
1800 * failed assertion. This is done so we catch all programming
1801 * errors, given that the function is called only from our code.
1803 * We have the following 'special' meanings for a few fields:
1804 * * If LVIS_FOCUSED is set, we assume the item has the focus
1805 * This is important in ICON mode, where it might get a larger
1806 * then usual rectange
1808 * Please note that subitem support works only in REPORT mode.
1811 * [I] infoPtr : valid pointer to the listview structure
1812 * [I] lpLVItem : item to compute the measures for
1813 * [O] lprcBox : ptr to Box rectangle
1814 * The internal LVIR_BOX rectangle
1815 * [0] lprcState : ptr to State icon rectangle
1816 * The internal LVIR_STATE rectangle
1817 * [O] lprcIcon : ptr to Icon rectangle
1818 * Same as LVM_GETITEMRECT with LVIR_ICON
1819 * [O] lprcLabel : ptr to Label rectangle
1820 * Same as LVM_GETITEMRECT with LVIR_LABEL
1825 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1826 LPRECT lprcBox, LPRECT lprcState,
1827 LPRECT lprcIcon, LPRECT lprcLabel)
1829 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1830 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1831 RECT Box, State, Icon, Label;
1832 COLUMN_INFO *lpColumnInfo = NULL;
1834 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1836 /* Be smart and try to figure out the minimum we have to do */
1837 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1838 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1840 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1841 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1843 if (lprcLabel) doLabel = TRUE;
1844 if (doLabel || lprcIcon) doIcon = TRUE;
1845 if (doIcon || lprcState) doState = TRUE;
1847 /************************************************************/
1848 /* compute the box rectangle (it should be cheap to do) */
1849 /************************************************************/
1850 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1851 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1853 if (lpLVItem->iSubItem)
1855 Box = lpColumnInfo->rcHeader;
1860 Box.right = infoPtr->nItemWidth;
1863 Box.bottom = infoPtr->nItemHeight;
1865 /************************************************************/
1866 /* compute STATEICON bounding box */
1867 /************************************************************/
1870 if (uView == LVS_ICON)
1872 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1873 if (infoPtr->himlNormal)
1874 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1875 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1879 /* we need the ident in report mode, if we don't have it, we fail */
1880 State.left = Box.left;
1881 if (uView == LVS_REPORT)
1883 if (lpLVItem->iSubItem == 0)
1885 State.left += REPORT_MARGINX;
1886 assert(lpLVItem->mask & LVIF_INDENT);
1887 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1890 State.top = Box.top;
1892 State.right = State.left;
1893 State.bottom = State.top;
1894 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1896 State.right += infoPtr->iconStateSize.cx;
1897 State.bottom += infoPtr->iconStateSize.cy;
1899 if (lprcState) *lprcState = State;
1900 TRACE(" - state=%s\n", debugrect(&State));
1903 /************************************************************/
1904 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1905 /************************************************************/
1908 if (uView == LVS_ICON)
1910 Icon.left = Box.left;
1911 if (infoPtr->himlNormal)
1912 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1913 Icon.top = Box.top + ICON_TOP_PADDING;
1914 Icon.right = Icon.left;
1915 Icon.bottom = Icon.top;
1916 if (infoPtr->himlNormal)
1918 Icon.right += infoPtr->iconSize.cx;
1919 Icon.bottom += infoPtr->iconSize.cy;
1922 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1924 Icon.left = State.right;
1926 Icon.right = Icon.left;
1927 if (infoPtr->himlSmall && (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE)))
1928 Icon.right += infoPtr->iconSize.cx;
1929 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1931 if(lprcIcon) *lprcIcon = Icon;
1932 TRACE(" - icon=%s\n", debugrect(&Icon));
1935 /************************************************************/
1936 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1937 /************************************************************/
1940 SIZE labelSize = { 0, 0 };
1942 /* calculate how far to the right can the label strech */
1943 Label.right = Box.right;
1944 if (uView == LVS_REPORT)
1946 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1949 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1951 labelSize.cx = infoPtr->nItemWidth;
1952 labelSize.cy = infoPtr->nItemHeight;
1956 /* we need the text in non owner draw mode */
1957 assert(lpLVItem->mask & LVIF_TEXT);
1958 if (is_textT(lpLVItem->pszText, TRUE))
1960 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1961 HDC hdc = GetDC(infoPtr->hwndSelf);
1962 HFONT hOldFont = SelectObject(hdc, hFont);
1966 /* compute rough rectangle where the label will go */
1967 SetRectEmpty(&rcText);
1968 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
1969 rcText.bottom = infoPtr->nItemHeight;
1970 if (uView == LVS_ICON)
1971 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1973 /* now figure out the flags */
1974 if (uView == LVS_ICON)
1975 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1977 uFormat = LV_SL_DT_FLAGS;
1979 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1981 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
1982 labelSize.cy = rcText.bottom - rcText.top;
1984 SelectObject(hdc, hOldFont);
1985 ReleaseDC(infoPtr->hwndSelf, hdc);
1989 if (uView == LVS_ICON)
1991 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1992 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1993 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1994 Label.right = Label.left + labelSize.cx;
1995 Label.bottom = Label.top + infoPtr->nItemHeight;
1996 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1998 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1999 labelSize.cy /= infoPtr->ntmHeight;
2000 labelSize.cy = max(labelSize.cy, 1);
2001 labelSize.cy *= infoPtr->ntmHeight;
2003 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2005 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2007 Label.left = Icon.right;
2008 Label.top = Box.top;
2009 Label.right = min(Label.left + labelSize.cx, Label.right);
2010 Label.bottom = Label.top + infoPtr->nItemHeight;
2013 if (lprcLabel) *lprcLabel = Label;
2014 TRACE(" - label=%s\n", debugrect(&Label));
2017 /* Fix the Box if necessary */
2020 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2021 else *lprcBox = Box;
2023 TRACE(" - box=%s\n", debugrect(&Box));
2027 * DESCRIPTION: [INTERNAL]
2030 * [I] infoPtr : valid pointer to the listview structure
2031 * [I] nItem : item number
2032 * [O] lprcBox : ptr to Box rectangle
2037 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2039 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2040 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2041 POINT Position, Origin;
2044 LISTVIEW_GetOrigin(infoPtr, &Origin);
2045 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2047 /* Be smart and try to figure out the minimum we have to do */
2049 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2050 lvItem.mask |= LVIF_TEXT;
2051 lvItem.iItem = nItem;
2052 lvItem.iSubItem = 0;
2053 lvItem.pszText = szDispText;
2054 lvItem.cchTextMax = DISP_TEXT_SIZE;
2055 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2056 if (uView == LVS_ICON)
2058 lvItem.mask |= LVIF_STATE;
2059 lvItem.stateMask = LVIS_FOCUSED;
2060 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2062 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2064 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2070 * Returns the current icon position, and advances it along the top.
2071 * The returned position is not offset by Origin.
2074 * [I] infoPtr : valid pointer to the listview structure
2075 * [O] lpPos : will get the current icon position
2080 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2082 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2084 *lpPos = infoPtr->currIconPos;
2086 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2087 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2089 infoPtr->currIconPos.x = 0;
2090 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2096 * Returns the current icon position, and advances it down the left edge.
2097 * The returned position is not offset by Origin.
2100 * [I] infoPtr : valid pointer to the listview structure
2101 * [O] lpPos : will get the current icon position
2106 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2108 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2110 *lpPos = infoPtr->currIconPos;
2112 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2113 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2115 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2116 infoPtr->currIconPos.y = 0;
2122 * Moves an icon to the specified position.
2123 * It takes care of invalidating the item, etc.
2126 * [I] infoPtr : valid pointer to the listview structure
2127 * [I] nItem : the item to move
2128 * [I] lpPos : the new icon position
2129 * [I] isNew : flags the item as being new
2135 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2141 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2142 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2144 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2145 LISTVIEW_InvalidateItem(infoPtr, nItem);
2148 /* Allocating a POINTER for every item is too resource intensive,
2149 * so we'll keep the (x,y) in different arrays */
2150 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2151 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2153 LISTVIEW_InvalidateItem(infoPtr, nItem);
2160 * Arranges listview items in icon display mode.
2163 * [I] infoPtr : valid pointer to the listview structure
2164 * [I] nAlignCode : alignment code
2170 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2172 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2173 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2177 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2179 TRACE("nAlignCode=%d\n", nAlignCode);
2181 if (nAlignCode == LVA_DEFAULT)
2183 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2184 else nAlignCode = LVA_ALIGNTOP;
2189 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2190 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2191 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2192 default: return FALSE;
2195 infoPtr->bAutoarrange = TRUE;
2196 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2197 for (i = 0; i < infoPtr->nItemCount; i++)
2199 next_pos(infoPtr, &pos);
2200 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2208 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2211 * [I] infoPtr : valid pointer to the listview structure
2212 * [O] lprcView : bounding rectangle
2218 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2222 SetRectEmpty(lprcView);
2224 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2228 for (i = 0; i < infoPtr->nItemCount; i++)
2230 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2231 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2232 lprcView->right = max(lprcView->right, x);
2233 lprcView->bottom = max(lprcView->bottom, y);
2235 if (infoPtr->nItemCount > 0)
2237 lprcView->right += infoPtr->nItemWidth;
2238 lprcView->bottom += infoPtr->nItemHeight;
2243 y = LISTVIEW_GetCountPerColumn(infoPtr);
2244 x = infoPtr->nItemCount / y;
2245 if (infoPtr->nItemCount % y) x++;
2246 lprcView->right = x * infoPtr->nItemWidth;
2247 lprcView->bottom = y * infoPtr->nItemHeight;
2251 lprcView->right = infoPtr->nItemWidth;
2252 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2259 * Retrieves the bounding rectangle of all the items.
2262 * [I] infoPtr : valid pointer to the listview structure
2263 * [O] lprcView : bounding rectangle
2269 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2273 TRACE("(lprcView=%p)\n", lprcView);
2275 if (!lprcView) return FALSE;
2277 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2278 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2279 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2281 TRACE("lprcView=%s\n", debugrect(lprcView));
2288 * Retrieves the subitem pointer associated with the subitem index.
2291 * [I] hdpaSubItems : DPA handle for a specific item
2292 * [I] nSubItem : index of subitem
2295 * SUCCESS : subitem pointer
2298 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2300 SUBITEM_INFO *lpSubItem;
2303 /* we should binary search here if need be */
2304 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2306 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2307 if (lpSubItem->iSubItem == nSubItem)
2317 * Caclulates the desired item width.
2320 * [I] infoPtr : valid pointer to the listview structure
2323 * The desired item width.
2325 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2327 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2330 TRACE("uView=%d\n", uView);
2332 if (uView == LVS_ICON)
2333 nItemWidth = infoPtr->iconSpacing.cx;
2334 else if (uView == LVS_REPORT)
2338 if (infoPtr->hdpaColumns->nItemCount > 0)
2340 LISTVIEW_GetHeaderRect(infoPtr, infoPtr->hdpaColumns->nItemCount - 1, &rcHeader);
2341 nItemWidth = rcHeader.right;
2344 else /* LVS_SMALLICON, or LVS_LIST */
2348 for (i = 0; i < infoPtr->nItemCount; i++)
2349 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2351 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2352 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2354 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2357 return max(nItemWidth, 1);
2362 * Caclulates the desired item height.
2365 * [I] infoPtr : valid pointer to the listview structure
2368 * The desired item height.
2370 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2372 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2375 TRACE("uView=%d\n", uView);
2377 if (uView == LVS_ICON)
2378 nItemHeight = infoPtr->iconSpacing.cy;
2381 nItemHeight = infoPtr->ntmHeight;
2382 if (infoPtr->himlState)
2383 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2384 if (infoPtr->himlSmall)
2385 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2386 if (infoPtr->himlState || infoPtr->himlSmall)
2387 nItemHeight += HEIGHT_PADDING;
2390 return max(nItemHeight, 1);
2395 * Updates the width, and height of an item.
2398 * [I] infoPtr : valid pointer to the listview structure
2403 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2405 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2406 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2412 * Retrieves and saves important text metrics info for the current
2416 * [I] infoPtr : valid pointer to the listview structure
2419 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2421 HDC hdc = GetDC(infoPtr->hwndSelf);
2422 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2423 HFONT hOldFont = SelectObject(hdc, hFont);
2426 if (GetTextMetricsW(hdc, &tm))
2428 infoPtr->ntmHeight = tm.tmHeight;
2429 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2431 SelectObject(hdc, hOldFont);
2432 ReleaseDC(infoPtr->hwndSelf, hdc);
2434 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2439 * A compare function for ranges
2442 * [I] range1 : pointer to range 1;
2443 * [I] range2 : pointer to range 2;
2447 * > 0 : if range 1 > range 2
2448 * < 0 : if range 2 > range 1
2449 * = 0 : if range intersects range 2
2451 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2455 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2457 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2462 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2468 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2470 #define ranges_check(ranges, desc) do { } while(0)
2473 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2478 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2480 assert (ranges->hdpa->nItemCount >= 0);
2481 ranges_dump(ranges);
2482 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2483 if (ranges->hdpa->nItemCount > 0)
2484 assert (prev->lower >= 0 && prev->lower < prev->upper);
2485 for (i = 1; i < ranges->hdpa->nItemCount; i++)
2487 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2488 assert (prev->upper <= curr->lower);
2489 assert (curr->lower < curr->upper);
2492 TRACE("--- Done checking---\n");
2495 static RANGES ranges_create(int count)
2497 RANGES ranges = (RANGES)COMCTL32_Alloc(sizeof(struct tagRANGES));
2498 if (!ranges) return NULL;
2499 ranges->hdpa = DPA_Create(count);
2500 if (ranges->hdpa) return ranges;
2501 COMCTL32_Free(ranges);
2505 static void ranges_clear(RANGES ranges)
2509 for(i = 0; i < ranges->hdpa->nItemCount; i++)
2510 COMCTL32_Free(DPA_GetPtr(ranges->hdpa, i));
2511 DPA_DeleteAllPtrs(ranges->hdpa);
2515 static void ranges_destroy(RANGES ranges)
2517 if (!ranges) return;
2518 ranges_clear(ranges);
2519 DPA_Destroy(ranges->hdpa);
2520 COMCTL32_Free(ranges);
2523 static RANGES ranges_clone(RANGES ranges)
2528 if (!(clone = ranges_create(ranges->hdpa->nItemCount))) goto fail;
2530 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2532 RANGE *newrng = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2533 if (!newrng) goto fail;
2534 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2535 DPA_SetPtr(clone->hdpa, i, newrng);
2540 TRACE ("clone failed\n");
2541 ranges_destroy(clone);
2545 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2549 for (i = 0; i < sub->hdpa->nItemCount; i++)
2550 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2555 static void ranges_dump(RANGES ranges)
2559 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2560 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2563 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2565 RANGE srchrng = { nItem, nItem + 1 };
2567 TRACE("(nItem=%d)\n", nItem);
2568 ranges_check(ranges, "before contain");
2569 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2572 static INT ranges_itemcount(RANGES ranges)
2576 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2578 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2579 count += sel->upper - sel->lower;
2585 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2587 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2590 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2591 if (index == -1) return TRUE;
2593 for (; index < ranges->hdpa->nItemCount; index++)
2595 chkrng = DPA_GetPtr(ranges->hdpa, index);
2596 if (chkrng->lower >= nItem)
2597 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2598 if (chkrng->upper > nItem)
2599 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2604 static BOOL ranges_add(RANGES ranges, RANGE range)
2609 TRACE("(%s)\n", debugrange(&range));
2610 ranges_check(ranges, "before add");
2612 /* try find overlapping regions first */
2613 srchrgn.lower = range.lower - 1;
2614 srchrgn.upper = range.upper + 1;
2615 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2621 TRACE("Adding new range\n");
2623 /* create the brand new range to insert */
2624 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2625 if(!newrgn) goto fail;
2628 /* figure out where to insert it */
2629 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2630 TRACE("index=%d\n", index);
2631 if (index == -1) index = 0;
2633 /* and get it over with */
2634 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2636 COMCTL32_Free(newrgn);
2642 RANGE *chkrgn, *mrgrgn;
2643 INT fromindex, mergeindex;
2645 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2646 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2648 chkrgn->lower = min(range.lower, chkrgn->lower);
2649 chkrgn->upper = max(range.upper, chkrgn->upper);
2651 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2653 /* merge now common anges */
2655 srchrgn.lower = chkrgn->lower - 1;
2656 srchrgn.upper = chkrgn->upper + 1;
2660 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2661 if (mergeindex == -1) break;
2662 if (mergeindex == index)
2664 fromindex = index + 1;
2668 TRACE("Merge with index %i\n", mergeindex);
2670 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2671 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2672 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2673 COMCTL32_Free(mrgrgn);
2674 DPA_DeletePtr(ranges->hdpa, mergeindex);
2675 if (mergeindex < index) index --;
2679 ranges_check(ranges, "after add");
2683 ranges_check(ranges, "failed add");
2687 static BOOL ranges_del(RANGES ranges, RANGE range)
2692 TRACE("(%s)\n", debugrange(&range));
2693 ranges_check(ranges, "before del");
2695 /* we don't use DPAS_SORTED here, since we need *
2696 * to find the first overlapping range */
2697 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2700 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2702 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2704 /* case 1: Same range */
2705 if ( (chkrgn->upper == range.upper) &&
2706 (chkrgn->lower == range.lower) )
2708 DPA_DeletePtr(ranges->hdpa, index);
2711 /* case 2: engulf */
2712 else if ( (chkrgn->upper <= range.upper) &&
2713 (chkrgn->lower >= range.lower) )
2715 DPA_DeletePtr(ranges->hdpa, index);
2717 /* case 3: overlap upper */
2718 else if ( (chkrgn->upper <= range.upper) &&
2719 (chkrgn->lower < range.lower) )
2721 chkrgn->upper = range.lower;
2723 /* case 4: overlap lower */
2724 else if ( (chkrgn->upper > range.upper) &&
2725 (chkrgn->lower >= range.lower) )
2727 chkrgn->lower = range.upper;
2730 /* case 5: fully internal */
2733 RANGE tmprgn = *chkrgn, *newrgn;
2735 if (!(newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE)))) goto fail;
2736 newrgn->lower = chkrgn->lower;
2737 newrgn->upper = range.lower;
2738 chkrgn->lower = range.upper;
2739 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2741 COMCTL32_Free(newrgn);
2748 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2751 ranges_check(ranges, "after del");
2755 ranges_check(ranges, "failed del");
2761 * Removes all selection ranges
2764 * [I] infoPtr : valid pointer to the listview structure
2765 * [I] toSkip : item range to skip removing the selection
2771 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2780 lvItem.stateMask = LVIS_SELECTED;
2782 /* need to clone the DPA because callbacks can change it */
2783 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2784 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2785 while(iterator_next(&i))
2786 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2787 /* note that the iterator destructor will free the cloned range */
2788 iterator_destroy(&i);
2793 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2797 if (!(toSkip = ranges_create(1))) return FALSE;
2798 if (nItem != -1) ranges_additem(toSkip, nItem);
2799 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2800 ranges_destroy(toSkip);
2804 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2806 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2811 * Retrieves the number of items that are marked as selected.
2814 * [I] infoPtr : valid pointer to the listview structure
2817 * Number of items selected.
2819 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2821 INT nSelectedCount = 0;
2823 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2826 for (i = 0; i < infoPtr->nItemCount; i++)
2828 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2833 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2835 TRACE("nSelectedCount=%d\n", nSelectedCount);
2836 return nSelectedCount;
2841 * Manages the item focus.
2844 * [I] infoPtr : valid pointer to the listview structure
2845 * [I] nItem : item index
2848 * TRUE : focused item changed
2849 * FALSE : focused item has NOT changed
2851 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2853 INT oldFocus = infoPtr->nFocusedItem;
2856 if (nItem == infoPtr->nFocusedItem) return FALSE;
2858 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2859 lvItem.stateMask = LVIS_FOCUSED;
2860 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2862 return oldFocus != infoPtr->nFocusedItem;
2865 /* Helper function for LISTVIEW_ShiftIndices *only* */
2866 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2868 if (nShiftItem < nItem) return nShiftItem;
2870 if (nShiftItem > nItem) return nShiftItem + direction;
2872 if (direction > 0) return nShiftItem + direction;
2874 return min(nShiftItem, infoPtr->nItemCount - 1);
2879 * Updates the various indices after an item has been inserted or deleted.
2882 * [I] infoPtr : valid pointer to the listview structure
2883 * [I] nItem : item index
2884 * [I] direction : Direction of shift, +1 or -1.
2889 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2894 /* temporarily disable change notification while shifting items */
2895 bOldChange = infoPtr->bDoChangeNotify;
2896 infoPtr->bDoChangeNotify = FALSE;
2898 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2900 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2902 assert(abs(direction) == 1);
2904 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2906 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2907 if (nNewFocus != infoPtr->nFocusedItem)
2908 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2910 /* But we are not supposed to modify nHotItem! */
2912 infoPtr->bDoChangeNotify = bOldChange;
2918 * Adds a block of selections.
2921 * [I] infoPtr : valid pointer to the listview structure
2922 * [I] nItem : item index
2927 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2929 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2930 INT nLast = max(infoPtr->nSelectionMark, nItem);
2934 if (nFirst == -1) nFirst = nItem;
2936 item.state = LVIS_SELECTED;
2937 item.stateMask = LVIS_SELECTED;
2939 /* FIXME: this is not correct LVS_OWNERDATA
2940 * setting the item states individually will generate
2941 * a LVN_ITEMCHANGED notification for each one. Instead,
2942 * we have to send a LVN_ODSTATECHANGED notification.
2943 * See MSDN documentation for LVN_ITEMCHANGED.
2945 for (i = nFirst; i <= nLast; i++)
2946 LISTVIEW_SetItemState(infoPtr,i,&item);
2952 * Sets a single group selection.
2955 * [I] infoPtr : valid pointer to the listview structure
2956 * [I] nItem : item index
2961 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2963 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2968 if (!(selection = ranges_create(100))) return;
2970 item.state = LVIS_SELECTED;
2971 item.stateMask = LVIS_SELECTED;
2973 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2975 if (infoPtr->nSelectionMark == -1)
2977 infoPtr->nSelectionMark = nItem;
2978 ranges_additem(selection, nItem);
2984 sel.lower = min(infoPtr->nSelectionMark, nItem);
2985 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
2986 ranges_add(selection, sel);
2991 RECT rcItem, rcSel, rcSelMark;
2994 rcItem.left = LVIR_BOUNDS;
2995 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2996 rcSelMark.left = LVIR_BOUNDS;
2997 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2998 UnionRect(&rcSel, &rcItem, &rcSelMark);
2999 iterator_frameditems(&i, infoPtr, &rcSel);
3000 while(iterator_next(&i))
3002 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3003 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3005 iterator_destroy(&i);
3008 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3009 iterator_rangesitems(&i, selection);
3010 while(iterator_next(&i))
3011 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3012 /* this will also destroy the selection */
3013 iterator_destroy(&i);
3015 LISTVIEW_SetItemFocus(infoPtr, nItem);
3020 * Sets a single selection.
3023 * [I] infoPtr : valid pointer to the listview structure
3024 * [I] nItem : item index
3029 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3033 TRACE("nItem=%d\n", nItem);
3035 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3037 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3038 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3039 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3041 infoPtr->nSelectionMark = nItem;
3046 * Set selection(s) with keyboard.
3049 * [I] infoPtr : valid pointer to the listview structure
3050 * [I] nItem : item index
3053 * SUCCESS : TRUE (needs to be repainted)
3054 * FAILURE : FALSE (nothing has changed)
3056 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3058 /* FIXME: pass in the state */
3059 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3060 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3061 BOOL bResult = FALSE;
3063 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3065 if (infoPtr->dwStyle & LVS_SINGLESEL)
3068 LISTVIEW_SetSelection(infoPtr, nItem);
3075 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3079 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3084 LISTVIEW_SetSelection(infoPtr, nItem);
3087 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3090 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3097 * Called when the mouse is being actively tracked and has hovered for a specified
3101 * [I] infoPtr : valid pointer to the listview structure
3102 * [I] fwKeys : key indicator
3103 * [I] pts : mouse position
3106 * 0 if the message was processed, non-zero if there was an error
3109 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3110 * over the item for a certain period of time.
3113 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
3115 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3116 /* FIXME: select the item!!! */
3117 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3124 * Called whenever WM_MOUSEMOVE is received.
3127 * [I] infoPtr : valid pointer to the listview structure
3128 * [I] fwKeys : key indicator
3129 * [I] pts : mouse position
3132 * 0 if the message is processed, non-zero if there was an error
3134 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
3136 TRACKMOUSEEVENT trackinfo;
3138 /* see if we are supposed to be tracking mouse hovering */
3139 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3140 /* fill in the trackinfo struct */
3141 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3142 trackinfo.dwFlags = TME_QUERY;
3143 trackinfo.hwndTrack = infoPtr->hwndSelf;
3144 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3146 /* see if we are already tracking this hwnd */
3147 _TrackMouseEvent(&trackinfo);
3149 if(!(trackinfo.dwFlags & TME_HOVER)) {
3150 trackinfo.dwFlags = TME_HOVER;
3152 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3153 _TrackMouseEvent(&trackinfo);
3162 * Tests wheather the item is assignable to a list with style lStyle
3164 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3166 if ( (lpLVItem->mask & LVIF_TEXT) &&
3167 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3168 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3176 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3179 * [I] infoPtr : valid pointer to the listview structure
3180 * [I] lpLVItem : valid pointer to new item atttributes
3181 * [I] isNew : the item being set is being inserted
3182 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3183 * [O] bChanged : will be set to TRUE if the item really changed
3189 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3191 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3199 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3201 if (lpLVItem->mask == 0) return TRUE;
3203 if (infoPtr->dwStyle & LVS_OWNERDATA)
3205 /* a virtual listview we stores only selection and focus */
3206 if (lpLVItem->mask & ~LVIF_STATE)
3212 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3213 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3217 /* we need to get the lParam and state of the item */
3218 item.iItem = lpLVItem->iItem;
3219 item.iSubItem = lpLVItem->iSubItem;
3220 item.mask = LVIF_STATE | LVIF_PARAM;
3221 item.stateMask = ~0;
3224 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3226 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3227 /* determine what fields will change */
3228 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3229 uChanged |= LVIF_STATE;
3231 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3232 uChanged |= LVIF_IMAGE;
3234 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3235 uChanged |= LVIF_PARAM;
3237 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3238 uChanged |= LVIF_INDENT;
3240 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3241 uChanged |= LVIF_TEXT;
3243 TRACE("uChanged=0x%x\n", uChanged);
3244 if (!uChanged) return TRUE;
3247 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3248 nmlv.iItem = lpLVItem->iItem;
3249 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3250 nmlv.uOldState = item.state;
3251 nmlv.uChanged = uChanged;
3252 nmlv.lParam = item.lParam;
3254 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3255 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3257 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3258 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3261 /* copy information */
3262 if (lpLVItem->mask & LVIF_TEXT)
3263 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3265 if (lpLVItem->mask & LVIF_IMAGE)
3266 lpItem->hdr.iImage = lpLVItem->iImage;
3268 if (lpLVItem->mask & LVIF_PARAM)
3269 lpItem->lParam = lpLVItem->lParam;
3271 if (lpLVItem->mask & LVIF_INDENT)
3272 lpItem->iIndent = lpLVItem->iIndent;
3274 if (uChanged & LVIF_STATE)
3276 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3278 lpItem->state &= ~lpLVItem->stateMask;
3279 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3281 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3283 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3284 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3286 else if (lpLVItem->stateMask & LVIS_SELECTED)
3287 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3289 /* if we are asked to change focus, and we manage it, do it */
3290 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3292 if (lpLVItem->state & LVIS_FOCUSED)
3294 LISTVIEW_SetItemFocus(infoPtr, -1);
3295 infoPtr->nFocusedItem = lpLVItem->iItem;
3296 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3298 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3299 infoPtr->nFocusedItem = -1;
3303 /* if we're inserting the item, we're done */
3304 if (isNew) return TRUE;
3306 /* send LVN_ITEMCHANGED notification */
3307 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3308 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3315 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3318 * [I] infoPtr : valid pointer to the listview structure
3319 * [I] lpLVItem : valid pointer to new subitem atttributes
3320 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3321 * [O] bChanged : will be set to TRUE if the item really changed
3327 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3330 SUBITEM_INFO *lpSubItem;
3332 /* we do not support subitems for virtual listviews */
3333 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3335 /* set subitem only if column is present */
3336 if (lpLVItem->iSubItem >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3338 /* First do some sanity checks */
3339 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3340 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3342 /* get the subitem structure, and create it if not there */
3343 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3344 assert (hdpaSubItems);
3346 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3349 SUBITEM_INFO *tmpSubItem;
3352 lpSubItem = (SUBITEM_INFO *)COMCTL32_Alloc(sizeof(SUBITEM_INFO));
3353 if (!lpSubItem) return FALSE;
3354 /* we could binary search here, if need be...*/
3355 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3357 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3358 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3360 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3362 COMCTL32_Free(lpSubItem);
3365 lpSubItem->iSubItem = lpLVItem->iSubItem;
3369 if (lpLVItem->mask & LVIF_IMAGE)
3370 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3372 lpSubItem->hdr.iImage = lpLVItem->iImage;
3376 if (lpLVItem->mask & LVIF_TEXT)
3377 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3379 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3388 * Sets item attributes.
3391 * [I] infoPtr : valid pointer to the listview structure
3392 * [I] lpLVItem : new item atttributes
3393 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3399 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3401 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3402 LPWSTR pszText = NULL;
3403 BOOL bResult, bChanged = FALSE;
3405 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3407 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3410 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3411 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3413 pszText = lpLVItem->pszText;
3414 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3417 /* actually set the fields */
3418 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3420 if (lpLVItem->iSubItem)
3421 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3423 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3425 /* redraw item, if necessary */
3426 if (bChanged && !infoPtr->bIsDrawing)
3428 /* this little optimization eliminates some nasty flicker */
3429 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3430 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3431 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3433 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3438 textfreeT(lpLVItem->pszText, isW);
3439 ((LVITEMW *)lpLVItem)->pszText = pszText;
3447 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3450 * [I] infoPtr : valid pointer to the listview structure
3455 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3457 LONG lStyle = infoPtr->dwStyle;
3458 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3460 SCROLLINFO scrollInfo;
3462 scrollInfo.cbSize = sizeof(SCROLLINFO);
3463 scrollInfo.fMask = SIF_POS;
3465 if (uView == LVS_LIST)
3467 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3468 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3470 else if (uView == LVS_REPORT)
3472 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3473 nItem = scrollInfo.nPos;
3477 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3478 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3481 TRACE("nItem=%d\n", nItem);
3489 * Erases the background of the given rectangle
3492 * [I] infoPtr : valid pointer to the listview structure
3493 * [I] hdc : device context handle
3494 * [I] lprcBox : clipping rectangle
3500 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3502 if (!infoPtr->hBkBrush) return FALSE;
3504 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3506 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3514 * [I] infoPtr : valid pointer to the listview structure
3515 * [I] hdc : device context handle
3516 * [I] nItem : item index
3517 * [I] nSubItem : subitem index
3518 * [I] pos : item position in client coordinates
3519 * [I] cdmode : custom draw mode
3525 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3527 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3528 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3529 WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3530 DWORD cdsubitemmode = CDRF_DODEFAULT;
3531 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3532 NMLVCUSTOMDRAW nmlvcd;
3536 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3538 /* get information needed for drawing the item */
3539 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3540 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3541 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3542 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3543 lvItem.iItem = nItem;
3544 lvItem.iSubItem = nSubItem;
3547 lvItem.cchTextMax = DISP_TEXT_SIZE;
3548 lvItem.pszText = szDispText;
3549 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3550 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3551 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3552 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3553 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3555 /* now check if we need to update the focus rectangle */
3556 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3558 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3559 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3560 OffsetRect(&rcBox, pos.x, pos.y);
3561 OffsetRect(&rcState, pos.x, pos.y);
3562 OffsetRect(&rcIcon, pos.x, pos.y);
3563 OffsetRect(&rcLabel, pos.x, pos.y);
3564 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3565 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3567 /* fill in the custom draw structure */
3568 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3570 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3571 if (cdmode & CDRF_NOTIFYITEMDRAW)
3572 cdsubitemmode = notify_prepaint ( infoPtr, hdc, &nmlvcd);
3573 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3574 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3576 /* in full row select, subitems, will just use main item's colors */
3577 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3578 nmlvcd.clrTextBk = CLR_NONE;
3581 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3583 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3586 TRACE("uStateImage=%d\n", uStateImage);
3587 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3592 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3593 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3595 TRACE("iImage=%d\n", lvItem.iImage);
3596 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3597 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3600 /* Don't bother painting item being edited */
3601 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3603 /* draw the selection background, if we're drawing the main item */
3607 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3608 rcSelect.right = rcBox.right;
3610 if (nmlvcd.clrTextBk != CLR_NONE)
3611 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3612 if(lprcFocus) *lprcFocus = rcSelect;
3615 /* figure out the text drawing flags */
3616 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3617 if (uView == LVS_ICON)
3618 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3621 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3623 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3624 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3625 default: uFormat |= DT_LEFT;
3628 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3630 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3631 else rcLabel.left += LABEL_HOR_PADDING;
3633 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3634 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3637 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3638 notify_postpaint(infoPtr, &nmlvcd);
3644 * Draws listview items when in owner draw mode.
3647 * [I] infoPtr : valid pointer to the listview structure
3648 * [I] hdc : device context handle
3653 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3655 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3656 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3657 DWORD cditemmode = CDRF_DODEFAULT;
3658 NMLVCUSTOMDRAW nmlvcd;
3659 POINT Origin, Position;
3665 ZeroMemory(&dis, sizeof(dis));
3667 /* Get scroll info once before loop */
3668 LISTVIEW_GetOrigin(infoPtr, &Origin);
3670 /* iterate through the invalidated rows */
3671 while(iterator_next(i))
3673 item.iItem = i->nItem;
3675 item.mask = LVIF_PARAM | LVIF_STATE;
3676 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3677 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3679 dis.CtlType = ODT_LISTVIEW;
3681 dis.itemID = item.iItem;
3682 dis.itemAction = ODA_DRAWENTIRE;
3684 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3685 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3686 dis.hwndItem = infoPtr->hwndSelf;
3688 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3689 dis.rcItem.left = Position.x + Origin.x;
3690 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3691 dis.rcItem.top = Position.y + Origin.y;
3692 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3693 dis.itemData = item.lParam;
3695 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3697 if (cdmode & CDRF_NOTIFYITEMDRAW)
3699 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3700 cditemmode = notify_prepaint (infoPtr, hdc, &nmlvcd);
3703 if (!(cditemmode & CDRF_SKIPDEFAULT))
3704 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3706 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3707 notify_postpaint(infoPtr, &nmlvcd);
3713 * Draws listview items when in report display mode.
3716 * [I] infoPtr : valid pointer to the listview structure
3717 * [I] hdc : device context handle
3718 * [I] cdmode : custom draw mode
3723 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3726 RECT rcClip, rcItem;
3727 POINT Origin, Position;
3733 /* figure out what to draw */
3734 rgntype = GetClipBox(hdc, &rcClip);
3735 if (rgntype == NULLREGION) return;
3737 /* Get scroll info once before loop */
3738 LISTVIEW_GetOrigin(infoPtr, &Origin);
3740 /* narrow down the columns we need to paint */
3741 for(colRange.lower = 0; colRange.lower < infoPtr->hdpaColumns->nItemCount; colRange.lower++)
3743 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3744 if (rcItem.right + Origin.x >= rcClip.left) break;
3746 for(colRange.upper = infoPtr->hdpaColumns->nItemCount; colRange.upper > 0; colRange.upper--)
3748 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3749 if (rcItem.left + Origin.x < rcClip.right) break;
3751 iterator_rangeitems(&j, colRange);
3753 /* in full row select, we _have_ to draw the main item */
3754 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3757 /* iterate through the invalidated rows */
3758 while(iterator_next(i))
3760 /* iterate through the invalidated columns */
3761 while(iterator_next(&j))
3763 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3764 Position.x += Origin.x;
3765 Position.y += Origin.y;
3767 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3769 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3771 rcItem.bottom = infoPtr->nItemHeight;
3772 OffsetRect(&rcItem, Position.x, Position.y);
3773 if (!RectVisible(hdc, &rcItem)) continue;
3776 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3779 iterator_destroy(&j);
3784 * Draws listview items when in list display mode.
3787 * [I] infoPtr : valid pointer to the listview structure
3788 * [I] hdc : device context handle
3789 * [I] cdmode : custom draw mode
3794 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3796 POINT Origin, Position;
3798 /* Get scroll info once before loop */
3799 LISTVIEW_GetOrigin(infoPtr, &Origin);
3801 while(iterator_prev(i))
3803 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3804 Position.x += Origin.x;
3805 Position.y += Origin.y;
3807 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3814 * Draws listview items.
3817 * [I] infoPtr : valid pointer to the listview structure
3818 * [I] hdc : device context handle
3823 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3825 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3826 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3827 NMLVCUSTOMDRAW nmlvcd;
3834 LISTVIEW_DUMP(infoPtr);
3836 infoPtr->bIsDrawing = TRUE;
3838 /* save dc values we're gonna trash while drawing */
3839 hOldFont = SelectObject(hdc, infoPtr->hFont);
3840 oldBkMode = GetBkMode(hdc);
3841 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3842 oldTextColor = GetTextColor(hdc);
3844 oldClrTextBk = infoPtr->clrTextBk;
3845 oldClrText = infoPtr->clrText;
3847 infoPtr->cditemmode = CDRF_DODEFAULT;
3849 GetClientRect(infoPtr->hwndSelf, &rcClient);
3850 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3851 cdmode = notify_prepaint(infoPtr, hdc, &nmlvcd);
3852 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3854 /* Use these colors to draw the items */
3855 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3856 infoPtr->clrText = nmlvcd.clrText;
3858 /* nothing to draw */
3859 if(infoPtr->nItemCount == 0) goto enddraw;
3861 /* figure out what we need to draw */
3862 iterator_visibleitems(&i, infoPtr, hdc);
3864 /* send cache hint notification */
3865 if (infoPtr->dwStyle & LVS_OWNERDATA)
3867 RANGE range = iterator_range(&i);
3870 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3871 nmlv.iFrom = range.lower;
3872 nmlv.iTo = range.upper - 1;
3873 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3876 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3877 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3880 if (uView == LVS_REPORT)
3881 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3882 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3883 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3885 /* if we have a focus rect, draw it */
3886 if (infoPtr->bFocus)
3887 DrawFocusRect(hdc, &infoPtr->rcFocus);
3889 iterator_destroy(&i);
3892 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3893 notify_postpaint(infoPtr, &nmlvcd);
3895 infoPtr->clrTextBk = oldClrTextBk;
3896 infoPtr->clrText = oldClrText;
3898 SelectObject(hdc, hOldFont);
3899 SetBkMode(hdc, oldBkMode);
3900 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3901 SetTextColor(hdc, oldTextColor);
3902 infoPtr->bIsDrawing = FALSE;
3908 * Calculates the approximate width and height of a given number of items.
3911 * [I] infoPtr : valid pointer to the listview structure
3912 * [I] nItemCount : number of items
3913 * [I] wWidth : width
3914 * [I] wHeight : height
3917 * Returns a DWORD. The width in the low word and the height in high word.
3919 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3920 WORD wWidth, WORD wHeight)
3922 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3923 INT nItemCountPerColumn = 1;
3924 INT nColumnCount = 0;
3925 DWORD dwViewRect = 0;
3927 if (nItemCount == -1)
3928 nItemCount = infoPtr->nItemCount;
3930 if (uView == LVS_LIST)
3932 if (wHeight == 0xFFFF)
3934 /* use current height */
3935 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3938 if (wHeight < infoPtr->nItemHeight)
3939 wHeight = infoPtr->nItemHeight;
3943 if (infoPtr->nItemHeight > 0)
3945 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3946 if (nItemCountPerColumn == 0)
3947 nItemCountPerColumn = 1;
3949 if (nItemCount % nItemCountPerColumn != 0)
3950 nColumnCount = nItemCount / nItemCountPerColumn;
3952 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3956 /* Microsoft padding magic */
3957 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3958 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3960 dwViewRect = MAKELONG(wWidth, wHeight);
3962 else if (uView == LVS_REPORT)
3963 FIXME("uView == LVS_REPORT: not implemented\n");
3964 else if (uView == LVS_SMALLICON)
3965 FIXME("uView == LVS_SMALLICON: not implemented\n");
3966 else if (uView == LVS_ICON)
3967 FIXME("uView == LVS_ICON: not implemented\n");
3972 /* << LISTVIEW_CreateDragImage >> */
3977 * Removes all listview items and subitems.
3980 * [I] infoPtr : valid pointer to the listview structure
3986 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3989 HDPA hdpaSubItems = NULL;
3996 /* we do it directly, to avoid notifications */
3997 ranges_clear(infoPtr->selectionRanges);
3998 infoPtr->nSelectionMark = -1;
3999 infoPtr->nFocusedItem = -1;
4000 SetRectEmpty(&infoPtr->rcFocus);
4001 /* But we are supposed to leave nHotItem as is! */
4004 /* send LVN_DELETEALLITEMS notification */
4005 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4007 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4009 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4011 /* send LVN_DELETEITEM notification, if not supressed */
4012 if (!bSuppress) notify_deleteitem(infoPtr, i);
4013 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4015 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4016 for (j = 0; j < hdpaSubItems->nItemCount; j++)
4018 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4019 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
4020 COMCTL32_Free(hdrItem);
4022 DPA_Destroy(hdpaSubItems);
4023 DPA_DeletePtr(infoPtr->hdpaItems, i);
4025 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4026 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4027 infoPtr->nItemCount --;
4030 LISTVIEW_UpdateScroll(infoPtr);
4032 LISTVIEW_InvalidateList(infoPtr);
4039 * Scrolls, and updates the columns, when a column is changing width.
4042 * [I] infoPtr : valid pointer to the listview structure
4043 * [I] nColumn : column to scroll
4044 * [I] dx : amount of scroll, in pixels
4049 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4051 COLUMN_INFO *lpColumnInfo;
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;
5771 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5775 if (subitem) lpht->iSubItem = 0;
5777 if (infoPtr->rcList.left > lpht->pt.x)
5778 lpht->flags |= LVHT_TOLEFT;
5779 else if (infoPtr->rcList.right < lpht->pt.x)
5780 lpht->flags |= LVHT_TORIGHT;
5782 if (infoPtr->rcList.top > lpht->pt.y)
5783 lpht->flags |= LVHT_ABOVE;
5784 else if (infoPtr->rcList.bottom < lpht->pt.y)
5785 lpht->flags |= LVHT_BELOW;
5787 TRACE("lpht->flags=0x%x\n", lpht->flags);
5788 if (lpht->flags) return -1;
5790 lpht->flags |= LVHT_NOWHERE;
5792 LISTVIEW_GetOrigin(infoPtr, &Origin);
5794 /* first deal with the large items */
5795 rcSearch.left = lpht->pt.x;
5796 rcSearch.top = lpht->pt.y;
5797 rcSearch.right = rcSearch.left + 1;
5798 rcSearch.bottom = rcSearch.top + 1;
5800 iterator_frameditems(&i, infoPtr, &rcSearch);
5801 iterator_next(&i); /* go to first item in the sequence */
5802 lpht->iItem = i.nItem;
5803 iterator_destroy(&i);
5805 TRACE("lpht->iItem=%d\n", lpht->iItem);
5806 if (lpht->iItem == -1) return -1;
5808 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5809 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5810 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5811 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5812 lvItem.iItem = lpht->iItem;
5813 lvItem.iSubItem = 0;
5814 lvItem.pszText = szDispText;
5815 lvItem.cchTextMax = DISP_TEXT_SIZE;
5816 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5817 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5819 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5820 LISTVIEW_GetItemOrigin(infoPtr, lpht->iItem, &Position);
5821 opt.x = lpht->pt.x - Position.x - Origin.x;
5822 opt.y = lpht->pt.y - Position.y - Origin.y;
5824 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
5827 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5828 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5829 if (!PtInRect(&rcBounds, opt)) return -1;
5831 if (PtInRect(&rcIcon, opt))
5832 lpht->flags |= LVHT_ONITEMICON;
5833 else if (PtInRect(&rcLabel, opt))
5834 lpht->flags |= LVHT_ONITEMLABEL;
5835 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5836 lpht->flags |= LVHT_ONITEMSTATEICON;
5837 if (lpht->flags & LVHT_ONITEM)
5838 lpht->flags &= ~LVHT_NOWHERE;
5840 TRACE("lpht->flags=0x%x\n", lpht->flags);
5841 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5845 rcBounds.right = rcBounds.left;
5846 for (j = 0; j < infoPtr->hdpaColumns->nItemCount; j++)
5848 rcBounds.left = rcBounds.right;
5849 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5850 if (PtInRect(&rcBounds, opt))
5858 if (!select || lpht->iItem == -1) return lpht->iItem;
5860 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5862 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5863 return PtInRect(&rcBounds, opt) ? lpht->iItem : -1;
5867 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5868 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5869 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5870 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5871 their own sort proc. when sending LVM_SORTITEMS.
5874 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5876 LVS_SORTXXX must be specified,
5877 LVS_OWNERDRAW is not set,
5878 <item>.pszText is not LPSTR_TEXTCALLBACK.
5880 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5881 are sorted based on item text..."
5883 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5885 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
5886 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
5887 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5889 /* if we're sorting descending, negate the return value */
5890 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5895 * Inserts a new item in the listview control.
5898 * [I] infoPtr : valid pointer to the listview structure
5899 * [I] lpLVItem : item information
5900 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5903 * SUCCESS : new item index
5906 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
5908 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5913 BOOL is_sorted, has_changed;
5916 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5918 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
5920 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5921 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
5923 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
5925 if ( !(lpItem = (ITEM_INFO *)COMCTL32_Alloc(sizeof(ITEM_INFO))) )
5928 /* insert item in listview control data structure */
5929 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
5930 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
5932 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5933 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5935 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
5936 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
5937 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
5938 if (nItem == -1) goto fail;
5939 infoPtr->nItemCount++;
5941 /* set the item attributes */
5942 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5944 /* full size structure expected - _WIN32IE >= 0x560 */
5947 else if (lpLVItem->mask & LVIF_INDENT)
5949 /* indent member expected - _WIN32IE >= 0x300 */
5950 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
5954 /* minimal structure expected */
5955 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
5958 item.state &= ~LVIS_STATEIMAGEMASK;
5959 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
5961 /* if we're sorted, sort the list, and update the index */
5964 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5965 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5966 assert(nItem != -1);
5969 /* make room for the position, if we are in the right mode */
5970 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5972 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5974 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5976 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5981 /* Add the subitem list to the items array. Do this last in case we go to
5982 * fail during the above.
5984 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5986 /* send LVN_INSERTITEM notification */
5987 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5989 nmlv.lParam = lpItem->lParam;
5990 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5992 /* align items (set position of each item) */
5993 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
5997 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
5998 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6000 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6002 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6005 /* now is the invalidation fun */
6006 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6010 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6011 infoPtr->nItemCount--;
6013 DPA_DeletePtr(hdpaSubItems, 0);
6014 DPA_Destroy (hdpaSubItems);
6015 COMCTL32_Free (lpItem);
6021 * Redraws a range of items.
6024 * [I] infoPtr : valid pointer to the listview structure
6025 * [I] nFirst : first item
6026 * [I] nLast : last item
6032 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6036 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6037 max(nFirst, nLast) >= infoPtr->nItemCount)
6040 for (i = nFirst; i <= nLast; i++)
6041 LISTVIEW_InvalidateItem(infoPtr, i);
6048 * Scroll the content of a listview.
6051 * [I] infoPtr : valid pointer to the listview structure
6052 * [I] dx : horizontal scroll amount in pixels
6053 * [I] dy : vertical scroll amount in pixels
6060 * If the control is in report mode (LVS_REPORT) the control can
6061 * be scrolled only in line increments. "dy" will be rounded to the
6062 * nearest number of pixels that are a whole line. Ex: if line height
6063 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6064 * is passed the the scroll will be 0. (per MSDN 7/2002)
6066 * For: (per experimentaion with native control and CSpy ListView)
6067 * LVS_ICON dy=1 = 1 pixel (vertical only)
6069 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6071 * LVS_LIST dx=1 = 1 column (horizontal only)
6072 * but will only scroll 1 column per message
6073 * no matter what the value.
6074 * dy must be 0 or FALSE returned.
6075 * LVS_REPORT dx=1 = 1 pixel
6079 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6081 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6083 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6084 dy /= infoPtr->nItemHeight;
6087 if (dy != 0) return FALSE;
6094 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6095 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6102 * Sets the background color.
6105 * [I] infoPtr : valid pointer to the listview structure
6106 * [I] clrBk : background color
6112 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6114 TRACE("(clrBk=%lx)\n", clrBk);
6116 if(infoPtr->clrBk != clrBk) {
6117 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6118 infoPtr->clrBk = clrBk;
6119 if (clrBk == CLR_NONE)
6120 infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6122 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6123 LISTVIEW_InvalidateList(infoPtr);
6129 /* LISTVIEW_SetBkImage */
6131 /*** Helper for {Insert,Set}ColumnT *only* */
6132 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6134 if (lpColumn->mask & LVCF_FMT)
6136 /* format member is valid */
6137 lphdi->mask |= HDI_FORMAT;
6139 /* set text alignment (leftmost column must be left-aligned) */
6140 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
6141 lphdi->fmt |= HDF_LEFT;
6142 else if (lpColumn->fmt & LVCFMT_RIGHT)
6143 lphdi->fmt |= HDF_RIGHT;
6144 else if (lpColumn->fmt & LVCFMT_CENTER)
6145 lphdi->fmt |= HDF_CENTER;
6147 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6148 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6150 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6152 lphdi->fmt |= HDF_IMAGE;
6153 lphdi->iImage = I_IMAGECALLBACK;
6157 if (lpColumn->mask & LVCF_WIDTH)
6159 lphdi->mask |= HDI_WIDTH;
6160 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6162 /* make it fill the remainder of the controls width */
6166 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6168 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6169 lphdi->cxy += rcHeader.right - rcHeader.left;
6172 /* retrieve the layout of the header */
6173 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6174 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6176 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6179 lphdi->cxy = lpColumn->cx;
6182 if (lpColumn->mask & LVCF_TEXT)
6184 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6185 lphdi->fmt |= HDF_STRING;
6186 lphdi->pszText = lpColumn->pszText;
6187 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6190 if (lpColumn->mask & LVCF_IMAGE)
6192 lphdi->mask |= HDI_IMAGE;
6193 lphdi->iImage = lpColumn->iImage;
6196 if (lpColumn->mask & LVCF_ORDER)
6198 lphdi->mask |= HDI_ORDER;
6199 lphdi->iOrder = lpColumn->iOrder;
6206 * Inserts a new column.
6209 * [I] infoPtr : valid pointer to the listview structure
6210 * [I] nColumn : column index
6211 * [I] lpColumn : column information
6212 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6215 * SUCCESS : new column index
6218 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6219 const LVCOLUMNW *lpColumn, BOOL isW)
6221 COLUMN_INFO *lpColumnInfo;
6225 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6227 if (!lpColumn || nColumn < 0) return -1;
6228 nColumn = min(nColumn, infoPtr->hdpaColumns->nItemCount);
6230 ZeroMemory(&hdi, sizeof(HDITEMW));
6231 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6233 /* insert item in header control */
6234 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6235 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6236 (WPARAM)nColumn, (LPARAM)&hdi);
6237 if (nNewColumn == -1) return -1;
6238 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6240 /* create our own column info */
6241 if (!(lpColumnInfo = COMCTL32_Alloc(sizeof(COLUMN_INFO)))) goto fail;
6242 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6244 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6245 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6247 /* now we have to actually adjust the data */
6248 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6250 SUBITEM_INFO *lpSubItem;
6254 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6256 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6257 for (i = 1; i < hdpaSubItems->nItemCount; i++)
6259 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6260 if (lpSubItem->iSubItem >= nNewColumn)
6261 lpSubItem->iSubItem++;
6266 /* make space for the new column */
6267 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6272 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6275 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6276 COMCTL32_Free(lpColumnInfo);
6283 * Sets the attributes of a header item.
6286 * [I] infoPtr : valid pointer to the listview structure
6287 * [I] nColumn : column index
6288 * [I] lpColumn : column attributes
6289 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6295 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6296 const LVCOLUMNW *lpColumn, BOOL isW)
6298 HDITEMW hdi, hdiget;
6301 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6303 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6305 ZeroMemory(&hdi, sizeof(HDITEMW));
6306 if (lpColumn->mask & LVCF_FMT)
6308 hdi.mask |= HDI_FORMAT;
6309 hdiget.mask = HDI_FORMAT;
6310 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6311 hdi.fmt = hdiget.fmt & HDF_STRING;
6313 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6315 /* set header item attributes */
6316 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6317 if (!bResult) return FALSE;
6319 if (lpColumn->mask & LVCF_FMT)
6321 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6322 int oldFmt = lpColumnInfo->fmt;
6324 lpColumnInfo->fmt = lpColumn->fmt;
6325 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6327 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6328 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6337 * Sets the column order array
6340 * [I] infoPtr : valid pointer to the listview structure
6341 * [I] iCount : number of elements in column order array
6342 * [I] lpiArray : pointer to column order array
6348 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6350 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6361 * Sets the width of a column
6364 * [I] infoPtr : valid pointer to the listview structure
6365 * [I] nColumn : column index
6366 * [I] cx : column width
6372 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6374 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6375 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6379 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6381 /* set column width only if in report or list mode */
6382 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6384 /* take care of invalid cx values */
6385 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6386 else if (uView == LVS_LIST && cx < 1) return FALSE;
6388 /* resize all columns if in LVS_LIST mode */
6389 if(uView == LVS_LIST)
6391 infoPtr->nItemWidth = cx;
6392 LISTVIEW_InvalidateList(infoPtr);
6396 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6398 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < infoPtr->hdpaColumns->nItemCount -1))
6403 lvItem.mask = LVIF_TEXT;
6405 lvItem.iSubItem = nColumn;
6406 lvItem.pszText = szDispText;
6407 lvItem.cchTextMax = DISP_TEXT_SIZE;
6408 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6410 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6411 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6412 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6414 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6415 max_cx += infoPtr->iconSize.cx;
6416 max_cx += TRAILING_LABEL_PADDING;
6419 /* autosize based on listview items width */
6420 if(cx == LVSCW_AUTOSIZE)
6422 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6424 /* if iCol is the last column make it fill the remainder of the controls width */
6425 if(nColumn == infoPtr->hdpaColumns->nItemCount - 1)
6430 LISTVIEW_GetOrigin(infoPtr, &Origin);
6431 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6433 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6437 /* Despite what the MS docs say, if this is not the last
6438 column, then MS resizes the column to the width of the
6439 largest text string in the column, including headers
6440 and items. This is different from LVSCW_AUTOSIZE in that
6441 LVSCW_AUTOSIZE ignores the header string length. */
6444 /* retrieve header text */
6445 hdi.mask = HDI_TEXT;
6446 hdi.cchTextMax = DISP_TEXT_SIZE;
6447 hdi.pszText = szDispText;
6448 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6450 HDC hdc = GetDC(infoPtr->hwndSelf);
6451 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6454 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6455 cx = size.cx + TRAILING_HEADER_PADDING;
6456 /* FIXME: Take into account the header image, if one is present */
6457 SelectObject(hdc, old_font);
6458 ReleaseDC(infoPtr->hwndSelf, hdc);
6460 cx = max (cx, max_cx);
6464 if (cx < 0) return FALSE;
6466 /* call header to update the column change */
6467 hdi.mask = HDI_WIDTH;
6469 TRACE("hdi.cxy=%d\n", hdi.cxy);
6470 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6475 * Sets the extended listview style.
6478 * [I] infoPtr : valid pointer to the listview structure
6480 * [I] dwStyle : style
6483 * SUCCESS : previous style
6486 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6488 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6492 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6494 infoPtr->dwLvExStyle = dwStyle;
6501 * Sets the new hot cursor used during hot tracking and hover selection.
6504 * [I] infoPtr : valid pointer to the listview structure
6505 * [I} hCurosr : the new hot cursor handle
6508 * Returns the previous hot cursor
6510 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6512 HCURSOR oldCursor = infoPtr->hHotCursor;
6514 infoPtr->hHotCursor = hCursor;
6522 * Sets the hot item index.
6525 * [I] infoPtr : valid pointer to the listview structure
6526 * [I] iIndex : index
6529 * SUCCESS : previous hot item index
6530 * FAILURE : -1 (no hot item)
6532 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6534 INT iOldIndex = infoPtr->nHotItem;
6536 infoPtr->nHotItem = iIndex;
6544 * Sets the amount of time the cursor must hover over an item before it is selected.
6547 * [I] infoPtr : valid pointer to the listview structure
6548 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6551 * Returns the previous hover time
6553 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6555 DWORD oldHoverTime = infoPtr->dwHoverTime;
6557 infoPtr->dwHoverTime = dwHoverTime;
6559 return oldHoverTime;
6564 * Sets spacing for icons of LVS_ICON style.
6567 * [I] infoPtr : valid pointer to the listview structure
6568 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6569 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6572 * MAKELONG(oldcx, oldcy)
6574 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6576 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6577 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6579 TRACE("requested=(%d,%d)\n", cx, cy);
6581 /* this is supported only for LVS_ICON style */
6582 if (uView != LVS_ICON) return oldspacing;
6584 /* set to defaults, if instructed to */
6585 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6586 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6588 /* if 0 then compute width
6589 * FIXME: Should scan each item and determine max width of
6590 * icon or label, then make that the width */
6592 cx = infoPtr->iconSpacing.cx;
6594 /* if 0 then compute height */
6596 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6597 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6600 infoPtr->iconSpacing.cx = cx;
6601 infoPtr->iconSpacing.cy = cy;
6603 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6604 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6605 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6606 infoPtr->ntmHeight);
6608 /* these depend on the iconSpacing */
6609 LISTVIEW_UpdateItemSize(infoPtr);
6614 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6618 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6625 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6626 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6635 * [I] infoPtr : valid pointer to the listview structure
6636 * [I] nType : image list type
6637 * [I] himl : image list handle
6640 * SUCCESS : old image list
6643 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6645 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6646 INT oldHeight = infoPtr->nItemHeight;
6647 HIMAGELIST himlOld = 0;
6649 TRACE("(nType=%d, himl=%p\n", nType, himl);
6654 himlOld = infoPtr->himlNormal;
6655 infoPtr->himlNormal = himl;
6656 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6657 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6661 himlOld = infoPtr->himlSmall;
6662 infoPtr->himlSmall = himl;
6663 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6667 himlOld = infoPtr->himlState;
6668 infoPtr->himlState = himl;
6669 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6670 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6674 ERR("Unknown icon type=%d\n", nType);
6678 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6679 if (infoPtr->nItemHeight != oldHeight)
6680 LISTVIEW_UpdateScroll(infoPtr);
6687 * Preallocates memory (does *not* set the actual count of items !)
6690 * [I] infoPtr : valid pointer to the listview structure
6691 * [I] nItems : item count (projected number of items to allocate)
6692 * [I] dwFlags : update flags
6698 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6700 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6702 if (infoPtr->dwStyle & LVS_OWNERDATA)
6704 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6705 INT nOldCount = infoPtr->nItemCount;
6707 if (nItems < nOldCount)
6709 RANGE range = { nItems, nOldCount };
6710 ranges_del(infoPtr->selectionRanges, range);
6711 if (infoPtr->nFocusedItem >= nItems)
6713 infoPtr->nFocusedItem = -1;
6714 SetRectEmpty(&infoPtr->rcFocus);
6718 infoPtr->nItemCount = nItems;
6719 LISTVIEW_UpdateScroll(infoPtr);
6721 /* the flags are valid only in ownerdata report and list modes */
6722 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6724 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6725 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6727 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6728 LISTVIEW_InvalidateList(infoPtr);
6735 LISTVIEW_GetOrigin(infoPtr, &Origin);
6736 nFrom = min(nOldCount, nItems);
6737 nTo = max(nOldCount, nItems);
6739 if (uView == LVS_REPORT)
6742 rcErase.top = nFrom * infoPtr->nItemHeight;
6743 rcErase.right = infoPtr->nItemWidth;
6744 rcErase.bottom = nTo * infoPtr->nItemHeight;
6745 OffsetRect(&rcErase, Origin.x, Origin.y);
6746 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6747 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6751 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6753 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6754 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6755 rcErase.right = rcErase.left + infoPtr->nItemWidth;
6756 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6757 OffsetRect(&rcErase, Origin.x, Origin.y);
6758 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6759 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6761 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6763 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
6764 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6765 OffsetRect(&rcErase, Origin.x, Origin.y);
6766 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6767 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6773 /* According to MSDN for non-LVS_OWNERDATA this is just
6774 * a performance issue. The control allocates its internal
6775 * data structures for the number of items specified. It
6776 * cuts down on the number of memory allocations. Therefore
6777 * we will just issue a WARN here
6779 WARN("for non-ownerdata performance option not implemented.\n");
6787 * Sets the position of an item.
6790 * [I] infoPtr : valid pointer to the listview structure
6791 * [I] nItem : item index
6792 * [I] pt : coordinate
6798 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6800 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6803 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6805 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6806 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6808 LISTVIEW_GetOrigin(infoPtr, &Origin);
6810 /* This point value seems to be an undocumented feature.
6811 * The best guess is that it means either at the origin,
6812 * or at true beginning of the list. I will assume the origin. */
6813 if ((pt.x == -1) && (pt.y == -1))
6816 if (uView == LVS_ICON)
6818 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6819 pt.y -= ICON_TOP_PADDING;
6824 infoPtr->bAutoarrange = FALSE;
6826 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
6831 * Sets the state of one or many items.
6834 * [I] infoPtr : valid pointer to the listview structure
6835 * [I] nItem : item index
6836 * [I] lpLVItem : item or subitem info
6842 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
6844 BOOL bResult = TRUE;
6847 lvItem.iItem = nItem;
6848 lvItem.iSubItem = 0;
6849 lvItem.mask = LVIF_STATE;
6850 lvItem.state = lpLVItem->state;
6851 lvItem.stateMask = lpLVItem->stateMask;
6852 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6856 /* apply to all items */
6857 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6858 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6861 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6868 * Sets the text of an item or subitem.
6871 * [I] hwnd : window handle
6872 * [I] nItem : item index
6873 * [I] lpLVItem : item or subitem info
6874 * [I] isW : TRUE if input is Unicode
6880 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
6884 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6886 lvItem.iItem = nItem;
6887 lvItem.iSubItem = lpLVItem->iSubItem;
6888 lvItem.mask = LVIF_TEXT;
6889 lvItem.pszText = lpLVItem->pszText;
6890 lvItem.cchTextMax = lpLVItem->cchTextMax;
6892 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6894 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6899 * Set item index that marks the start of a multiple selection.
6902 * [I] infoPtr : valid pointer to the listview structure
6903 * [I] nIndex : index
6906 * Index number or -1 if there is no selection mark.
6908 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6910 INT nOldIndex = infoPtr->nSelectionMark;
6912 TRACE("(nIndex=%d)\n", nIndex);
6914 infoPtr->nSelectionMark = nIndex;
6921 * Sets the text background color.
6924 * [I] infoPtr : valid pointer to the listview structure
6925 * [I] clrTextBk : text background color
6931 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6933 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6935 if (infoPtr->clrTextBk != clrTextBk)
6937 infoPtr->clrTextBk = clrTextBk;
6938 LISTVIEW_InvalidateList(infoPtr);
6946 * Sets the text foreground color.
6949 * [I] infoPtr : valid pointer to the listview structure
6950 * [I] clrText : text color
6956 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6958 TRACE("(clrText=%lx)\n", clrText);
6960 if (infoPtr->clrText != clrText)
6962 infoPtr->clrText = clrText;
6963 LISTVIEW_InvalidateList(infoPtr);
6971 * Determines which listview item is located at the specified position.
6974 * [I] infoPtr : valid pointer to the listview structure
6975 * [I] hwndNewToolTip : handle to new ToolTip
6980 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
6982 HWND hwndOldToolTip = infoPtr->hwndToolTip;
6983 infoPtr->hwndToolTip = hwndNewToolTip;
6984 return hwndOldToolTip;
6987 /* LISTVIEW_SetUnicodeFormat */
6988 /* LISTVIEW_SetWorkAreas */
6992 * Callback internally used by LISTVIEW_SortItems()
6995 * [I] first : pointer to first ITEM_INFO to compare
6996 * [I] second : pointer to second ITEM_INFO to compare
6997 * [I] lParam : HWND of control
7000 * if first comes before second : negative
7001 * if first comes after second : positive
7002 * if first and second are equivalent : zero
7004 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7006 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7007 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7008 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7010 /* Forward the call to the client defined callback */
7011 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7016 * Sorts the listview items.
7019 * [I] infoPtr : valid pointer to the listview structure
7020 * [I] pfnCompare : application-defined value
7021 * [I] lParamSort : pointer to comparision callback
7027 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7029 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7032 LPVOID selectionMarkItem;
7036 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7038 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7040 if (!infoPtr->hdpaItems) return FALSE;
7042 /* if there are 0 or 1 items, there is no need to sort */
7043 if (infoPtr->nItemCount < 2) return TRUE;
7045 if (infoPtr->nFocusedItem >= 0)
7047 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7048 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7049 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7051 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7052 /* clear the lpItem->state for non-selected ones */
7053 /* remove the selection ranges */
7055 infoPtr->pfnCompare = pfnCompare;
7056 infoPtr->lParamSort = lParamSort;
7057 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
7059 /* Adjust selections and indices so that they are the way they should
7060 * be after the sort (otherwise, the list items move around, but
7061 * whatever is at the item's previous original position will be
7064 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7065 for (i=0; i < infoPtr->nItemCount; i++)
7067 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7068 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7070 if (lpItem->state & LVIS_SELECTED)
7072 item.state = LVIS_SELECTED;
7073 item.stateMask = LVIS_SELECTED;
7074 LISTVIEW_SetItemState(infoPtr, i, &item);
7076 if (lpItem->state & LVIS_FOCUSED)
7078 infoPtr->nFocusedItem = i;
7079 lpItem->state &= ~LVIS_FOCUSED;
7082 if (selectionMarkItem != NULL)
7083 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7084 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7086 /* refresh the display */
7087 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7088 LISTVIEW_InvalidateList(infoPtr);
7095 * Updates an items or rearranges the listview control.
7098 * [I] infoPtr : valid pointer to the listview structure
7099 * [I] nItem : item index
7105 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7107 TRACE("(nItem=%d)\n", nItem);
7109 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7111 /* rearrange with default alignment style */
7112 if (is_autoarrange(infoPtr))
7113 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7115 LISTVIEW_InvalidateItem(infoPtr, nItem);
7123 * Creates the listview control.
7126 * [I] hwnd : window handle
7127 * [I] lpcs : the create parameters
7133 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7135 LISTVIEW_INFO *infoPtr;
7136 UINT uView = lpcs->style & LVS_TYPEMASK;
7139 TRACE("(lpcs=%p)\n", lpcs);
7141 /* initialize info pointer */
7142 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7143 if (!infoPtr) return -1;
7145 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7147 infoPtr->hwndSelf = hwnd;
7148 infoPtr->dwStyle = lpcs->style;
7149 /* determine the type of structures to use */
7150 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7151 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7153 /* initialize color information */
7154 infoPtr->clrBk = CLR_NONE;
7155 infoPtr->clrText = comctl32_color.clrWindowText;
7156 infoPtr->clrTextBk = CLR_DEFAULT;
7157 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7159 /* set default values */
7160 infoPtr->nFocusedItem = -1;
7161 infoPtr->nSelectionMark = -1;
7162 infoPtr->nHotItem = -1;
7163 infoPtr->bRedraw = TRUE;
7164 infoPtr->bNoItemMetrics = TRUE;
7165 infoPtr->bDoChangeNotify = TRUE;
7166 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7167 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7168 infoPtr->nEditLabelItem = -1;
7169 infoPtr->dwHoverTime = -1; /* default system hover time */
7171 /* get default font (icon title) */
7172 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7173 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7174 infoPtr->hFont = infoPtr->hDefaultFont;
7175 LISTVIEW_SaveTextMetrics(infoPtr);
7178 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7179 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7180 0, 0, 0, 0, hwnd, NULL,
7181 lpcs->hInstance, NULL);
7182 if (!infoPtr->hwndHeader) goto fail;
7184 /* set header unicode format */
7185 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7187 /* set header font */
7188 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7190 /* allocate memory for the data structure */
7191 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7192 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7193 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7194 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7195 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7197 /* initialize the icon sizes */
7198 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7199 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7201 /* init item size to avoid division by 0 */
7202 LISTVIEW_UpdateItemSize (infoPtr);
7204 if (uView == LVS_REPORT)
7206 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7208 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7212 /* set HDS_HIDDEN flag to hide the header bar */
7213 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7214 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7221 DestroyWindow(infoPtr->hwndHeader);
7222 ranges_destroy(infoPtr->selectionRanges);
7223 DPA_Destroy(infoPtr->hdpaItems);
7224 DPA_Destroy(infoPtr->hdpaPosX);
7225 DPA_Destroy(infoPtr->hdpaPosY);
7226 DPA_Destroy(infoPtr->hdpaColumns);
7227 COMCTL32_Free(infoPtr);
7233 * Erases the background of the listview control.
7236 * [I] infoPtr : valid pointer to the listview structure
7237 * [I] hdc : device context handle
7243 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7247 TRACE("(hdc=%p)\n", hdc);
7249 if (!GetClipBox(hdc, &rc)) return FALSE;
7251 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7257 * Helper function for LISTVIEW_[HV]Scroll *only*.
7258 * Performs vertical/horizontal scrolling by a give amount.
7261 * [I] infoPtr : valid pointer to the listview structure
7262 * [I] dx : amount of horizontal scroll
7263 * [I] dy : amount of vertical scroll
7265 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7267 /* now we can scroll the list */
7268 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7269 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7270 /* if we have focus, adjust rect */
7271 OffsetRect(&infoPtr->rcFocus, dx, dy);
7272 UpdateWindow(infoPtr->hwndSelf);
7277 * Performs vertical scrolling.
7280 * [I] infoPtr : valid pointer to the listview structure
7281 * [I] nScrollCode : scroll code
7282 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7283 * [I] hScrollWnd : scrollbar control window handle
7289 * SB_LINEUP/SB_LINEDOWN:
7290 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7291 * for LVS_REPORT is 1 line
7292 * for LVS_LIST cannot occur
7295 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7296 INT nScrollDiff, HWND hScrollWnd)
7298 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7299 INT nOldScrollPos, nNewScrollPos;
7300 SCROLLINFO scrollInfo;
7303 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7304 debugscrollcode(nScrollCode), nScrollDiff);
7306 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7308 scrollInfo.cbSize = sizeof(SCROLLINFO);
7309 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7311 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7313 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7315 nOldScrollPos = scrollInfo.nPos;
7316 switch (nScrollCode)
7322 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7326 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7330 nScrollDiff = -scrollInfo.nPage;
7334 nScrollDiff = scrollInfo.nPage;
7337 case SB_THUMBPOSITION:
7339 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7346 /* quit right away if pos isn't changing */
7347 if (nScrollDiff == 0) return 0;
7349 /* calculate new position, and handle overflows */
7350 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7351 if (nScrollDiff > 0) {
7352 if (nNewScrollPos < nOldScrollPos ||
7353 nNewScrollPos > scrollInfo.nMax)
7354 nNewScrollPos = scrollInfo.nMax;
7356 if (nNewScrollPos > nOldScrollPos ||
7357 nNewScrollPos < scrollInfo.nMin)
7358 nNewScrollPos = scrollInfo.nMin;
7361 /* set the new position, and reread in case it changed */
7362 scrollInfo.fMask = SIF_POS;
7363 scrollInfo.nPos = nNewScrollPos;
7364 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7366 /* carry on only if it really changed */
7367 if (nNewScrollPos == nOldScrollPos) return 0;
7369 /* now adjust to client coordinates */
7370 nScrollDiff = nOldScrollPos - nNewScrollPos;
7371 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7373 /* and scroll the window */
7374 scroll_list(infoPtr, 0, nScrollDiff);
7381 * Performs horizontal scrolling.
7384 * [I] infoPtr : valid pointer to the listview structure
7385 * [I] nScrollCode : scroll code
7386 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7387 * [I] hScrollWnd : scrollbar control window handle
7393 * SB_LINELEFT/SB_LINERIGHT:
7394 * for LVS_ICON, LVS_SMALLICON 1 pixel
7395 * for LVS_REPORT is 1 pixel
7396 * for LVS_LIST is 1 column --> which is a 1 because the
7397 * scroll is based on columns not pixels
7400 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7401 INT nScrollDiff, HWND hScrollWnd)
7403 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7404 INT nOldScrollPos, nNewScrollPos;
7405 SCROLLINFO scrollInfo;
7407 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7408 debugscrollcode(nScrollCode), nScrollDiff);
7410 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7412 scrollInfo.cbSize = sizeof(SCROLLINFO);
7413 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7415 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7417 nOldScrollPos = scrollInfo.nPos;
7419 switch (nScrollCode)
7433 nScrollDiff = -scrollInfo.nPage;
7437 nScrollDiff = scrollInfo.nPage;
7440 case SB_THUMBPOSITION:
7442 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7449 /* quit right away if pos isn't changing */
7450 if (nScrollDiff == 0) return 0;
7452 /* calculate new position, and handle overflows */
7453 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7454 if (nScrollDiff > 0) {
7455 if (nNewScrollPos < nOldScrollPos ||
7456 nNewScrollPos > scrollInfo.nMax)
7457 nNewScrollPos = scrollInfo.nMax;
7459 if (nNewScrollPos > nOldScrollPos ||
7460 nNewScrollPos < scrollInfo.nMin)
7461 nNewScrollPos = scrollInfo.nMin;
7464 /* set the new position, and reread in case it changed */
7465 scrollInfo.fMask = SIF_POS;
7466 scrollInfo.nPos = nNewScrollPos;
7467 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7469 /* carry on only if it really changed */
7470 if (nNewScrollPos == nOldScrollPos) return 0;
7472 if(uView == LVS_REPORT)
7473 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7475 /* now adjust to client coordinates */
7476 nScrollDiff = nOldScrollPos - nNewScrollPos;
7477 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7479 /* and scroll the window */
7480 scroll_list(infoPtr, nScrollDiff, 0);
7485 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7487 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7488 INT gcWheelDelta = 0;
7489 UINT pulScrollLines = 3;
7490 SCROLLINFO scrollInfo;
7492 TRACE("(wheelDelta=%d)\n", wheelDelta);
7494 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7495 gcWheelDelta -= wheelDelta;
7497 scrollInfo.cbSize = sizeof(SCROLLINFO);
7498 scrollInfo.fMask = SIF_POS;
7505 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7506 * should be fixed in the future.
7508 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7509 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7513 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7515 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7516 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7517 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7522 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7533 * [I] infoPtr : valid pointer to the listview structure
7534 * [I] nVirtualKey : virtual key
7535 * [I] lKeyData : key data
7540 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7542 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7544 NMLVKEYDOWN nmKeyDown;
7546 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7548 /* send LVN_KEYDOWN notification */
7549 nmKeyDown.wVKey = nVirtualKey;
7550 nmKeyDown.flags = 0;
7551 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7553 switch (nVirtualKey)
7556 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7558 notify(infoPtr, NM_RETURN);
7559 notify(infoPtr, LVN_ITEMACTIVATE);
7564 if (infoPtr->nItemCount > 0)
7569 if (infoPtr->nItemCount > 0)
7570 nItem = infoPtr->nItemCount - 1;
7574 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7578 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7582 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7586 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7590 if (uView == LVS_REPORT)
7591 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7593 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7594 * LISTVIEW_GetCountPerRow(infoPtr);
7595 if(nItem < 0) nItem = 0;
7599 if (uView == LVS_REPORT)
7600 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7602 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7603 * LISTVIEW_GetCountPerRow(infoPtr);
7604 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7608 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7609 LISTVIEW_KeySelection(infoPtr, nItem);
7619 * [I] infoPtr : valid pointer to the listview structure
7624 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7628 /* if we did not have the focus, there's nothing to do */
7629 if (!infoPtr->bFocus) return 0;
7631 /* send NM_KILLFOCUS notification */
7632 notify(infoPtr, NM_KILLFOCUS);
7634 /* if we have a focus rectagle, get rid of it */
7635 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7637 /* set window focus flag */
7638 infoPtr->bFocus = FALSE;
7640 /* invalidate the selected items before reseting focus flag */
7641 LISTVIEW_InvalidateSelectedItems(infoPtr);
7648 * Processes double click messages (left mouse button).
7651 * [I] infoPtr : valid pointer to the listview structure
7652 * [I] wKey : key flag
7653 * [I] pts : mouse coordinate
7658 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7660 LVHITTESTINFO htInfo;
7662 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7664 /* send NM_RELEASEDCAPTURE notification */
7665 notify(infoPtr, NM_RELEASEDCAPTURE);
7667 htInfo.pt.x = pts.x;
7668 htInfo.pt.y = pts.y;
7670 /* send NM_DBLCLK notification */
7671 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7672 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7674 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7675 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7682 * Processes mouse down messages (left mouse button).
7685 * [I] infoPtr : valid pointer to the listview structure
7686 * [I] wKey : key flag
7687 * [I] pts : mouse coordinate
7692 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7694 LVHITTESTINFO lvHitTestInfo;
7695 static BOOL bGroupSelect = TRUE;
7696 POINT pt = { pts.x, pts.y };
7699 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7701 /* send NM_RELEASEDCAPTURE notification */
7702 notify(infoPtr, NM_RELEASEDCAPTURE);
7704 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7706 /* set left button down flag */
7707 infoPtr->bLButtonDown = TRUE;
7709 lvHitTestInfo.pt.x = pts.x;
7710 lvHitTestInfo.pt.y = pts.y;
7712 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7713 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7714 infoPtr->nEditLabelItem = -1;
7715 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7717 if (infoPtr->dwStyle & LVS_SINGLESEL)
7719 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7720 infoPtr->nEditLabelItem = nItem;
7722 LISTVIEW_SetSelection(infoPtr, nItem);
7726 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7730 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7731 LISTVIEW_SetItemFocus(infoPtr, nItem);
7732 infoPtr->nSelectionMark = nItem;
7738 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7739 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7741 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7742 infoPtr->nSelectionMark = nItem;
7745 else if (wKey & MK_CONTROL)
7749 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7751 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7752 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7753 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7754 infoPtr->nSelectionMark = nItem;
7756 else if (wKey & MK_SHIFT)
7758 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7762 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7763 infoPtr->nEditLabelItem = nItem;
7765 /* set selection (clears other pre-existing selections) */
7766 LISTVIEW_SetSelection(infoPtr, nItem);
7772 /* remove all selections */
7773 LISTVIEW_DeselectAll(infoPtr);
7781 * Processes mouse up messages (left mouse button).
7784 * [I] infoPtr : valid pointer to the listview structure
7785 * [I] wKey : key flag
7786 * [I] pts : mouse coordinate
7791 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7793 LVHITTESTINFO lvHitTestInfo;
7795 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7797 if (!infoPtr->bLButtonDown) return 0;
7799 lvHitTestInfo.pt.x = pts.x;
7800 lvHitTestInfo.pt.y = pts.y;
7802 /* send NM_CLICK notification */
7803 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7804 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7806 /* set left button flag */
7807 infoPtr->bLButtonDown = FALSE;
7809 /* if we clicked on a selected item, edit the label */
7810 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7811 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7818 * Destroys the listview control (called after WM_DESTROY).
7821 * [I] infoPtr : valid pointer to the listview structure
7826 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7830 /* delete all items */
7831 LISTVIEW_DeleteAllItems(infoPtr);
7833 /* destroy data structure */
7834 DPA_Destroy(infoPtr->hdpaItems);
7835 DPA_Destroy(infoPtr->hdpaPosX);
7836 DPA_Destroy(infoPtr->hdpaPosY);
7837 DPA_Destroy(infoPtr->hdpaColumns);
7838 ranges_destroy(infoPtr->selectionRanges);
7840 /* destroy image lists */
7841 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
7843 if (infoPtr->himlNormal)
7844 ImageList_Destroy(infoPtr->himlNormal);
7845 if (infoPtr->himlSmall)
7846 ImageList_Destroy(infoPtr->himlSmall);
7847 if (infoPtr->himlState)
7848 ImageList_Destroy(infoPtr->himlState);
7851 /* destroy font, bkgnd brush */
7853 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7854 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7856 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7858 /* free listview info pointer*/
7859 COMCTL32_Free(infoPtr);
7866 * Handles notifications from header.
7869 * [I] infoPtr : valid pointer to the listview structure
7870 * [I] nCtrlId : control identifier
7871 * [I] lpnmh : notification information
7876 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
7878 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7880 TRACE("(lpnmh=%p)\n", lpnmh);
7882 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= infoPtr->hdpaColumns->nItemCount) return 0;
7884 switch (lpnmh->hdr.code)
7888 case HDN_ITEMCHANGEDW:
7889 case HDN_ITEMCHANGEDA:
7891 COLUMN_INFO *lpColumnInfo;
7894 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
7898 hdi.mask = HDI_WIDTH;
7899 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
7903 cxy = lpnmh->pitem->cxy;
7905 /* determine how much we change since the last know position */
7906 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
7907 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7910 RECT rcCol = lpColumnInfo->rcHeader;
7912 lpColumnInfo->rcHeader.right += dx;
7913 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
7914 if (uView == LVS_REPORT && is_redrawing(infoPtr))
7916 /* this trick works for left aligned columns only */
7917 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
7919 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
7920 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
7922 rcCol.top = infoPtr->rcList.top;
7923 rcCol.bottom = infoPtr->rcList.bottom;
7924 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
7930 case HDN_ITEMCLICKW:
7931 case HDN_ITEMCLICKA:
7933 /* Handle sorting by Header Column */
7936 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7938 nmlv.iSubItem = lpnmh->iItem;
7939 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7949 * Determines the type of structure to use.
7952 * [I] infoPtr : valid pointer to the listview structureof the sender
7953 * [I] hwndFrom : listview window handle
7954 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
7959 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7961 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
7963 if (nCommand != NF_REQUERY) return 0;
7965 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
7972 * Paints/Repaints the listview control.
7975 * [I] infoPtr : valid pointer to the listview structure
7976 * [I] hdc : device context handle
7981 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7983 TRACE("(hdc=%p)\n", hdc);
7985 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
7987 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7989 infoPtr->bNoItemMetrics = FALSE;
7990 LISTVIEW_UpdateItemSize(infoPtr);
7991 if (uView == LVS_ICON || uView == LVS_SMALLICON)
7992 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7993 LISTVIEW_UpdateScroll(infoPtr);
7996 LISTVIEW_Refresh(infoPtr, hdc);
8001 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8003 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8004 LISTVIEW_Refresh(infoPtr, hdc);
8005 EndPaint(infoPtr->hwndSelf, &ps);
8013 * Processes double click messages (right mouse button).
8016 * [I] infoPtr : valid pointer to the listview structure
8017 * [I] wKey : key flag
8018 * [I] pts : mouse coordinate
8023 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8025 LVHITTESTINFO lvHitTestInfo;
8027 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8029 /* send NM_RELEASEDCAPTURE notification */
8030 notify(infoPtr, NM_RELEASEDCAPTURE);
8032 /* send NM_RDBLCLK notification */
8033 lvHitTestInfo.pt.x = pts.x;
8034 lvHitTestInfo.pt.y = pts.y;
8035 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8036 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8043 * Processes mouse down messages (right mouse button).
8046 * [I] infoPtr : valid pointer to the listview structure
8047 * [I] wKey : key flag
8048 * [I] pts : mouse coordinate
8053 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8055 LVHITTESTINFO lvHitTestInfo;
8058 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8060 /* send NM_RELEASEDCAPTURE notification */
8061 notify(infoPtr, NM_RELEASEDCAPTURE);
8063 /* make sure the listview control window has the focus */
8064 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8066 /* set right button down flag */
8067 infoPtr->bRButtonDown = TRUE;
8069 /* determine the index of the selected item */
8070 lvHitTestInfo.pt.x = pts.x;
8071 lvHitTestInfo.pt.y = pts.y;
8072 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8074 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8076 LISTVIEW_SetItemFocus(infoPtr, nItem);
8077 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8078 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8079 LISTVIEW_SetSelection(infoPtr, nItem);
8083 LISTVIEW_DeselectAll(infoPtr);
8091 * Processes mouse up messages (right mouse button).
8094 * [I] infoPtr : valid pointer to the listview structure
8095 * [I] wKey : key flag
8096 * [I] pts : mouse coordinate
8101 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8103 LVHITTESTINFO lvHitTestInfo;
8106 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8108 if (!infoPtr->bRButtonDown) return 0;
8110 /* set button flag */
8111 infoPtr->bRButtonDown = FALSE;
8113 /* Send NM_RClICK notification */
8114 lvHitTestInfo.pt.x = pts.x;
8115 lvHitTestInfo.pt.y = pts.y;
8116 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8117 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8119 /* Change to screen coordinate for WM_CONTEXTMENU */
8120 pt = lvHitTestInfo.pt;
8121 ClientToScreen(infoPtr->hwndSelf, &pt);
8123 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8124 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8125 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8136 * [I] infoPtr : valid pointer to the listview structure
8137 * [I] hwnd : window handle of window containing the cursor
8138 * [I] nHittest : hit-test code
8139 * [I] wMouseMsg : ideintifier of the mouse message
8142 * TRUE if cursor is set
8145 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8147 LVHITTESTINFO lvHitTestInfo;
8149 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8151 if(!infoPtr->hHotCursor) return FALSE;
8153 GetCursorPos(&lvHitTestInfo.pt);
8154 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8156 SetCursor(infoPtr->hHotCursor);
8166 * [I] infoPtr : valid pointer to the listview structure
8167 * [I] hwndLoseFocus : handle of previously focused window
8172 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8174 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8176 /* if we have the focus already, there's nothing to do */
8177 if (infoPtr->bFocus) return 0;
8179 /* send NM_SETFOCUS notification */
8180 notify(infoPtr, NM_SETFOCUS);
8182 /* set window focus flag */
8183 infoPtr->bFocus = TRUE;
8185 /* put the focus rect back on */
8186 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8188 /* redraw all visible selected items */
8189 LISTVIEW_InvalidateSelectedItems(infoPtr);
8199 * [I] infoPtr : valid pointer to the listview structure
8200 * [I] fRedraw : font handle
8201 * [I] fRedraw : redraw flag
8206 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8208 HFONT oldFont = infoPtr->hFont;
8210 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8212 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8213 if (infoPtr->hFont == oldFont) return 0;
8215 LISTVIEW_SaveTextMetrics(infoPtr);
8217 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8218 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8220 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8227 * Message handling for WM_SETREDRAW.
8228 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8231 * [I] infoPtr : valid pointer to the listview structure
8232 * [I] bRedraw: state of redraw flag
8235 * DefWinProc return value
8237 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8239 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8241 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8242 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8244 infoPtr->bRedraw = bRedraw;
8246 if(!bRedraw) return 0;
8248 if (is_autoarrange(infoPtr))
8249 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8250 LISTVIEW_UpdateScroll(infoPtr);
8252 /* despite what the WM_SETREDRAW docs says, apps expect us
8253 * to invalidate the listview here... stupid! */
8254 LISTVIEW_InvalidateList(infoPtr);
8261 * Resizes the listview control. This function processes WM_SIZE
8262 * messages. At this time, the width and height are not used.
8265 * [I] infoPtr : valid pointer to the listview structure
8266 * [I] Width : new width
8267 * [I] Height : new height
8272 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8274 RECT rcOld = infoPtr->rcList;
8276 TRACE("(width=%d, height=%d)\n", Width, Height);
8278 LISTVIEW_UpdateSize(infoPtr);
8279 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8281 /* do not bother with display related stuff if we're not redrawing */
8282 if (!is_redrawing(infoPtr)) return 0;
8284 if (is_autoarrange(infoPtr))
8285 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8287 LISTVIEW_UpdateScroll(infoPtr);
8289 /* refresh all only for lists whose height changed significantly */
8290 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8291 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8292 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8293 LISTVIEW_InvalidateList(infoPtr);
8300 * Sets the size information.
8303 * [I] infoPtr : valid pointer to the listview structure
8308 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8310 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8312 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8314 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8316 if (uView == LVS_LIST)
8318 /* Apparently the "LIST" style is supposed to have the same
8319 * number of items in a column even if there is no scroll bar.
8320 * Since if a scroll bar already exists then the bottom is already
8321 * reduced, only reduce if the scroll bar does not currently exist.
8322 * The "2" is there to mimic the native control. I think it may be
8323 * related to either padding or edges. (GLA 7/2002)
8325 if (!(infoPtr->dwStyle & WS_HSCROLL))
8326 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8327 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8329 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8334 hl.prc = &infoPtr->rcList;
8336 Header_Layout(infoPtr->hwndHeader, &hl);
8338 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8340 infoPtr->rcList.top = max(wp.cy, 0);
8343 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8348 * Processes WM_STYLECHANGED messages.
8351 * [I] infoPtr : valid pointer to the listview structure
8352 * [I] wStyleType : window style type (normal or extended)
8353 * [I] lpss : window style information
8358 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8359 const STYLESTRUCT *lpss)
8361 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8362 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8364 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8365 wStyleType, lpss->styleOld, lpss->styleNew);
8367 if (wStyleType != GWL_STYLE) return 0;
8369 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8370 /* what if LVS_OWNERDATA changed? */
8371 /* or LVS_SINGLESEL */
8372 /* or LVS_SORT{AS,DES}CENDING */
8374 infoPtr->dwStyle = lpss->styleNew;
8376 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8377 ((lpss->styleNew & WS_HSCROLL) == 0))
8378 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8380 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8381 ((lpss->styleNew & WS_VSCROLL) == 0))
8382 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8384 if (uNewView != uOldView)
8386 SIZE oldIconSize = infoPtr->iconSize;
8389 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8390 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8392 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8393 SetRectEmpty(&infoPtr->rcFocus);
8395 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8396 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8398 if (uNewView == LVS_ICON)
8400 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8402 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8403 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8404 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8407 else if (uNewView == LVS_REPORT)
8412 hl.prc = &infoPtr->rcList;
8414 Header_Layout(infoPtr->hwndHeader, &hl);
8415 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8418 LISTVIEW_UpdateItemSize(infoPtr);
8421 if (uNewView == LVS_REPORT)
8422 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8424 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8425 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8426 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8428 /* update the size of the client area */
8429 LISTVIEW_UpdateSize(infoPtr);
8431 /* add scrollbars if needed */
8432 LISTVIEW_UpdateScroll(infoPtr);
8434 /* invalidate client area + erase background */
8435 LISTVIEW_InvalidateList(infoPtr);
8442 * Window procedure of the listview control.
8445 static LRESULT WINAPI
8446 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8448 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8450 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8452 if (!infoPtr && (uMsg != WM_CREATE))
8453 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8457 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8462 case LVM_APPROXIMATEVIEWRECT:
8463 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8464 LOWORD(lParam), HIWORD(lParam));
8466 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8468 /* case LVM_CANCELEDITLABEL: */
8470 /* case LVM_CREATEDRAGIMAGE: */
8472 case LVM_DELETEALLITEMS:
8473 return LISTVIEW_DeleteAllItems(infoPtr);
8475 case LVM_DELETECOLUMN:
8476 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8478 case LVM_DELETEITEM:
8479 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8481 case LVM_EDITLABELW:
8482 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8484 case LVM_EDITLABELA:
8485 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8487 /* case LVM_ENABLEGROUPVIEW: */
8489 case LVM_ENSUREVISIBLE:
8490 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8493 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8496 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8498 case LVM_GETBKCOLOR:
8499 return infoPtr->clrBk;
8501 /* case LVM_GETBKIMAGE: */
8503 case LVM_GETCALLBACKMASK:
8504 return infoPtr->uCallbackMask;
8506 case LVM_GETCOLUMNA:
8507 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8509 case LVM_GETCOLUMNW:
8510 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8512 case LVM_GETCOLUMNORDERARRAY:
8513 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8515 case LVM_GETCOLUMNWIDTH:
8516 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8518 case LVM_GETCOUNTPERPAGE:
8519 return LISTVIEW_GetCountPerPage(infoPtr);
8521 case LVM_GETEDITCONTROL:
8522 return (LRESULT)infoPtr->hwndEdit;
8524 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8525 return infoPtr->dwLvExStyle;
8527 /* case LVM_GETGROUPINFO: */
8529 /* case LVM_GETGROUPMETRICS: */
8532 return (LRESULT)infoPtr->hwndHeader;
8534 case LVM_GETHOTCURSOR:
8535 return (LRESULT)infoPtr->hHotCursor;
8537 case LVM_GETHOTITEM:
8538 return infoPtr->nHotItem;
8540 case LVM_GETHOVERTIME:
8541 return infoPtr->dwHoverTime;
8543 case LVM_GETIMAGELIST:
8544 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8546 /* case LVM_GETINSERTMARK: */
8548 /* case LVM_GETINSERTMARKCOLOR: */
8550 /* case LVM_GETINSERTMARKRECT: */
8552 case LVM_GETISEARCHSTRINGA:
8553 case LVM_GETISEARCHSTRINGW:
8554 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8558 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8561 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8563 case LVM_GETITEMCOUNT:
8564 return infoPtr->nItemCount;
8566 case LVM_GETITEMPOSITION:
8567 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8569 case LVM_GETITEMRECT:
8570 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8572 case LVM_GETITEMSPACING:
8573 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8575 case LVM_GETITEMSTATE:
8576 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8578 case LVM_GETITEMTEXTA:
8579 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8581 case LVM_GETITEMTEXTW:
8582 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8584 case LVM_GETNEXTITEM:
8585 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8587 case LVM_GETNUMBEROFWORKAREAS:
8588 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8592 if (!lParam) return FALSE;
8593 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8596 /* case LVM_GETOUTLINECOLOR: */
8598 /* case LVM_GETSELECTEDCOLUMN: */
8600 case LVM_GETSELECTEDCOUNT:
8601 return LISTVIEW_GetSelectedCount(infoPtr);
8603 case LVM_GETSELECTIONMARK:
8604 return infoPtr->nSelectionMark;
8606 case LVM_GETSTRINGWIDTHA:
8607 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8609 case LVM_GETSTRINGWIDTHW:
8610 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8612 case LVM_GETSUBITEMRECT:
8613 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8615 case LVM_GETTEXTBKCOLOR:
8616 return infoPtr->clrTextBk;
8618 case LVM_GETTEXTCOLOR:
8619 return infoPtr->clrText;
8621 /* case LVM_GETTILEINFO: */
8623 /* case LVM_GETTILEVIEWINFO: */
8625 case LVM_GETTOOLTIPS:
8626 return (LRESULT)infoPtr->hwndToolTip;
8628 case LVM_GETTOPINDEX:
8629 return LISTVIEW_GetTopIndex(infoPtr);
8631 /*case LVM_GETUNICODEFORMAT:
8632 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8635 /* case LVM_GETVIEW: */
8637 case LVM_GETVIEWRECT:
8638 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8640 case LVM_GETWORKAREAS:
8641 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8644 /* case LVM_HASGROUP: */
8647 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8649 case LVM_INSERTCOLUMNA:
8650 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8652 case LVM_INSERTCOLUMNW:
8653 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8655 /* case LVM_INSERTGROUP: */
8657 /* case LVM_INSERTGROUPSORTED: */
8659 case LVM_INSERTITEMA:
8660 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8662 case LVM_INSERTITEMW:
8663 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8665 /* case LVM_INSERTMARKHITTEST: */
8667 /* case LVM_ISGROUPVIEWENABLED: */
8669 /* case LVM_MAPIDTOINDEX: */
8671 /* case LVM_MAPINDEXTOID: */
8673 /* case LVM_MOVEGROUP: */
8675 /* case LVM_MOVEITEMTOGROUP: */
8677 case LVM_REDRAWITEMS:
8678 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8680 /* case LVM_REMOVEALLGROUPS: */
8682 /* case LVM_REMOVEGROUP: */
8685 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8687 case LVM_SETBKCOLOR:
8688 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8690 /* case LVM_SETBKIMAGE: */
8692 case LVM_SETCALLBACKMASK:
8693 infoPtr->uCallbackMask = (UINT)wParam;
8696 case LVM_SETCOLUMNA:
8697 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8699 case LVM_SETCOLUMNW:
8700 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8702 case LVM_SETCOLUMNORDERARRAY:
8703 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8705 case LVM_SETCOLUMNWIDTH:
8706 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8708 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8709 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8711 /* case LVM_SETGROUPINFO: */
8713 /* case LVM_SETGROUPMETRICS: */
8715 case LVM_SETHOTCURSOR:
8716 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8718 case LVM_SETHOTITEM:
8719 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8721 case LVM_SETHOVERTIME:
8722 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8724 case LVM_SETICONSPACING:
8725 return LISTVIEW_SetIconSpacing(infoPtr, SLOWORD(lParam), SHIWORD(lParam));
8727 case LVM_SETIMAGELIST:
8728 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8730 /* case LVM_SETINFOTIP: */
8732 /* case LVM_SETINSERTMARK: */
8734 /* case LVM_SETINSERTMARKCOLOR: */
8737 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8740 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8742 case LVM_SETITEMCOUNT:
8743 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8745 case LVM_SETITEMPOSITION:
8747 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8748 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8751 case LVM_SETITEMPOSITION32:
8752 if (lParam == 0) return FALSE;
8753 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8755 case LVM_SETITEMSTATE:
8756 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8758 case LVM_SETITEMTEXTA:
8759 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8761 case LVM_SETITEMTEXTW:
8762 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8764 /* case LVM_SETOUTLINECOLOR: */
8766 /* case LVM_SETSELECTEDCOLUMN: */
8768 case LVM_SETSELECTIONMARK:
8769 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8771 case LVM_SETTEXTBKCOLOR:
8772 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8774 case LVM_SETTEXTCOLOR:
8775 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8777 /* case LVM_SETTILEINFO: */
8779 /* case LVM_SETTILEVIEWINFO: */
8781 /* case LVM_SETTILEWIDTH: */
8783 case LVM_SETTOOLTIPS:
8784 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
8786 /* case LVM_SETUNICODEFORMAT: */
8788 /* case LVM_SETVIEW: */
8790 /* case LVM_SETWORKAREAS: */
8792 /* case LVM_SORTGROUPS: */
8795 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8797 /* LVM_SORTITEMSEX: */
8799 case LVM_SUBITEMHITTEST:
8800 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8803 return LISTVIEW_Update(infoPtr, (INT)wParam);
8806 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8809 return LISTVIEW_Command(infoPtr, wParam, lParam);
8812 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8815 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8818 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8821 return (LRESULT)infoPtr->hFont;
8824 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8827 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8830 return LISTVIEW_KillFocus(infoPtr);
8832 case WM_LBUTTONDBLCLK:
8833 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8835 case WM_LBUTTONDOWN:
8836 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8839 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8842 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8845 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8848 return LISTVIEW_NCDestroy(infoPtr);
8851 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
8852 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
8855 case WM_NOTIFYFORMAT:
8856 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8859 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8861 case WM_RBUTTONDBLCLK:
8862 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8864 case WM_RBUTTONDOWN:
8865 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8868 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8871 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8876 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8879 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8882 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8885 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8887 case WM_STYLECHANGED:
8888 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8890 case WM_SYSCOLORCHANGE:
8891 COMCTL32_RefreshSysColors();
8894 /* case WM_TIMER: */
8897 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8900 if (wParam & (MK_SHIFT | MK_CONTROL))
8901 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8902 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8904 case WM_WINDOWPOSCHANGED:
8905 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
8907 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8908 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8909 LISTVIEW_UpdateSize(infoPtr);
8910 LISTVIEW_UpdateScroll(infoPtr);
8912 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8914 /* case WM_WININICHANGE: */
8917 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8918 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8921 /* call default window procedure */
8922 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8930 * Registers the window class.
8938 void LISTVIEW_Register(void)
8942 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8943 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8944 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8945 wndClass.cbClsExtra = 0;
8946 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8947 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8948 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8949 wndClass.lpszClassName = WC_LISTVIEWW;
8950 RegisterClassW(&wndClass);
8955 * Unregisters the window class.
8963 void LISTVIEW_Unregister(void)
8965 UnregisterClassW(WC_LISTVIEWW, NULL);
8970 * Handle any WM_COMMAND messages
8973 * [I] infoPtr : valid pointer to the listview structure
8974 * [I] wParam : the first message parameter
8975 * [I] lParam : the second message parameter
8980 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8982 switch (HIWORD(wParam))
8987 * Adjust the edit window size
8990 HDC hdc = GetDC(infoPtr->hwndEdit);
8991 HFONT hFont, hOldFont = 0;
8996 if (!infoPtr->hwndEdit || !hdc) return 0;
8997 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8998 GetWindowRect(infoPtr->hwndEdit, &rect);
9000 /* Select font to get the right dimension of the string */
9001 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9004 hOldFont = SelectObject(hdc, hFont);
9007 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9009 TEXTMETRICW textMetric;
9011 /* Add Extra spacing for the next character */
9012 GetTextMetricsW(hdc, &textMetric);
9013 sz.cx += (textMetric.tmMaxCharWidth * 2);
9021 rect.bottom - rect.top,
9022 SWP_DRAWFRAME|SWP_NOMOVE);
9025 SelectObject(hdc, hOldFont);
9027 ReleaseDC(infoPtr->hwndSelf, hdc);
9033 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
9042 * Subclassed edit control windproc function
9045 * [I] hwnd : the edit window handle
9046 * [I] uMsg : the message that is to be processed
9047 * [I] wParam : first message parameter
9048 * [I] lParam : second message parameter
9049 * [I] isW : TRUE if input is Unicode
9054 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9056 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9057 BOOL cancel = FALSE;
9059 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9060 hwnd, uMsg, wParam, lParam, isW);
9065 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9072 WNDPROC editProc = infoPtr->EditWndProc;
9073 infoPtr->EditWndProc = 0;
9074 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9075 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9079 if (VK_ESCAPE == (INT)wParam)
9084 else if (VK_RETURN == (INT)wParam)
9088 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9092 if (infoPtr->hwndEdit)
9094 LPWSTR buffer = NULL;
9096 infoPtr->hwndEdit = 0;
9099 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9103 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9105 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9106 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9110 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9112 if (buffer) COMCTL32_Free(buffer);
9116 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9122 * Subclassed edit control Unicode windproc function
9125 * [I] hwnd : the edit window handle
9126 * [I] uMsg : the message that is to be processed
9127 * [I] wParam : first message parameter
9128 * [I] lParam : second message parameter
9132 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9134 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9139 * Subclassed edit control ANSI windproc function
9142 * [I] hwnd : the edit window handle
9143 * [I] uMsg : the message that is to be processed
9144 * [I] wParam : first message parameter
9145 * [I] lParam : second message parameter
9149 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9151 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9156 * Creates a subclassed edit cotrol
9159 * [I] infoPtr : valid pointer to the listview structure
9160 * [I] text : initial text for the edit
9161 * [I] style : the window style
9162 * [I] isW : TRUE if input is Unicode
9166 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9167 INT x, INT y, INT width, INT height, BOOL isW)
9169 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9174 TEXTMETRICW textMetric;
9175 HINSTANCE hinst = (HINSTANCE)GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9177 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9179 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9180 hdc = GetDC(infoPtr->hwndSelf);
9182 /* Select the font to get appropriate metric dimensions */
9183 if(infoPtr->hFont != 0)
9184 hOldFont = SelectObject(hdc, infoPtr->hFont);
9186 /*Get String Length in pixels */
9187 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9189 /*Add Extra spacing for the next character */
9190 GetTextMetricsW(hdc, &textMetric);
9191 sz.cx += (textMetric.tmMaxCharWidth * 2);
9193 if(infoPtr->hFont != 0)
9194 SelectObject(hdc, hOldFont);
9196 ReleaseDC(infoPtr->hwndSelf, hdc);
9198 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9200 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9202 if (!hedit) return 0;
9204 infoPtr->EditWndProc = (WNDPROC)
9205 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9206 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9208 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);