When removing focus, no need to go through all elements.
[wine.git] / dlls / comctl32 / listview.c
blob1b7ca417414a949f0d1c9668932d9aaf6e3a72d3
1 /*
2 * Listview control
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
24 * NOTES
25 * Listview control implementation.
27 * TODO:
28 * -- Hot item handling.
29 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
30 * -- Support CustonDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs)
32 * Notifications:
33 * LISTVIEW_Notify : most notifications from children (editbox and header)
35 * Data structure:
36 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
38 * Advanced functionality:
39 * LISTVIEW_GetNumberOfWorkAreas : not implemented
40 * LISTVIEW_GetISearchString : not implemented
41 * LISTVIEW_GetBkImage : not implemented
42 * LISTVIEW_SetBkImage : not implemented
43 * LISTVIEW_GetColumnOrderArray : simple hack only
44 * LISTVIEW_SetColumnOrderArray : simple hack only
45 * LISTVIEW_Arrange : empty stub
46 * LISTVIEW_ApproximateViewRect : incomplete
47 * LISTVIEW_Update : not completed
49 * Known differences in message stream from native control (not known if
50 * these differences cause problems):
51 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
52 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
53 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
54 * processing for "USEDOUBLECLICKTIME".
58 #include "config.h"
59 #include "wine/port.h"
61 #include <assert.h>
62 #include <ctype.h>
63 #include <string.h>
64 #include <stdlib.h>
65 #include <stdio.h>
67 #include "winbase.h"
68 #include "winnt.h"
69 #include "heap.h"
70 #include "commctrl.h"
71 #include "comctl32.h"
72 #include "wine/debug.h"
74 WINE_DEFAULT_DEBUG_CHANNEL(listview);
76 /* make sure you set this to 0 for production use! */
77 #define DEBUG_RANGES 1
79 typedef struct tagCOLUMN_INFO
81 RECT rcHeader; /* tracks the header's rectangle */
82 int fmt; /* same as LVCOLUMN.fmt */
83 } COLUMN_INFO;
85 typedef struct tagITEMHDR
87 LPWSTR pszText;
88 INT iImage;
89 } ITEMHDR, *LPITEMHDR;
91 typedef struct tagLISTVIEW_SUBITEM
93 ITEMHDR hdr;
94 INT iSubItem;
95 } LISTVIEW_SUBITEM;
97 typedef struct tagLISTVIEW_ITEM
99 ITEMHDR hdr;
100 UINT state;
101 LPARAM lParam;
102 INT iIndent;
103 } LISTVIEW_ITEM;
105 typedef struct tagRANGE
107 INT lower;
108 INT upper;
109 } RANGE;
111 typedef struct tagRANGES
113 HDPA hdpa;
114 } *RANGES;
116 typedef struct tagITERATOR
118 INT nItem;
119 INT nSpecial;
120 RANGE range;
121 RANGES ranges;
122 INT index;
123 } ITERATOR;
125 typedef struct tagLISTVIEW_INFO
127 HWND hwndSelf;
128 HBRUSH hBkBrush;
129 COLORREF clrBk;
130 COLORREF clrText;
131 COLORREF clrTextBk;
132 COLORREF clrTextBkDefault;
133 HIMAGELIST himlNormal;
134 HIMAGELIST himlSmall;
135 HIMAGELIST himlState;
136 BOOL bLButtonDown;
137 BOOL bRButtonDown;
138 INT nItemHeight;
139 INT nItemWidth;
140 RANGES selectionRanges;
141 INT nSelectionMark;
142 INT nHotItem;
143 SHORT notifyFormat;
144 RECT rcList; /* This rectangle is really the window
145 * client rectangle possibly reduced by the
146 * horizontal scroll bar and/or header - see
147 * LISTVIEW_UpdateSize. This rectangle offset
148 * by the LISTVIEW_GetOrigin value is in
149 * client coordinates */
150 RECT rcView; /* This rectangle contains all items -
151 * contructed in LISTVIEW_AlignTop and
152 * LISTVIEW_AlignLeft */
153 SIZE iconSize;
154 SIZE iconSpacing;
155 SIZE iconStateSize;
156 UINT uCallbackMask;
157 HWND hwndHeader;
158 HFONT hDefaultFont;
159 HCURSOR hHotCursor;
160 HFONT hFont;
161 INT ntmHeight; /* from GetTextMetrics from above font */
162 BOOL bRedraw;
163 BOOL bFocus;
164 INT nFocusedItem;
165 RECT rcFocus;
166 DWORD dwStyle; /* the cached window GWL_STYLE */
167 DWORD dwLvExStyle; /* extended listview style */
168 INT nItemCount; /* the number of items in the list */
169 HDPA hdpaItems; /* array ITEM_INFO pointers */
170 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
171 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
172 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
173 PFNLVCOMPARE pfnCompare;
174 LPARAM lParamSort;
175 HWND hwndEdit;
176 WNDPROC EditWndProc;
177 INT nEditLabelItem;
178 DWORD dwHoverTime;
180 DWORD lastKeyPressTimestamp;
181 WPARAM charCode;
182 INT nSearchParamLength;
183 WCHAR szSearchParam[ MAX_PATH ];
184 BOOL bIsDrawing;
185 } LISTVIEW_INFO;
188 * constants
190 /* How many we debug buffer to allocate */
191 #define DEBUG_BUFFERS 20
192 /* The size of a single debug bbuffer */
193 #define DEBUG_BUFFER_SIZE 256
195 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
196 #define SB_INTERNAL -1
198 /* maximum size of a label */
199 #define DISP_TEXT_SIZE 512
201 /* padding for items in list and small icon display modes */
202 #define WIDTH_PADDING 12
204 /* padding for items in list, report and small icon display modes */
205 #define HEIGHT_PADDING 1
207 /* offset of items in report display mode */
208 #define REPORT_MARGINX 2
210 /* padding for icon in large icon display mode
211 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
212 * that HITTEST will see.
213 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
214 * ICON_TOP_PADDING - sum of the two above.
215 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
216 * LABEL_VERT_PADDING - between bottom of text and end of box
218 * ICON_LR_PADDING - additional width above icon size.
219 * ICON_LR_HALF - half of the above value
221 #define ICON_TOP_PADDING_NOTHITABLE 2
222 #define ICON_TOP_PADDING_HITABLE 2
223 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
224 #define ICON_BOTTOM_PADDING 4
225 #define LABEL_VERT_PADDING 7
226 #define ICON_LR_PADDING 16
227 #define ICON_LR_HALF (ICON_LR_PADDING/2)
229 /* default label width for items in list and small icon display modes */
230 #define DEFAULT_LABEL_WIDTH 40
232 /* default column width for items in list display mode */
233 #define DEFAULT_COLUMN_WIDTH 128
235 /* Size of "line" scroll for V & H scrolls */
236 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
238 /* Padding betwen image and label */
239 #define IMAGE_PADDING 2
241 /* Padding behind the label */
242 #define TRAILING_PADDING 5
244 /* Border for the icon caption */
245 #define CAPTION_BORDER 2
247 /* Standard DrawText flags */
248 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
249 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
250 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
252 /* The time in milisecods to reset the search in the list */
253 #define KEY_DELAY 450
255 /* Dump the LISTVIEW_INFO structure to the debug channel */
256 #define LISTVIEW_DUMP(iP) do { \
257 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
258 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
259 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
260 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
261 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
262 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
263 (iP->bFocus) ? "true" : "false"); \
264 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
265 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
266 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
267 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
268 iP->hwndSelf, \
269 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
270 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
271 } while(0)
275 * forward declarations
277 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
278 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
279 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
280 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
281 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
282 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *);
283 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
284 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
285 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
286 static INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *);
287 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
288 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *, INT);
289 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
290 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
291 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
292 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
293 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, POINT);
294 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
295 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
296 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
297 static void LISTVIEW_UnsupportedStyles(LONG);
298 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
299 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
300 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
301 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
302 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
303 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
304 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
305 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
306 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
307 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
308 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
309 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
311 /******** Text handling functions *************************************/
313 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
314 * text string. The string may be ANSI or Unicode, in which case
315 * the boolean isW tells us the type of the string.
317 * The name of the function tell what type of strings it expects:
318 * W: Unicode, T: ANSI/Unicode - function of isW
321 static inline BOOL is_textW(LPCWSTR text)
323 return text != NULL && text != LPSTR_TEXTCALLBACKW;
326 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
328 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
329 return is_textW(text);
332 static inline int textlenT(LPCWSTR text, BOOL isW)
334 return !is_textT(text, isW) ? 0 :
335 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
338 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
340 if (isDestW)
341 if (isSrcW) lstrcpynW(dest, src, max);
342 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
343 else
344 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
345 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
348 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
350 LPWSTR wstr = (LPWSTR)text;
352 if (!isW && is_textT(text, isW))
354 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
355 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
356 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
358 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
359 return wstr;
362 static inline void textfreeT(LPWSTR wstr, BOOL isW)
364 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
368 * dest is a pointer to a Unicode string
369 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
371 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
373 BOOL bResult = TRUE;
375 if (src == LPSTR_TEXTCALLBACKW)
377 if (is_textW(*dest)) COMCTL32_Free(*dest);
378 *dest = LPSTR_TEXTCALLBACKW;
380 else
382 LPWSTR pszText = textdupTtoW(src, isW);
383 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
384 bResult = Str_SetPtrW(dest, pszText);
385 textfreeT(pszText, isW);
387 return bResult;
391 * compares a Unicode to a Unicode/ANSI text string
393 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
395 if (!aw) return bt ? -1 : 0;
396 if (!bt) return aw ? 1 : 0;
397 if (aw == LPSTR_TEXTCALLBACKW)
398 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
399 if (bt != LPSTR_TEXTCALLBACKW)
401 LPWSTR bw = textdupTtoW(bt, isW);
402 int r = bw ? lstrcmpW(aw, bw) : 1;
403 textfreeT(bw, isW);
404 return r;
407 return 1;
410 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
412 int res;
414 n = min(min(n, strlenW(s1)), strlenW(s2));
415 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
416 return res ? res - sizeof(WCHAR) : res;
419 /******** Debugging functions *****************************************/
421 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
423 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
424 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
427 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
429 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
430 n = min(textlenT(text, isW), n);
431 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
434 static char* debug_getbuf()
436 static int index = 0;
437 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
438 return buffers[index++ % DEBUG_BUFFERS];
441 static inline char* debugrange(const RANGE* lprng)
443 if (lprng)
445 char* buf = debug_getbuf();
446 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
447 return buf;
448 } else return "(null)";
451 static inline char* debugpoint(const POINT* lppt)
453 if (lppt)
455 char* buf = debug_getbuf();
456 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
457 return buf;
458 } else return "(null)";
461 static inline char* debugrect(const RECT* rect)
463 if (rect)
465 char* buf = debug_getbuf();
466 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
467 rect->left, rect->top, rect->right, rect->bottom);
468 return buf;
469 } else return "(null)";
472 static char* debugnmlistview(LPNMLISTVIEW plvnm)
474 if (plvnm)
476 char* buf = debug_getbuf();
477 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
478 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
479 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
480 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
481 return buf;
482 } else return "(null)";
485 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
487 char* buf = debug_getbuf(), *text = buf;
488 int len, size = DEBUG_BUFFER_SIZE;
490 if (lpLVItem == NULL) return "(null)";
491 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
492 if (len == -1) goto end; buf += len; size -= len;
493 if (lpLVItem->mask & LVIF_STATE)
494 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
495 else len = 0;
496 if (len == -1) goto end; buf += len; size -= len;
497 if (lpLVItem->mask & LVIF_TEXT)
498 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
499 else len = 0;
500 if (len == -1) goto end; buf += len; size -= len;
501 if (lpLVItem->mask & LVIF_IMAGE)
502 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
503 else len = 0;
504 if (len == -1) goto end; buf += len; size -= len;
505 if (lpLVItem->mask & LVIF_PARAM)
506 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
507 else len = 0;
508 if (len == -1) goto end; buf += len; size -= len;
509 if (lpLVItem->mask & LVIF_INDENT)
510 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
511 else len = 0;
512 if (len == -1) goto end; buf += len; size -= len;
513 goto undo;
514 end:
515 buf = text + strlen(text);
516 undo:
517 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
518 return text;
521 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
523 char* buf = debug_getbuf(), *text = buf;
524 int len, size = DEBUG_BUFFER_SIZE;
526 if (lpColumn == NULL) return "(null)";
527 len = snprintf(buf, size, "{");
528 if (len == -1) goto end; buf += len; size -= len;
529 if (lpColumn->mask & LVCF_SUBITEM)
530 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
531 else len = 0;
532 if (len == -1) goto end; buf += len; size -= len;
533 if (lpColumn->mask & LVCF_FMT)
534 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
535 else len = 0;
536 if (len == -1) goto end; buf += len; size -= len;
537 if (lpColumn->mask & LVCF_WIDTH)
538 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
539 else len = 0;
540 if (len == -1) goto end; buf += len; size -= len;
541 if (lpColumn->mask & LVCF_TEXT)
542 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
543 else len = 0;
544 if (len == -1) goto end; buf += len; size -= len;
545 if (lpColumn->mask & LVCF_IMAGE)
546 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
547 else len = 0;
548 if (len == -1) goto end; buf += len; size -= len;
549 if (lpColumn->mask & LVCF_ORDER)
550 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
551 else len = 0;
552 if (len == -1) goto end; buf += len; size -= len;
553 goto undo;
554 end:
555 buf = text + strlen(text);
556 undo:
557 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
558 return text;
562 /******** Notification functions i************************************/
564 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
566 LRESULT result;
568 TRACE("(code=%d)\n", code);
570 pnmh->hwndFrom = infoPtr->hwndSelf;
571 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
572 pnmh->code = code;
573 result = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
574 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
576 TRACE(" <= %ld\n", result);
578 return result;
581 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
583 NMHDR nmh;
584 return notify_hdr(infoPtr, code, &nmh);
587 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
589 notify(infoPtr, LVN_ITEMACTIVATE);
592 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
594 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
595 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
598 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
600 NMLISTVIEW nmlv;
602 ZeroMemory(&nmlv, sizeof(nmlv));
603 nmlv.iItem = lvht->iItem;
604 nmlv.iSubItem = lvht->iSubItem;
605 nmlv.ptAction = lvht->pt;
606 return notify_listview(infoPtr, code, &nmlv);
609 static int get_ansi_notification(INT unicodeNotificationCode)
611 switch (unicodeNotificationCode)
613 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
614 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
615 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
616 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
617 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
618 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
620 ERR("unknown notification %x\n", unicodeNotificationCode);
621 assert(FALSE);
625 Send notification. depends on dispinfoW having same
626 structure as dispinfoA.
627 infoPtr : listview struct
628 notificationCode : *Unicode* notification code
629 pdi : dispinfo structure (can be unicode or ansi)
630 isW : TRUE if dispinfo is Unicode
632 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
634 BOOL bResult = FALSE;
635 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
636 INT realNotifCode;
637 INT cchTempBufMax = 0, savCchTextMax = 0;
638 LPWSTR pszTempBuf = NULL, savPszText = NULL;
640 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
642 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
643 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
646 if (convertToAnsi || convertToUnicode)
648 if (notificationCode != LVN_GETDISPINFOW)
650 cchTempBufMax = convertToUnicode ?
651 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
652 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
654 else
656 cchTempBufMax = pdi->item.cchTextMax;
657 *pdi->item.pszText = 0; /* make sure we don't process garbage */
660 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
661 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
662 if (!pszTempBuf) return FALSE;
663 if (convertToUnicode)
664 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
665 pszTempBuf, cchTempBufMax);
666 else
667 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
668 cchTempBufMax, NULL, NULL);
669 savCchTextMax = pdi->item.cchTextMax;
670 savPszText = pdi->item.pszText;
671 pdi->item.pszText = pszTempBuf;
672 pdi->item.cchTextMax = cchTempBufMax;
675 if (infoPtr->notifyFormat == NFR_ANSI)
676 realNotifCode = get_ansi_notification(notificationCode);
677 else
678 realNotifCode = notificationCode;
679 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
680 bResult = notify_hdr(infoPtr, realNotifCode, (LPNMHDR)pdi);
682 if (convertToUnicode || convertToAnsi)
684 if (convertToUnicode) /* note : pointer can be changed by app ! */
685 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
686 savCchTextMax, NULL, NULL);
687 else
688 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
689 savPszText, savCchTextMax);
690 pdi->item.pszText = savPszText; /* restores our buffer */
691 pdi->item.cchTextMax = savCchTextMax;
692 HeapFree(GetProcessHeap(), 0, pszTempBuf);
694 return bResult;
697 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc, LPRECT rcBounds)
699 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
700 lpnmlvcd->nmcd.hdc = hdc;
701 lpnmlvcd->nmcd.rc = *rcBounds;
702 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
703 lpnmlvcd->clrText = infoPtr->clrText;
706 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
708 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
709 return notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
712 /******** Item iterator functions **********************************/
714 static RANGES ranges_create(int count);
715 static void ranges_destroy(RANGES ranges);
716 static BOOL ranges_add(RANGES ranges, RANGE range);
717 static BOOL ranges_del(RANGES ranges, RANGE range);
718 static void ranges_dump(RANGES ranges);
720 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
722 RANGE range = { nItem, nItem + 1 };
724 return ranges_add(ranges, range);
727 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
729 RANGE range = { nItem, nItem + 1 };
731 return ranges_del(ranges, range);
734 /***
735 * ITERATOR DOCUMENTATION
737 * The iterator functions allow for easy, and convenient iteration
738 * over items of iterest in the list. Typically, you create a
739 * iterator, use it, and destroy it, as such:
740 * ITERATOR i;
742 * iterator_xxxitems(&i, ...);
743 * while (iterator_{prev,next}(&i)
745 * //code which uses i.nItem
747 * iterator_destroy(&i);
749 * where xxx is either: framed, or visible.
750 * Note that it is important that the code destroys the iterator
751 * after it's done with it, as the creation of the iterator may
752 * allocate memory, which thus needs to be freed.
754 * You can iterate both forwards, and backwards through the list,
755 * by using iterator_next or iterator_prev respectively.
757 * Lower numbered items are draw on top of higher number items in
758 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
759 * items may overlap). So, to test items, you should use
760 * iterator_next
761 * which lists the items top to bottom (in Z-order).
762 * For drawing items, you should use
763 * iterator_prev
764 * which lists the items bottom to top (in Z-order).
765 * If you keep iterating over the items after the end-of-items
766 * marker (-1) is returned, the iterator will start from the
767 * beginning. Typically, you don't need to test for -1,
768 * because iterator_{next,prev} will return TRUE if more items
769 * are to be iterated over, or FALSE otherwise.
771 * Note: the iterator is defined to be bidirectional. That is,
772 * any number of prev followed by any number of next, or
773 * five versa, should leave the iterator at the same item:
774 * prev * n, next * n = next * n, prev * n
776 * The iterator has a notion of a out-of-order, special item,
777 * which sits at the start of the list. This is used in
778 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
779 * which needs to be first, as it may overlap other items.
781 * The code is a bit messy because we have:
782 * - a special item to deal with
783 * - simple range, or composite range
784 * - empty range.
785 * If find bugs, or want to add features, please make sure you
786 * always check/modify *both* iterator_prev, and iterator_next.
789 /****
790 * This function iterates through the items in increasing order,
791 * but prefixed by the special item, then -1. That is:
792 * special, 1, 2, 3, ..., n, -1.
793 * Each item is listed only once.
795 static inline BOOL iterator_next(ITERATOR* i)
797 if (i->nItem == -1)
799 i->nItem = i->nSpecial;
800 if (i->nItem != -1) return TRUE;
802 if (i->nItem == i->nSpecial)
804 if (i->ranges) i->index = 0;
805 goto pickarange;
808 i->nItem++;
809 testitem:
810 if (i->nItem == i->nSpecial) i->nItem++;
811 if (i->nItem < i->range.upper) return TRUE;
813 pickarange:
814 if (i->ranges)
816 if (i->index < i->ranges->hdpa->nItemCount)
817 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
818 else goto end;
820 else if (i->nItem >= i->range.upper) goto end;
822 i->nItem = i->range.lower;
823 if (i->nItem >= 0) goto testitem;
824 end:
825 i->nItem = -1;
826 return FALSE;
829 /****
830 * This function iterates through the items in decreasing order,
831 * followed by the special item, then -1. That is:
832 * n, n-1, ..., 3, 2, 1, special, -1.
833 * Each item is listed only once.
835 static inline BOOL iterator_prev(ITERATOR* i)
837 BOOL start = FALSE;
839 if (i->nItem == -1)
841 start = TRUE;
842 if (i->ranges) i->index = i->ranges->hdpa->nItemCount;
843 goto pickarange;
845 if (i->nItem == i->nSpecial)
847 i->nItem = -1;
848 return FALSE;
851 testitem:
852 i->nItem--;
853 if (i->nItem == i->nSpecial) i->nItem--;
854 if (i->nItem >= i->range.lower) return TRUE;
856 pickarange:
857 if (i->ranges)
859 if (i->index > 0)
860 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
861 else goto end;
863 else if (!start && i->nItem < i->range.lower) goto end;
865 i->nItem = i->range.upper;
866 if (i->nItem > 0) goto testitem;
867 end:
868 return (i->nItem = i->nSpecial) != -1;
871 static RANGE iterator_range(ITERATOR* i)
873 RANGE range;
875 if (!i->ranges) return i->range;
877 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
878 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->ranges->hdpa->nItemCount - 1)).upper;
879 return range;
882 /***
883 * Releases resources associated with this ierator.
885 static inline void iterator_destroy(ITERATOR* i)
887 if (i->ranges) ranges_destroy(i->ranges);
890 /***
891 * Create an empty iterator.
893 static inline BOOL iterator_empty(ITERATOR* i)
895 ZeroMemory(i, sizeof(*i));
896 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
897 return TRUE;
901 /***
902 * Create an iterator over a bunch of ranges.
903 * Please note that the iterator will take ownership of the ranges,
904 * and will free them upon destruction.
906 static inline BOOL iterator_ranges(ITERATOR* i, RANGES ranges)
908 iterator_empty(i);
909 i->ranges = ranges;
910 return TRUE;
913 /***
914 * Creates an iterator over the items which intersect lprc.
916 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
918 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
919 RECT frame = *lprc, rcItem, rcTemp;
920 POINT Origin;
922 /* in case we fail, we want to return an empty iterator */
923 if (!iterator_empty(i)) return FALSE;
925 LISTVIEW_GetOrigin(infoPtr, &Origin);
927 TRACE("(lprc=%s)\n", debugrect(lprc));
928 OffsetRect(&frame, -Origin.x, -Origin.y);
930 if (uView == LVS_ICON || uView == LVS_SMALLICON)
932 INT nItem;
934 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
936 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
937 if (IntersectRect(&rcTemp, &rcItem, lprc))
938 i->nSpecial = infoPtr->nFocusedItem;
940 if (!(i->ranges = ranges_create(50))) return FALSE;
941 /* to do better here, we need to have PosX, and PosY sorted */
942 TRACE("building icon ranges:\n");
943 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
945 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
946 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
947 rcItem.right = rcItem.left + infoPtr->nItemWidth;
948 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
949 if (IntersectRect(&rcTemp, &rcItem, &frame))
950 ranges_additem(i->ranges, nItem);
952 return TRUE;
954 else if (uView == LVS_REPORT)
956 INT lower, upper;
958 if (frame.left >= infoPtr->nItemWidth) return TRUE;
959 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
961 lower = max(frame.top / infoPtr->nItemHeight, 0);
962 upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1);
963 if (upper < lower) return TRUE;
964 i->range.lower = lower;
965 i->range.upper = upper + 1;
966 TRACE(" report=%s\n", debugrange(&i->range));
968 else
970 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
971 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
972 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
973 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
974 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
975 INT lower = nFirstCol * nPerCol + nFirstRow;
976 RANGE item_range;
977 INT nCol;
979 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
980 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
982 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
984 if (!(i->ranges = ranges_create(nLastCol - nFirstCol + 1))) return FALSE;
985 TRACE("building list ranges:\n");
986 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
988 item_range.lower = nCol * nPerCol + nFirstRow;
989 if(item_range.lower >= infoPtr->nItemCount) break;
990 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
991 TRACE(" list=%s\n", debugrange(&item_range));
992 ranges_add(i->ranges, item_range);
996 return TRUE;
999 /***
1000 * Creates an iterator over the items which intersect the visible region of hdc.
1002 static BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
1004 POINT Origin, Position;
1005 RECT rcItem, rcClip;
1006 INT rgntype;
1008 rgntype = GetClipBox(hdc, &rcClip);
1009 if (rgntype == NULLREGION) return iterator_empty(i);
1010 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1011 if (rgntype == SIMPLEREGION) return TRUE;
1013 /* first deal with the special item */
1014 if (i->nSpecial != -1)
1016 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1017 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1020 /* if we can't deal with the region, we'll just go with the simple range */
1021 LISTVIEW_GetOrigin(infoPtr, &Origin);
1022 TRACE("building visible range:\n");
1023 if (!i->ranges && i->range.lower < i->range.upper)
1025 if (!(i->ranges = ranges_create(50))) return TRUE;
1026 if (!ranges_add(i->ranges, i->range))
1028 ranges_destroy(i->ranges);
1029 i->ranges = 0;
1030 return TRUE;
1034 /* now delete the invisible items from the list */
1035 while(iterator_next(i))
1037 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1038 rcItem.left = Position.x + Origin.x;
1039 rcItem.top = Position.y + Origin.y;
1040 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1041 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1042 if (!RectVisible(hdc, &rcItem))
1043 ranges_delitem(i->ranges, i->nItem);
1045 /* the iterator should restart on the next iterator_next */
1046 TRACE("done\n");
1048 return TRUE;
1051 /******** Misc helper functions ************************************/
1053 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1054 WPARAM wParam, LPARAM lParam, BOOL isW)
1056 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1057 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1060 /******** Internal API functions ************************************/
1062 /* The Invalidate* are macros, so we preserve the caller location */
1063 #define LISTVIEW_InvalidateRect(infoPtr, rect) while(infoPtr->bRedraw) { \
1064 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
1065 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
1066 break;\
1069 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
1070 RECT rcBox; \
1071 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox); \
1072 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
1073 } while (0)
1075 #define LISTVIEW_InvalidateSubItem(infoPtr, nItem, nSubItem) do { \
1076 POINT Origin, Position; \
1077 RECT rcBox; \
1078 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT); \
1079 LISTVIEW_GetOrigin(infoPtr, &Origin); \
1080 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position); \
1081 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox); \
1082 rcBox.top = 0; \
1083 rcBox.bottom = infoPtr->nItemHeight; \
1084 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y); \
1085 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
1086 } while (0)
1088 #define LISTVIEW_InvalidateList(infoPtr)\
1089 LISTVIEW_InvalidateRect(infoPtr, NULL)
1092 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1094 COLUMN_INFO *columnInfo;
1095 assert (nSubItem >= 0 && nSubItem < infoPtr->hdpaColumns->nItemCount);
1096 columnInfo = (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1097 assert (columnInfo);
1098 return columnInfo;
1101 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1103 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1106 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1108 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1111 /***
1112 * DESCRIPTION:
1113 * Retrieves the number of items that can fit vertically in the client area.
1115 * PARAMETER(S):
1116 * [I] infoPtr : valid pointer to the listview structure
1118 * RETURN:
1119 * Number of items per row.
1121 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1123 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1125 return max(nListWidth/infoPtr->nItemWidth, 1);
1128 /***
1129 * DESCRIPTION:
1130 * Retrieves the number of items that can fit horizontally in the client
1131 * area.
1133 * PARAMETER(S):
1134 * [I] infoPtr : valid pointer to the listview structure
1136 * RETURN:
1137 * Number of items per column.
1139 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1141 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1143 return max(nListHeight / infoPtr->nItemHeight, 1);
1147 /*************************************************************************
1148 * LISTVIEW_ProcessLetterKeys
1150 * Processes keyboard messages generated by pressing the letter keys
1151 * on the keyboard.
1152 * What this does is perform a case insensitive search from the
1153 * current position with the following quirks:
1154 * - If two chars or more are pressed in quick succession we search
1155 * for the corresponding string (e.g. 'abc').
1156 * - If there is a delay we wipe away the current search string and
1157 * restart with just that char.
1158 * - If the user keeps pressing the same character, whether slowly or
1159 * fast, so that the search string is entirely composed of this
1160 * character ('aaaaa' for instance), then we search for first item
1161 * that starting with that character.
1162 * - If the user types the above character in quick succession, then
1163 * we must also search for the corresponding string ('aaaaa'), and
1164 * go to that string if there is a match.
1166 * PARAMETERS
1167 * [I] hwnd : handle to the window
1168 * [I] charCode : the character code, the actual character
1169 * [I] keyData : key data
1171 * RETURNS
1173 * Zero.
1175 * BUGS
1177 * - The current implementation has a list of characters it will
1178 * accept and it ignores averything else. In particular it will
1179 * ignore accentuated characters which seems to match what
1180 * Windows does. But I'm not sure it makes sense to follow
1181 * Windows there.
1182 * - We don't sound a beep when the search fails.
1184 * SEE ALSO
1186 * TREEVIEW_ProcessLetterKeys
1188 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1190 INT nItem;
1191 INT endidx,idx;
1192 LVITEMW item;
1193 WCHAR buffer[MAX_PATH];
1194 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1196 /* simple parameter checking */
1197 if (!charCode || !keyData) return 0;
1199 /* only allow the valid WM_CHARs through */
1200 if (!isalnum(charCode) &&
1201 charCode != '.' && charCode != '`' && charCode != '!' &&
1202 charCode != '@' && charCode != '#' && charCode != '$' &&
1203 charCode != '%' && charCode != '^' && charCode != '&' &&
1204 charCode != '*' && charCode != '(' && charCode != ')' &&
1205 charCode != '-' && charCode != '_' && charCode != '+' &&
1206 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1207 charCode != '}' && charCode != '[' && charCode != '{' &&
1208 charCode != '/' && charCode != '?' && charCode != '>' &&
1209 charCode != '<' && charCode != ',' && charCode != '~')
1210 return 0;
1212 /* if there's one item or less, there is no where to go */
1213 if (infoPtr->nItemCount <= 1) return 0;
1215 /* update the search parameters */
1216 infoPtr->lastKeyPressTimestamp = GetTickCount();
1217 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1218 if (infoPtr->nSearchParamLength < MAX_PATH)
1219 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1220 if (infoPtr->charCode != charCode)
1221 infoPtr->charCode = charCode = 0;
1222 } else {
1223 infoPtr->charCode=charCode;
1224 infoPtr->szSearchParam[0]=charCode;
1225 infoPtr->nSearchParamLength=1;
1226 /* Redundant with the 1 char string */
1227 charCode=0;
1230 /* and search from the current position */
1231 nItem=-1;
1232 if (infoPtr->nFocusedItem >= 0) {
1233 endidx=infoPtr->nFocusedItem;
1234 idx=endidx;
1235 /* if looking for single character match,
1236 * then we must always move forward
1238 if (infoPtr->nSearchParamLength == 1)
1239 idx++;
1240 } else {
1241 endidx=infoPtr->nItemCount;
1242 idx=0;
1244 do {
1245 if (idx == infoPtr->nItemCount) {
1246 if (endidx == infoPtr->nItemCount || endidx == 0)
1247 break;
1248 idx=0;
1251 /* get item */
1252 item.mask = LVIF_TEXT;
1253 item.iItem = idx;
1254 item.iSubItem = 0;
1255 item.pszText = buffer;
1256 item.cchTextMax = MAX_PATH;
1257 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1259 /* check for a match */
1260 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1261 nItem=idx;
1262 break;
1263 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1264 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1265 /* This would work but we must keep looking for a longer match */
1266 nItem=idx;
1268 idx++;
1269 } while (idx != endidx);
1271 if (nItem != -1)
1272 LISTVIEW_KeySelection(infoPtr, nItem);
1274 return 0;
1277 /*************************************************************************
1278 * LISTVIEW_UpdateHeaderSize [Internal]
1280 * Function to resize the header control
1282 * PARAMS
1283 * [I] hwnd : handle to a window
1284 * [I] nNewScrollPos : scroll pos to set
1286 * RETURNS
1287 * None.
1289 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1291 RECT winRect;
1292 POINT point[2];
1294 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1296 GetWindowRect(infoPtr->hwndHeader, &winRect);
1297 point[0].x = winRect.left;
1298 point[0].y = winRect.top;
1299 point[1].x = winRect.right;
1300 point[1].y = winRect.bottom;
1302 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1303 point[0].x = -nNewScrollPos;
1304 point[1].x += nNewScrollPos;
1306 SetWindowPos(infoPtr->hwndHeader,0,
1307 point[0].x,point[0].y,point[1].x,point[1].y,
1308 SWP_NOZORDER | SWP_NOACTIVATE);
1311 /***
1312 * DESCRIPTION:
1313 * Update the scrollbars. This functions should be called whenever
1314 * the content, size or view changes.
1316 * PARAMETER(S):
1317 * [I] infoPtr : valid pointer to the listview structure
1319 * RETURN:
1320 * None
1322 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1324 LONG lStyle = infoPtr->dwStyle;
1325 UINT uView = lStyle & LVS_TYPEMASK;
1326 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1327 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1328 SCROLLINFO scrollInfo;
1330 if (lStyle & LVS_NOSCROLL) return;
1332 scrollInfo.cbSize = sizeof(SCROLLINFO);
1334 if (uView == LVS_LIST)
1336 /* update horizontal scrollbar */
1337 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1338 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1340 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1341 infoPtr->nItemCount, nCountPerColumn, nCountPerRow);
1343 scrollInfo.nMin = 0;
1344 scrollInfo.nMax = infoPtr->nItemCount / nCountPerColumn;
1345 if((infoPtr->nItemCount % nCountPerColumn) == 0)
1346 scrollInfo.nMax--;
1347 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1348 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1349 scrollInfo.nPage = nCountPerRow;
1350 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1351 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1352 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1354 else if (uView == LVS_REPORT)
1356 BOOL test;
1358 /* update vertical scrollbar */
1359 scrollInfo.nMin = 0;
1360 scrollInfo.nMax = infoPtr->nItemCount - 1;
1361 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1362 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1363 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1364 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1365 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1366 scrollInfo.nMax, scrollInfo.nPage, test);
1367 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1368 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1370 /* update horizontal scrollbar */
1371 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1372 scrollInfo.fMask = SIF_POS;
1373 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1374 || infoPtr->nItemCount == 0)
1376 scrollInfo.nPos = 0;
1378 scrollInfo.nMin = 0;
1379 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1380 scrollInfo.nPage = nListWidth;
1381 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1382 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1383 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1384 scrollInfo.nMax, scrollInfo.nPage, test);
1385 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1386 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1388 /* Update the Header Control */
1389 scrollInfo.fMask = SIF_POS;
1390 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1391 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1394 else
1396 RECT rcView;
1398 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1400 INT nViewWidth = rcView.right - rcView.left;
1401 INT nViewHeight = rcView.bottom - rcView.top;
1403 /* Update Horizontal Scrollbar */
1404 scrollInfo.fMask = SIF_POS;
1405 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1406 || infoPtr->nItemCount == 0)
1408 scrollInfo.nPos = 0;
1410 scrollInfo.nMin = 0;
1411 scrollInfo.nMax = max(nViewWidth, 0)-1;
1412 scrollInfo.nPage = nListWidth;
1413 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1414 TRACE("LVS_ICON/SMALLICON Horz.\n");
1415 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1417 /* Update Vertical Scrollbar */
1418 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1419 scrollInfo.fMask = SIF_POS;
1420 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1421 || infoPtr->nItemCount == 0)
1423 scrollInfo.nPos = 0;
1425 scrollInfo.nMin = 0;
1426 scrollInfo.nMax = max(nViewHeight,0)-1;
1427 scrollInfo.nPage = nListHeight;
1428 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1429 TRACE("LVS_ICON/SMALLICON Vert.\n");
1430 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1436 /***
1437 * DESCRIPTION:
1438 * Shows/hides the focus rectangle.
1440 * PARAMETER(S):
1441 * [I] infoPtr : valid pointer to the listview structure
1442 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1444 * RETURN:
1445 * None
1447 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1449 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1450 HDC hdc;
1452 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1454 if (infoPtr->nFocusedItem < 0) return;
1456 /* we need some gymnastics in ICON mode to handle large items */
1457 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1459 RECT rcBox;
1461 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1462 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1464 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1465 return;
1469 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1471 /* for some reason, owner draw should work only in report mode */
1472 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1474 DRAWITEMSTRUCT dis;
1475 LVITEMW item;
1477 item.iItem = infoPtr->nFocusedItem;
1478 item.iSubItem = 0;
1479 item.mask = LVIF_PARAM;
1480 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1482 ZeroMemory(&dis, sizeof(dis));
1483 dis.CtlType = ODT_LISTVIEW;
1484 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1485 dis.itemID = item.iItem;
1486 dis.itemAction = ODA_FOCUS;
1487 if (fShow) dis.itemState |= ODS_FOCUS;
1488 dis.hwndItem = infoPtr->hwndSelf;
1489 dis.hDC = hdc;
1490 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1491 dis.itemData = item.lParam;
1493 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1495 else
1497 DrawFocusRect(hdc, &infoPtr->rcFocus);
1499 done:
1500 ReleaseDC(infoPtr->hwndSelf, hdc);
1503 /***
1504 * Invalidates all visible selected items.
1506 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1508 ITERATOR i;
1510 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1511 while(iterator_next(&i))
1513 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1514 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1516 iterator_destroy(&i);
1520 /***
1521 * DESCRIPTION:
1522 * Prints a message for unsupported window styles.
1523 * A kind of TODO list for window styles.
1525 * PARAMETER(S):
1526 * [I] lStyle : window style
1528 * RETURN:
1529 * None
1531 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1533 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1534 FIXME(" LVS_NOSCROLL\n");
1536 if (lStyle & LVS_NOLABELWRAP)
1537 FIXME(" LVS_NOLABELWRAP\n");
1539 if (lStyle & LVS_SORTASCENDING)
1540 FIXME(" LVS_SORTASCENDING\n");
1542 if (lStyle & LVS_SORTDESCENDING)
1543 FIXME(" LVS_SORTDESCENDING\n");
1547 /***
1548 * DESCRIPTION: [INTERNAL]
1549 * Computes an item's (left,top) corner, relative to rcView.
1550 * That is, the position has NOT been made relative to the Origin.
1551 * This is deliberate, to avoid computing the Origin over, and
1552 * over again, when this function is call in a loop. Instead,
1553 * one ca factor the computation of the Origin before the loop,
1554 * and offset the value retured by this function, on every iteration.
1556 * PARAMETER(S):
1557 * [I] infoPtr : valid pointer to the listview structure
1558 * [I] nItem : item number
1559 * [O] lpptOrig : item top, left corner
1561 * RETURN:
1562 * None.
1564 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1566 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1568 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1570 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1572 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1573 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1575 else if (uView == LVS_LIST)
1577 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1578 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1579 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1581 else /* LVS_REPORT */
1583 lpptPosition->x = REPORT_MARGINX;
1584 lpptPosition->y = nItem * infoPtr->nItemHeight;
1588 /***
1589 * DESCRIPTION: [INTERNAL]
1590 * Compute the rectangles of an item. This is to localize all
1591 * the computations in one place. If you are not interested in some
1592 * of these values, simply pass in a NULL -- the fucntion is smart
1593 * enough to compute only what's necessary. The function computes
1594 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1595 * one, the BOX rectangle. This rectangle is very cheap to compute,
1596 * and is guaranteed to contain all the other rectangles. Computing
1597 * the ICON rect is also cheap, but all the others are potentaily
1598 * expensive. This gives an easy and effective optimization when
1599 * searching (like point inclusion, or rectangle intersection):
1600 * first test against the BOX, and if TRUE, test agains the desired
1601 * rectangle.
1602 * If the function does not have all the necessary information
1603 * to computed the requested rectangles, will crash with a
1604 * failed assertion. This is done so we catch all programming
1605 * errors, given that the function is called only from our code.
1607 * We have the following 'special' meanings for a few fields:
1608 * * If LVIS_FOCUSED is set, we assume the item has the focus
1609 * This is important in ICON mode, where it might get a larger
1610 * then usual rectange
1612 * Please note that subitem support works only in REPORT mode.
1614 * PARAMETER(S):
1615 * [I] infoPtr : valid pointer to the listview structure
1616 * [I] lpLVItem : item to compute the measures for
1617 * [O] lprcBox : ptr to Box rectangle
1618 * The internal LVIR_BOX rectangle
1619 * [0] lprcState : ptr to State icon rectangle
1620 * The internal LVIR_STATE rectangle
1621 * [O] lprcIcon : ptr to Icon rectangle
1622 * Same as LVM_GETITEMRECT with LVIR_ICON
1623 * [O] lprcLabel : ptr to Label rectangle
1624 * Same as LVM_GETITEMRECT with LVIR_LABEL
1626 * RETURN:
1627 * None.
1629 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem,
1630 LPRECT lprcBox, LPRECT lprcState,
1631 LPRECT lprcIcon, LPRECT lprcLabel)
1633 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1634 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1635 RECT Box, State, Icon, Label;
1636 COLUMN_INFO *lpColumnInfo = NULL;
1638 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1640 /* Be smart and try to figure out the minimum we have to do */
1641 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1642 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1644 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1645 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1647 if (lprcLabel) doLabel = TRUE;
1648 if (doLabel || lprcIcon) doIcon = TRUE;
1649 if (doIcon || lprcState) doState = TRUE;
1651 /************************************************************/
1652 /* compute the box rectangle (it should be cheap to do) */
1653 /************************************************************/
1654 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1655 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1657 if (lpLVItem->iSubItem)
1659 Box = lpColumnInfo->rcHeader;
1661 else
1663 Box.left = 0;
1664 Box.right = infoPtr->nItemWidth;
1666 Box.top = 0;
1667 Box.bottom = infoPtr->nItemHeight;
1669 /************************************************************/
1670 /* compute STATEICON bounding box */
1671 /************************************************************/
1672 if (doState)
1674 if (uView == LVS_ICON)
1676 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1677 if (infoPtr->himlNormal)
1678 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1679 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1681 else
1683 /* we need the ident in report mode, if we don't have it, we fail */
1684 State.left = Box.left;
1685 if (uView == LVS_REPORT)
1687 State.left += REPORT_MARGINX;
1688 if (lpLVItem->iSubItem == 0)
1690 assert(lpLVItem->mask & LVIF_INDENT);
1691 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1694 State.top = Box.top;
1696 State.right = State.left;
1697 State.bottom = State.top;
1698 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1700 State.right += infoPtr->iconStateSize.cx;
1701 State.bottom += infoPtr->iconStateSize.cy;
1703 if (lprcState) *lprcState = State;
1704 TRACE(" - state=%s\n", debugrect(&State));
1707 /************************************************************/
1708 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1709 /************************************************************/
1710 if (doIcon)
1712 if (uView == LVS_ICON)
1714 Icon.left = Box.left;
1715 if (infoPtr->himlNormal)
1716 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1717 Icon.top = Box.top + ICON_TOP_PADDING;
1718 Icon.right = Icon.left;
1719 Icon.bottom = Icon.top;
1720 if (infoPtr->himlNormal)
1722 Icon.right += infoPtr->iconSize.cx;
1723 Icon.bottom += infoPtr->iconSize.cy;
1726 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1728 Icon.left = State.right;
1729 if (!IsRectEmpty(&State)) Icon.left += IMAGE_PADDING;
1730 Icon.top = Box.top;
1731 Icon.right = Icon.left;
1732 if (infoPtr->himlSmall && (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE)))
1733 Icon.right += infoPtr->iconSize.cx;
1734 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1736 if(lprcIcon) *lprcIcon = Icon;
1737 TRACE(" - icon=%s\n", debugrect(&Icon));
1740 /************************************************************/
1741 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1742 /************************************************************/
1743 if (doLabel)
1745 SIZE labelSize = { 0, 0 };
1747 /* calculate how far to the right can the label strech */
1748 Label.right = Box.right;
1749 if (uView == LVS_REPORT)
1751 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1752 Label.right -= REPORT_MARGINX;
1755 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1757 labelSize.cx = infoPtr->nItemWidth;
1758 labelSize.cy = infoPtr->nItemHeight;
1759 goto calc_label;
1762 /* we need the text in non owner draw mode */
1763 assert(lpLVItem->mask & LVIF_TEXT);
1764 if (is_textT(lpLVItem->pszText, TRUE))
1766 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1767 HDC hdc = GetDC(infoPtr->hwndSelf);
1768 HFONT hOldFont = SelectObject(hdc, hFont);
1769 UINT uFormat;
1770 RECT rcText;
1772 /* compute rough rectangle where the label will go */
1773 SetRectEmpty(&rcText);
1774 rcText.right = infoPtr->nItemWidth - TRAILING_PADDING;
1775 rcText.bottom = infoPtr->nItemHeight;
1776 if (uView == LVS_ICON)
1777 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1779 /* now figure out the flags */
1780 if (uView == LVS_ICON)
1781 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1782 else
1783 uFormat = LV_SL_DT_FLAGS;
1785 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1787 labelSize.cx = min(rcText.right - rcText.left + TRAILING_PADDING, infoPtr->nItemWidth);
1788 labelSize.cy = rcText.bottom - rcText.top;
1790 SelectObject(hdc, hOldFont);
1791 ReleaseDC(infoPtr->hwndSelf, hdc);
1794 calc_label:
1795 if (uView == LVS_ICON)
1797 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1798 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1799 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1800 Label.right = Label.left + labelSize.cx;
1801 Label.bottom = Label.top + infoPtr->nItemHeight;
1802 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1804 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1805 labelSize.cy /= infoPtr->ntmHeight;
1806 labelSize.cy = max(labelSize.cy, 1);
1807 labelSize.cy *= infoPtr->ntmHeight;
1809 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1811 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1813 Label.left = Icon.right;
1814 if (!IsRectEmpty(&Icon) || !IsRectEmpty(&State)) Label.left += IMAGE_PADDING;
1815 Label.top = Box.top;
1816 Label.right = min(Label.left + labelSize.cx, Label.right);
1817 Label.bottom = Label.top + infoPtr->nItemHeight;
1820 if (lprcLabel) *lprcLabel = Label;
1821 TRACE(" - label=%s\n", debugrect(&Label));
1824 /* Fix the Box if necessary */
1825 if (lprcBox)
1827 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1828 else *lprcBox = Box;
1830 TRACE(" - box=%s\n", debugrect(&Box));
1833 /***
1834 * DESCRIPTION: [INTERNAL]
1836 * PARAMETER(S):
1837 * [I] infoPtr : valid pointer to the listview structure
1838 * [I] nItem : item number
1839 * [O] lprcBox : ptr to Box rectangle
1841 * RETURN:
1842 * None.
1844 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
1846 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1847 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1848 POINT Position, Origin;
1849 LVITEMW lvItem;
1851 LISTVIEW_GetOrigin(infoPtr, &Origin);
1852 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1854 /* Be smart and try to figure out the minimum we have to do */
1855 lvItem.mask = 0;
1856 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1857 lvItem.mask |= LVIF_TEXT;
1858 lvItem.iItem = nItem;
1859 lvItem.iSubItem = 0;
1860 lvItem.pszText = szDispText;
1861 lvItem.cchTextMax = DISP_TEXT_SIZE;
1862 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
1863 if (uView == LVS_ICON)
1865 lvItem.mask |= LVIF_STATE;
1866 lvItem.stateMask = LVIS_FOCUSED;
1867 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
1869 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
1871 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
1874 /***
1875 * DESCRIPTION:
1876 * Aligns the items with the top edge of the window.
1878 * PARAMETER(S):
1879 * [I] infoPtr : valid pointer to the listview structure
1881 * RETURN:
1882 * None
1884 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1886 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1887 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1888 POINT ptItem;
1889 RECT rcView;
1890 INT i, off_x=0, off_y=0;
1892 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1894 /* Since SetItemPosition uses upper-left of icon, and for
1895 style=LVS_ICON the icon is not left adjusted, get the offset */
1896 if (uView == LVS_ICON)
1898 off_y = ICON_TOP_PADDING;
1899 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1901 ptItem.x = off_x;
1902 ptItem.y = off_y;
1903 ZeroMemory(&rcView, sizeof(RECT));
1904 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1905 off_x, off_y,
1906 infoPtr->rcList.left, infoPtr->rcList.right);
1908 if (nListWidth > infoPtr->nItemWidth)
1910 for (i = 0; i < infoPtr->nItemCount; i++)
1912 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1914 ptItem.x = off_x;
1915 ptItem.y += infoPtr->nItemHeight;
1918 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1919 ptItem.x += infoPtr->nItemWidth;
1920 rcView.right = max(rcView.right, ptItem.x);
1923 rcView.right -= off_x;
1924 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1926 else
1928 for (i = 0; i < infoPtr->nItemCount; i++)
1930 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1931 ptItem.y += infoPtr->nItemHeight;
1934 rcView.right = infoPtr->nItemWidth;
1935 rcView.bottom = ptItem.y-off_y;
1938 infoPtr->rcView = rcView;
1942 /***
1943 * DESCRIPTION:
1944 * Aligns the items with the left edge of the window.
1946 * PARAMETER(S):
1947 * [I] infoPtr : valid pointer to the listview structure
1949 * RETURN:
1950 * None
1952 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1954 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1955 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1956 POINT ptItem;
1957 RECT rcView;
1958 INT i, off_x=0, off_y=0;
1960 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1962 /* Since SetItemPosition uses upper-left of icon, and for
1963 style=LVS_ICON the icon is not left adjusted, get the offset */
1964 if (uView == LVS_ICON)
1966 off_y = ICON_TOP_PADDING;
1967 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1969 ptItem.x = off_x;
1970 ptItem.y = off_y;
1971 ZeroMemory(&rcView, sizeof(RECT));
1972 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1974 if (nListHeight > infoPtr->nItemHeight)
1976 for (i = 0; i < infoPtr->nItemCount; i++)
1978 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1980 ptItem.y = off_y;
1981 ptItem.x += infoPtr->nItemWidth;
1984 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1985 ptItem.y += infoPtr->nItemHeight;
1986 rcView.bottom = max(rcView.bottom, ptItem.y);
1989 rcView.right = ptItem.x + infoPtr->nItemWidth;
1991 else
1993 for (i = 0; i < infoPtr->nItemCount; i++)
1995 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1996 ptItem.x += infoPtr->nItemWidth;
1999 rcView.bottom = infoPtr->nItemHeight;
2000 rcView.right = ptItem.x;
2003 infoPtr->rcView = rcView;
2008 /***
2009 * DESCRIPTION:
2010 * Retrieves the bounding rectangle of all the items.
2012 * PARAMETER(S):
2013 * [I] infoPtr : valid pointer to the listview structure
2014 * [O] lprcView : bounding rectangle
2016 * RETURN:
2017 * SUCCESS : TRUE
2018 * FAILURE : FALSE
2020 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2022 POINT ptOrigin;
2024 TRACE("(lprcView=%p)\n", lprcView);
2026 if (!lprcView) return FALSE;
2028 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2030 *lprcView = infoPtr->rcView;
2031 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2033 TRACE("lprcView=%s\n", debugrect(lprcView));
2035 return TRUE;
2038 /***
2039 * DESCRIPTION:
2040 * Retrieves the subitem pointer associated with the subitem index.
2042 * PARAMETER(S):
2043 * [I] hdpaSubItems : DPA handle for a specific item
2044 * [I] nSubItem : index of subitem
2046 * RETURN:
2047 * SUCCESS : subitem pointer
2048 * FAILURE : NULL
2050 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2052 LISTVIEW_SUBITEM *lpSubItem;
2053 INT i;
2055 /* we should binary search here if need be */
2056 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2058 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
2059 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
2060 return lpSubItem;
2063 return NULL;
2067 /***
2068 * DESCRIPTION:
2069 * Calculates the width of a specific item.
2071 * PARAMETER(S):
2072 * [I] infoPtr : valid pointer to the listview structure
2073 * [I] nItem : item to calculate width, or -1 for max of all
2075 * RETURN:
2076 * Returns the width of an item width an item.
2078 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr, INT nItem)
2080 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2081 INT nItemWidth = 0, i;
2083 if (uView == LVS_ICON)
2084 nItemWidth = infoPtr->iconSpacing.cx;
2085 else if (uView == LVS_REPORT)
2087 RECT rcHeaderItem;
2089 /* calculate width of header */
2090 for (i = 0; i < infoPtr->hdpaColumns->nItemCount; i++)
2092 LISTVIEW_GetHeaderRect(infoPtr, i, &rcHeaderItem);
2093 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
2096 else
2098 INT nLabelWidth;
2100 if (infoPtr->nItemCount == 0) return DEFAULT_COLUMN_WIDTH;
2102 /* get width of string */
2103 if (nItem == -1)
2105 for (i = 0; i < infoPtr->nItemCount; i++)
2107 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
2108 nItemWidth = max(nItemWidth, nLabelWidth);
2111 else
2112 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
2113 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
2114 nItemWidth += WIDTH_PADDING;
2115 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2116 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2117 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
2120 return max(nItemWidth, 1);
2123 /***
2124 * DESCRIPTION:
2125 * Calculates the max width of any item in the list.
2127 * PARAMETER(S):
2128 * [I] infoPtr : valid pointer to the listview structure
2130 * RETURN:
2131 * Returns item width.
2133 static inline INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *infoPtr)
2135 return LISTVIEW_CalculateItemWidth(infoPtr, -1);
2138 /***
2139 * DESCRIPTION:
2140 * Retrieves and saves important text metrics info for the current
2141 * Listview font.
2143 * PARAMETER(S):
2144 * [I] infoPtr : valid pointer to the listview structure
2147 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2149 HDC hdc = GetDC(infoPtr->hwndSelf);
2150 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2151 HFONT hOldFont = SelectObject(hdc, hFont);
2152 TEXTMETRICW tm;
2154 if (GetTextMetricsW(hdc, &tm))
2155 infoPtr->ntmHeight = tm.tmHeight;
2156 SelectObject(hdc, hOldFont);
2157 ReleaseDC(infoPtr->hwndSelf, hdc);
2159 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2163 /***
2164 * DESCRIPTION:
2165 * Calculates the height of an item.
2167 * PARAMETER(S):
2168 * [I] infoPtr : valid pointer to the listview structure
2170 * RETURN:
2171 * Returns item height.
2173 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *infoPtr)
2175 INT nItemHeight;
2177 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
2178 nItemHeight = infoPtr->iconSpacing.cy;
2179 else
2181 nItemHeight = infoPtr->ntmHeight;
2182 if (infoPtr->himlState)
2183 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2184 if (infoPtr->himlSmall)
2185 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2186 if (infoPtr->himlState || infoPtr->himlSmall)
2187 nItemHeight += HEIGHT_PADDING;
2189 return nItemHeight;
2192 /***
2193 * DESCRIPTION:
2194 * A compare function for ranges
2196 * PARAMETER(S)
2197 * [I] range1 : pointer to range 1;
2198 * [I] range2 : pointer to range 2;
2199 * [I] flags : flags
2201 * RETURNS:
2202 * > 0 : if range 1 > range 2
2203 * < 0 : if range 2 > range 1
2204 * = 0 : if range intersects range 2
2206 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2208 INT cmp;
2210 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2211 cmp = -1;
2212 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2213 cmp = 1;
2214 else
2215 cmp = 0;
2217 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2219 return cmp;
2222 #if DEBUG_RANGES
2223 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2224 #else
2225 #define ranges_check(ranges, desc) do { } while(0)
2226 #endif
2228 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2230 INT i;
2231 RANGE *prev, *curr;
2233 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2234 assert (ranges);
2235 assert (ranges->hdpa->nItemCount >= 0);
2236 ranges_dump(ranges);
2237 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2238 if (ranges->hdpa->nItemCount > 0)
2239 assert (prev->lower >= 0 && prev->lower < prev->upper);
2240 for (i = 1; i < ranges->hdpa->nItemCount; i++)
2242 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2243 assert (prev->upper <= curr->lower);
2244 assert (curr->lower < curr->upper);
2245 prev = curr;
2247 TRACE("--- Done checking---\n");
2250 static RANGES ranges_create(int count)
2252 RANGES ranges = (RANGES)COMCTL32_Alloc(sizeof(struct tagRANGES));
2253 if (!ranges) return NULL;
2254 ranges->hdpa = DPA_Create(count);
2255 if (ranges->hdpa) return ranges;
2256 COMCTL32_Free(ranges);
2257 return NULL;
2260 static void ranges_destroy(RANGES ranges)
2262 if (ranges && ranges->hdpa)
2264 INT i;
2266 for(i = 0; i < ranges->hdpa->nItemCount; i++)
2267 COMCTL32_Free(DPA_GetPtr(ranges->hdpa, i));
2268 DPA_Destroy(ranges->hdpa);
2269 ranges->hdpa = NULL;
2271 COMCTL32_Free(ranges);
2274 static RANGES ranges_clone(RANGES ranges)
2276 RANGES clone;
2277 INT i;
2279 if (!(clone = ranges_create(ranges->hdpa->nItemCount))) goto fail;
2281 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2283 RANGE *newrng = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2284 if (!newrng) goto fail;
2285 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2286 DPA_InsertPtr(clone->hdpa, i, newrng);
2288 return clone;
2290 fail:
2291 TRACE ("clone failed\n");
2292 if (clone) ranges_destroy(clone);
2293 return NULL;
2296 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2298 INT i;
2300 for (i = 0; i < sub->hdpa->nItemCount; i++)
2301 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2303 return ranges;
2306 static void ranges_dump(RANGES ranges)
2308 INT i;
2310 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2311 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2314 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2316 RANGE srchrng = { nItem, nItem + 1 };
2318 TRACE("(nItem=%d)\n", nItem);
2319 ranges_check(ranges, "before contain");
2320 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2323 static INT ranges_itemcount(RANGES ranges)
2325 INT i, count = 0;
2327 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2329 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2330 count += sel->upper - sel->lower;
2333 return count;
2336 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2338 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2339 INT index;
2341 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2342 if (index == -1) return TRUE;
2344 for (; index < ranges->hdpa->nItemCount; index++)
2346 chkrng = DPA_GetPtr(ranges->hdpa, index);
2347 if (chkrng->lower >= nItem)
2348 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2349 if (chkrng->upper > nItem)
2350 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2352 return TRUE;
2355 static BOOL ranges_add(RANGES ranges, RANGE range)
2357 RANGE srchrgn;
2358 INT index;
2360 TRACE("(%s)\n", debugrange(&range));
2361 ranges_check(ranges, "before add");
2363 /* try find overlapping regions first */
2364 srchrgn.lower = range.lower - 1;
2365 srchrgn.upper = range.upper + 1;
2366 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2368 if (index == -1)
2370 RANGE *newrgn;
2372 TRACE("Adding new range\n");
2374 /* create the brand new range to insert */
2375 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2376 if(!newrgn) goto fail;
2377 *newrgn = range;
2379 /* figure out where to insert it */
2380 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2381 TRACE("index=%d\n", index);
2382 if (index == -1) index = 0;
2384 /* and get it over with */
2385 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2387 COMCTL32_Free(newrgn);
2388 goto fail;
2391 else
2393 RANGE *chkrgn, *mrgrgn;
2394 INT fromindex, mergeindex;
2396 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2397 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2399 chkrgn->lower = min(range.lower, chkrgn->lower);
2400 chkrgn->upper = max(range.upper, chkrgn->upper);
2402 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2404 /* merge now common anges */
2405 fromindex = 0;
2406 srchrgn.lower = chkrgn->lower - 1;
2407 srchrgn.upper = chkrgn->upper + 1;
2411 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2412 if (mergeindex == -1) break;
2413 if (mergeindex == index)
2415 fromindex = index + 1;
2416 continue;
2419 TRACE("Merge with index %i\n", mergeindex);
2421 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2422 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2423 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2424 COMCTL32_Free(mrgrgn);
2425 DPA_DeletePtr(ranges->hdpa, mergeindex);
2426 if (mergeindex < index) index --;
2427 } while(1);
2430 ranges_check(ranges, "after add");
2431 return TRUE;
2433 fail:
2434 ranges_check(ranges, "failed add");
2435 return FALSE;
2438 static BOOL ranges_del(RANGES ranges, RANGE range)
2440 RANGE *chkrgn;
2441 INT index;
2443 TRACE("(%s)\n", debugrange(&range));
2444 ranges_check(ranges, "before del");
2446 /* we don't use DPAS_SORTED here, since we need *
2447 * to find the first overlapping range */
2448 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2449 while(index != -1)
2451 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2453 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2455 /* case 1: Same range */
2456 if ( (chkrgn->upper == range.upper) &&
2457 (chkrgn->lower == range.lower) )
2459 DPA_DeletePtr(ranges->hdpa, index);
2460 break;
2462 /* case 2: engulf */
2463 else if ( (chkrgn->upper <= range.upper) &&
2464 (chkrgn->lower >= range.lower) )
2466 DPA_DeletePtr(ranges->hdpa, index);
2468 /* case 3: overlap upper */
2469 else if ( (chkrgn->upper <= range.upper) &&
2470 (chkrgn->lower < range.lower) )
2472 chkrgn->upper = range.lower;
2474 /* case 4: overlap lower */
2475 else if ( (chkrgn->upper > range.upper) &&
2476 (chkrgn->lower >= range.lower) )
2478 chkrgn->lower = range.upper;
2479 break;
2481 /* case 5: fully internal */
2482 else
2484 RANGE tmprgn = *chkrgn, *newrgn;
2486 if (!(newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE)))) goto fail;
2487 newrgn->lower = chkrgn->lower;
2488 newrgn->upper = range.lower;
2489 chkrgn->lower = range.upper;
2490 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2492 COMCTL32_Free(newrgn);
2493 goto fail;
2495 chkrgn = &tmprgn;
2496 break;
2499 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2502 ranges_check(ranges, "after del");
2503 return TRUE;
2505 fail:
2506 ranges_check(ranges, "failed del");
2507 return FALSE;
2510 /***
2511 * DESCRIPTION:
2512 * Removes all selection ranges
2514 * Parameters(s):
2515 * [I] infoPtr : valid pointer to the listview structure
2516 * [I] toSkip : item range to skip removing the selection
2518 * RETURNS:
2519 * SUCCESS : TRUE
2520 * FAILURE : TRUE
2522 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2524 LVITEMW lvItem;
2525 ITERATOR i;
2526 RANGES clone;
2528 TRACE("()\n");
2530 lvItem.state = 0;
2531 lvItem.stateMask = LVIS_SELECTED;
2533 /* need to clone the DPA because callbacks can change it */
2534 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2535 iterator_ranges(&i, ranges_diff(clone, toSkip));
2536 while(iterator_next(&i))
2537 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2538 /* note that the iterator destructor will free the cloned range */
2539 iterator_destroy(&i);
2541 return TRUE;
2544 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2546 RANGES toSkip;
2548 if (!(toSkip = ranges_create(1))) return FALSE;
2549 if (nItem != -1) ranges_additem(toSkip, nItem);
2550 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2551 ranges_destroy(toSkip);
2552 return TRUE;
2555 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2557 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2560 /***
2561 * DESCRIPTION:
2562 * Retrieves the number of items that are marked as selected.
2564 * PARAMETER(S):
2565 * [I] infoPtr : valid pointer to the listview structure
2567 * RETURN:
2568 * Number of items selected.
2570 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2572 INT nSelectedCount = 0;
2574 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2576 INT i;
2577 for (i = 0; i < infoPtr->nItemCount; i++)
2579 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2580 nSelectedCount++;
2583 else
2584 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2586 TRACE("nSelectedCount=%d\n", nSelectedCount);
2587 return nSelectedCount;
2590 /***
2591 * DESCRIPTION:
2592 * Manages the item focus.
2594 * PARAMETER(S):
2595 * [I] infoPtr : valid pointer to the listview structure
2596 * [I] nItem : item index
2598 * RETURN:
2599 * TRUE : focused item changed
2600 * FALSE : focused item has NOT changed
2602 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2604 INT oldFocus = infoPtr->nFocusedItem;
2605 LVITEMW lvItem;
2607 if (nItem == infoPtr->nFocusedItem) return FALSE;
2609 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2610 lvItem.stateMask = LVIS_FOCUSED;
2611 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2613 return oldFocus != infoPtr->nFocusedItem;
2616 /* Helper function for LISTVIEW_ShiftIndices *only* */
2617 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2619 if (nShiftItem < nItem) return nShiftItem;
2621 if (nShiftItem > nItem) return nShiftItem + direction;
2623 if (direction > 0) return nShiftItem + direction;
2625 return min(nShiftItem, infoPtr->nItemCount - 1);
2629 * DESCRIPTION:
2630 * Updates the various indices after an item has been inserted or deleted.
2632 * PARAMETER(S):
2633 * [I] infoPtr : valid pointer to the listview structure
2634 * [I] nItem : item index
2635 * [I] direction : Direction of shift, +1 or -1.
2637 * RETURN:
2638 * None
2640 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2642 INT nNewFocus;
2644 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2646 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2648 assert(abs(direction) == 1);
2650 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2652 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2653 if (nNewFocus != infoPtr->nFocusedItem)
2654 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2656 /* But we are not supposed to modify nHotItem! */
2661 * DESCRIPTION:
2662 * Adds a block of selections.
2664 * PARAMETER(S):
2665 * [I] infoPtr : valid pointer to the listview structure
2666 * [I] nItem : item index
2668 * RETURN:
2669 * None
2671 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2673 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2674 INT nLast = max(infoPtr->nSelectionMark, nItem);
2675 INT i;
2676 LVITEMW item;
2678 if (nFirst == -1)
2679 nFirst = nItem;
2681 item.state = LVIS_SELECTED;
2682 item.stateMask = LVIS_SELECTED;
2684 /* FIXME: this is not correct LVS_OWNERDATA
2685 * See docu for LVN_ITEMCHANGED. Is there something similar for
2686 * RemoveGroupSelection (is there such a thing?)?
2688 for (i = nFirst; i <= nLast; i++)
2689 LISTVIEW_SetItemState(infoPtr,i,&item);
2691 LISTVIEW_SetItemFocus(infoPtr, nItem);
2692 infoPtr->nSelectionMark = nItem;
2696 /***
2697 * DESCRIPTION:
2698 * Sets a single group selection.
2700 * PARAMETER(S):
2701 * [I] infoPtr : valid pointer to the listview structure
2702 * [I] nItem : item index
2704 * RETURN:
2705 * None
2707 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2709 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2710 RANGES selection;
2711 LVITEMW item;
2712 ITERATOR i;
2714 if (!(selection = ranges_create(100))) return;
2716 item.state = LVIS_SELECTED;
2717 item.stateMask = LVIS_SELECTED;
2719 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2721 if (infoPtr->nSelectionMark == -1)
2723 infoPtr->nSelectionMark = nItem;
2724 ranges_additem(selection, nItem);
2726 else
2728 RANGE sel;
2730 sel.lower = min(infoPtr->nSelectionMark, nItem);
2731 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
2732 ranges_add(selection, sel);
2735 else
2737 RECT rcItem, rcSel, rcSelMark;
2738 POINT ptItem;
2740 rcItem.left = LVIR_BOUNDS;
2741 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2742 rcSelMark.left = LVIR_BOUNDS;
2743 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2744 UnionRect(&rcSel, &rcItem, &rcSelMark);
2745 iterator_frameditems(&i, infoPtr, &rcSel);
2746 while(iterator_next(&i))
2748 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
2749 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
2751 iterator_destroy(&i);
2754 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
2755 iterator_ranges(&i, selection);
2756 while(iterator_next(&i))
2757 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
2758 /* this will also destroy the selection */
2759 iterator_destroy(&i);
2761 LISTVIEW_SetItemFocus(infoPtr, nItem);
2764 /***
2765 * DESCRIPTION:
2766 * Sets a single selection.
2768 * PARAMETER(S):
2769 * [I] infoPtr : valid pointer to the listview structure
2770 * [I] nItem : item index
2772 * RETURN:
2773 * None
2775 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2777 LVITEMW lvItem;
2779 TRACE("nItem=%d\n", nItem);
2781 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
2783 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2784 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2785 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2787 infoPtr->nSelectionMark = nItem;
2790 /***
2791 * DESCRIPTION:
2792 * Set selection(s) with keyboard.
2794 * PARAMETER(S):
2795 * [I] infoPtr : valid pointer to the listview structure
2796 * [I] INT : item index
2798 * RETURN:
2799 * SUCCESS : TRUE (needs to be repainted)
2800 * FAILURE : FALSE (nothing has changed)
2802 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2804 /* FIXME: pass in the state */
2805 LONG lStyle = infoPtr->dwStyle;
2806 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2807 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2808 BOOL bResult = FALSE;
2810 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2812 if (lStyle & LVS_SINGLESEL)
2814 bResult = TRUE;
2815 LISTVIEW_SetSelection(infoPtr, nItem);
2817 else
2819 if (wShift)
2821 bResult = TRUE;
2822 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2824 else if (wCtrl)
2826 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2828 else
2830 bResult = TRUE;
2831 LISTVIEW_SetSelection(infoPtr, nItem);
2834 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2837 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2838 return bResult;
2842 /***
2843 * DESCRIPTION:
2844 * Called when the mouse is being actively tracked and has hovered for a specified
2845 * amount of time
2847 * PARAMETER(S):
2848 * [I] infoPtr : valid pointer to the listview structure
2849 * [I] fwKeys : key indicator
2850 * [I] pts : mouse position
2852 * RETURN:
2853 * 0 if the message was processed, non-zero if there was an error
2855 * INFO:
2856 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2857 * over the item for a certain period of time.
2860 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2862 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2863 /* FIXME: select the item!!! */
2864 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2866 return 0;
2869 /***
2870 * DESCRIPTION:
2871 * Called whenever WM_MOUSEMOVE is received.
2873 * PARAMETER(S):
2874 * [I] infoPtr : valid pointer to the listview structure
2875 * [I] fwKeys : key indicator
2876 * [I] pts : mouse position
2878 * RETURN:
2879 * 0 if the message is processed, non-zero if there was an error
2881 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2883 TRACKMOUSEEVENT trackinfo;
2885 /* see if we are supposed to be tracking mouse hovering */
2886 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2887 /* fill in the trackinfo struct */
2888 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2889 trackinfo.dwFlags = TME_QUERY;
2890 trackinfo.hwndTrack = infoPtr->hwndSelf;
2891 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2893 /* see if we are already tracking this hwnd */
2894 _TrackMouseEvent(&trackinfo);
2896 if(!(trackinfo.dwFlags & TME_HOVER)) {
2897 trackinfo.dwFlags = TME_HOVER;
2899 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2900 _TrackMouseEvent(&trackinfo);
2904 return 0;
2908 /***
2909 * Tests wheather the item is assignable to a list with style lStyle
2911 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2913 if ( (lpLVItem->mask & LVIF_TEXT) &&
2914 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2915 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2917 return TRUE;
2921 /***
2922 * DESCRIPTION:
2923 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2925 * PARAMETER(S):
2926 * [I] infoPtr : valid pointer to the listview structure
2927 * [I] lpLVItem : valid pointer to new item atttributes
2928 * [I] isNew : the item being set is being inserted
2929 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2930 * [O] bChanged : will be set to TRUE if the item really changed
2932 * RETURN:
2933 * SUCCESS : TRUE
2934 * FAILURE : FALSE
2936 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
2938 LISTVIEW_ITEM *lpItem;
2939 NMLISTVIEW nmlv;
2940 UINT uChanged = 0;
2941 LVITEMW item;
2943 TRACE("()\n");
2945 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
2947 if (lpLVItem->mask == 0) return TRUE;
2949 if (infoPtr->dwStyle & LVS_OWNERDATA)
2951 /* a virtual listview we stores only selection and focus */
2952 if ((lpLVItem->mask & ~LVIF_STATE) || (lpLVItem->stateMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
2953 return FALSE;
2954 lpItem = NULL;
2956 else
2958 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2959 if (!hdpaSubItems) return FALSE;
2960 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0))) return FALSE;
2963 /* we need to get the lParam and state of the item */
2964 item.iItem = lpLVItem->iItem;
2965 item.iSubItem = lpLVItem->iSubItem;
2966 item.mask = LVIF_STATE | LVIF_PARAM;
2967 item.stateMask = ~0;
2968 item.state = 0;
2969 item.lParam = 0;
2970 if (!isNew && !LISTVIEW_GetItemT(infoPtr, &item, TRUE)) return FALSE;
2972 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
2973 /* determine what fields will change */
2974 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
2975 uChanged |= LVIF_STATE;
2977 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2978 uChanged |= LVIF_IMAGE;
2980 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2981 uChanged |= LVIF_PARAM;
2983 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2984 uChanged |= LVIF_INDENT;
2986 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2987 uChanged |= LVIF_TEXT;
2989 TRACE("uChanged=0x%x\n", uChanged);
2990 if (!uChanged) return TRUE;
2991 *bChanged = TRUE;
2993 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2994 nmlv.iItem = lpLVItem->iItem;
2995 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
2996 nmlv.uOldState = item.state;
2997 nmlv.uChanged = uChanged;
2998 nmlv.lParam = item.lParam;
3000 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3001 /* and we are _NOT_ virtual (LVS_OWERNDATA) */
3002 if(lpItem && !isNew && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3003 return FALSE;
3005 /* copy information */
3006 if (lpLVItem->mask & LVIF_TEXT)
3007 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3009 if (lpLVItem->mask & LVIF_IMAGE)
3010 lpItem->hdr.iImage = lpLVItem->iImage;
3012 if (lpLVItem->mask & LVIF_PARAM)
3013 lpItem->lParam = lpLVItem->lParam;
3015 if (lpLVItem->mask & LVIF_INDENT)
3016 lpItem->iIndent = lpLVItem->iIndent;
3018 if (uChanged & LVIF_STATE)
3020 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED))
3022 lpItem->state &= ~lpLVItem->stateMask;
3023 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3025 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3027 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3028 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3030 else if (lpLVItem->stateMask & LVIS_SELECTED)
3031 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3033 /* if we are asked to change focus, and we manage it, do it */
3034 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3036 if (lpLVItem->state & LVIS_FOCUSED)
3038 LISTVIEW_SetItemFocus(infoPtr, -1);
3039 infoPtr->nFocusedItem = lpLVItem->iItem;
3040 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
3042 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3043 infoPtr->nFocusedItem = -1;
3047 /* if we're inserting the item, we're done */
3048 if (isNew) return TRUE;
3050 /* send LVN_ITEMCHANGED notification */
3051 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3052 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3054 return TRUE;
3057 /***
3058 * DESCRIPTION:
3059 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3061 * PARAMETER(S):
3062 * [I] infoPtr : valid pointer to the listview structure
3063 * [I] lpLVItem : valid pointer to new subitem atttributes
3064 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3065 * [O] bChanged : will be set to TRUE if the item really changed
3067 * RETURN:
3068 * SUCCESS : TRUE
3069 * FAILURE : FALSE
3071 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged)
3073 HDPA hdpaSubItems;
3074 LISTVIEW_SUBITEM *lpSubItem;
3076 /* we do not support subitems for virtual listviews */
3077 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3079 /* set subitem only if column is present */
3080 if (lpLVItem->iSubItem >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3082 /* First do some sanity checks */
3083 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3084 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3086 /* get the subitem structure, and create it if not there */
3087 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3088 if (!hdpaSubItems) return FALSE;
3090 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3091 if (!lpSubItem)
3093 LISTVIEW_SUBITEM *tmpSubItem;
3094 INT i;
3096 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
3097 if (!lpSubItem) return FALSE;
3098 /* we could binary search here, if need be...*/
3099 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3101 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3102 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3104 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3106 COMCTL32_Free(lpSubItem);
3107 return FALSE;
3109 lpSubItem->iSubItem = lpLVItem->iSubItem;
3110 *bChanged = TRUE;
3113 if (lpLVItem->mask & LVIF_IMAGE)
3114 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3116 lpSubItem->hdr.iImage = lpLVItem->iImage;
3117 *bChanged = TRUE;
3120 if (lpLVItem->mask & LVIF_TEXT)
3121 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3123 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3124 *bChanged = TRUE;
3127 return TRUE;
3130 /***
3131 * DESCRIPTION:
3132 * Sets item attributes.
3134 * PARAMETER(S):
3135 * [I] infoPtr : valid pointer to the listview structure
3136 * [I] lpLVItem : new item atttributes
3137 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3139 * RETURN:
3140 * SUCCESS : TRUE
3141 * FAILURE : FALSE
3143 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
3145 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3146 LPWSTR pszText = NULL;
3147 BOOL bResult, bChanged = FALSE;
3149 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3151 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3152 return FALSE;
3154 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3155 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3157 pszText = lpLVItem->pszText;
3158 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3161 /* actually set the fields */
3162 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3164 if (lpLVItem->iSubItem)
3165 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3166 else
3167 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3169 /* redraw item, if necessary */
3170 if (bChanged && !infoPtr->bIsDrawing)
3172 /* this little optimization eliminates some nasty flicker */
3173 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3174 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3175 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3176 else
3177 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3179 /* restore text */
3180 if (pszText)
3182 textfreeT(lpLVItem->pszText, isW);
3183 lpLVItem->pszText = pszText;
3186 return bResult;
3189 /***
3190 * DESCRIPTION:
3191 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3193 * PARAMETER(S):
3194 * [I] infoPtr : valid pointer to the listview structure
3196 * RETURN:
3197 * item index
3199 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3201 LONG lStyle = infoPtr->dwStyle;
3202 UINT uView = lStyle & LVS_TYPEMASK;
3203 INT nItem = 0;
3204 SCROLLINFO scrollInfo;
3206 scrollInfo.cbSize = sizeof(SCROLLINFO);
3207 scrollInfo.fMask = SIF_POS;
3209 if (uView == LVS_LIST)
3211 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3212 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3214 else if (uView == LVS_REPORT)
3216 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3217 nItem = scrollInfo.nPos;
3219 else
3221 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3222 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3225 TRACE("nItem=%d\n", nItem);
3227 return nItem;
3231 /***
3232 * DESCRIPTION:
3233 * Erases the background of the given rectangle
3235 * PARAMETER(S):
3236 * [I] infoPtr : valid pointer to the listview structure
3237 * [I] hdc : device context handle
3238 * [I] lprcBox : clipping rectangle
3240 * RETURN:
3241 * Success: TRUE
3242 * Failure: FALSE
3244 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
3246 if (!infoPtr->hBkBrush) return FALSE;
3248 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3250 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3253 /***
3254 * DESCRIPTION:
3255 * Draws an item.
3257 * PARAMETER(S):
3258 * [I] infoPtr : valid pointer to the listview structure
3259 * [I] hdc : device context handle
3260 * [I] nItem : item index
3261 * [I] nSubItem : subitem index
3262 * [I] pos : item position in client coordinates
3263 * [I] cdmode : custom draw mode
3265 * RETURN:
3266 * Success: TRUE
3267 * Failure: FALSE
3269 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3271 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3272 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3273 DWORD cditemmode = CDRF_DODEFAULT;
3274 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3275 NMLVCUSTOMDRAW nmlvcd;
3276 HIMAGELIST himl;
3277 LVITEMW lvItem;
3279 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3281 /* get information needed for drawing the item */
3282 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3283 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3284 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3285 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3286 lvItem.iItem = nItem;
3287 lvItem.state = 0;
3288 lvItem.iSubItem = nSubItem;
3289 lvItem.cchTextMax = DISP_TEXT_SIZE;
3290 lvItem.pszText = szDispText;
3291 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3292 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3293 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3294 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3296 /* now check if we need to update the focus rectangle */
3297 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3299 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3300 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3301 OffsetRect(&rcBox, pos.x, pos.y);
3302 OffsetRect(&rcState, pos.x, pos.y);
3303 OffsetRect(&rcIcon, pos.x, pos.y);
3304 OffsetRect(&rcLabel, pos.x, pos.y);
3305 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3306 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3308 /* fill in the custom draw structure */
3309 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox);
3310 nmlvcd.nmcd.dwItemSpec = lvItem.iItem;
3311 nmlvcd.iSubItem = lvItem.iSubItem;
3312 if (lvItem.state & LVIS_SELECTED) nmlvcd.nmcd.uItemState |= CDIS_SELECTED;
3313 if (lvItem.state & LVIS_FOCUSED) nmlvcd.nmcd.uItemState |= CDIS_FOCUS;
3314 if (lvItem.iItem == infoPtr->nHotItem) nmlvcd.nmcd.uItemState |= CDIS_HOT;
3315 nmlvcd.nmcd.lItemlParam = lvItem.lParam;
3317 if (lvItem.state & LVIS_SELECTED)
3319 if (infoPtr->bFocus)
3321 nmlvcd.clrTextBk = comctl32_color.clrHighlight;
3322 nmlvcd.clrText = comctl32_color.clrHighlightText;
3324 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
3326 nmlvcd.clrTextBk = comctl32_color.clr3dFace;
3327 nmlvcd.clrText = comctl32_color.clrBtnText;
3331 if (cdmode & CDRF_NOTIFYITEMDRAW)
3332 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3333 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3335 /* state icons */
3336 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3338 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3339 if (uStateImage)
3340 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3343 /* small icons */
3344 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3345 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3346 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3347 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3349 /* Don't bother painting item being edited */
3350 if (infoPtr->hwndEdit && lprcFocus && nSubItem == 0) goto postpaint;
3352 /* Set the text attributes */
3353 if (nmlvcd.clrTextBk != CLR_NONE)
3355 SetBkMode(hdc, OPAQUE);
3356 SetBkColor(hdc, nmlvcd.clrTextBk == CLR_DEFAULT ? infoPtr->clrTextBkDefault : nmlvcd.clrTextBk);
3358 else
3359 SetBkMode(hdc, TRANSPARENT);
3360 SetTextColor(hdc, nmlvcd.clrText);
3362 /* draw the selection background, if we're drawing the main item */
3363 if (nSubItem == 0)
3365 rcSelect = rcLabel;
3366 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3367 rcSelect.right = rcBox.right;
3369 if (lvItem.state & LVIS_SELECTED)
3370 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3371 if(lprcFocus) *lprcFocus = rcSelect;
3374 /* figure out the text drawing flags */
3375 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3376 if (uView == LVS_ICON)
3377 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3378 else if (nSubItem)
3380 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3382 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3383 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3384 default: uFormat |= DT_LEFT;
3387 if (!(uFormat & (DT_RIGHT | DT_CENTER))) rcLabel.left += 2;
3388 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3390 postpaint:
3391 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3392 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3393 return TRUE;
3396 /***
3397 * DESCRIPTION:
3398 * Draws listview items when in owner draw mode.
3400 * PARAMETER(S):
3401 * [I] infoPtr : valid pointer to the listview structure
3402 * [I] hdc : device context handle
3404 * RETURN:
3405 * None
3407 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
3409 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3410 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3411 POINT Origin, Position;
3412 DRAWITEMSTRUCT dis;
3413 LVITEMW item;
3414 ITERATOR i;
3416 TRACE("()\n");
3418 ZeroMemory(&dis, sizeof(dis));
3420 /* Get scroll info once before loop */
3421 LISTVIEW_GetOrigin(infoPtr, &Origin);
3423 /* figure out what we need to draw */
3424 iterator_visibleitems(&i, infoPtr, hdc);
3426 /* send cache hint notification */
3427 if (infoPtr->dwStyle & LVS_OWNERDATA)
3429 RANGE range = iterator_range(&i);
3430 NMLVCACHEHINT nmlv;
3432 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3433 nmlv.iFrom = range.lower;
3434 nmlv.iTo = range.upper - 1;
3435 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3438 /* iterate through the invalidated rows */
3439 while(iterator_prev(&i))
3441 item.iItem = i.nItem;
3442 item.iSubItem = 0;
3443 item.mask = LVIF_PARAM | LVIF_STATE;
3444 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3445 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3447 dis.CtlType = ODT_LISTVIEW;
3448 dis.CtlID = uID;
3449 dis.itemID = item.iItem;
3450 dis.itemAction = ODA_DRAWENTIRE;
3451 dis.itemState = 0;
3452 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3453 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3454 dis.hwndItem = infoPtr->hwndSelf;
3455 dis.hDC = hdc;
3456 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3457 dis.rcItem.left = Position.x + Origin.x;
3458 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3459 dis.rcItem.top = Position.y + Origin.y;
3460 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3461 dis.itemData = item.lParam;
3463 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3464 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3466 iterator_destroy(&i);
3469 /***
3470 * DESCRIPTION:
3471 * Draws listview items when in report display mode.
3473 * PARAMETER(S):
3474 * [I] infoPtr : valid pointer to the listview structure
3475 * [I] hdc : device context handle
3476 * [I] cdmode : custom draw mode
3478 * RETURN:
3479 * None
3481 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3483 INT rgntype, nFirstCol, nLastCol, nCol;
3484 RECT rcClip, rcItem;
3485 POINT Origin, Position;
3486 ITERATOR i;
3488 TRACE("()\n");
3490 /* figure out what to draw */
3491 rgntype = GetClipBox(hdc, &rcClip);
3492 if (rgntype == NULLREGION) return;
3494 /* Get scroll info once before loop */
3495 LISTVIEW_GetOrigin(infoPtr, &Origin);
3497 /* narrow down the columns we need to paint */
3498 for(nFirstCol = 0; nFirstCol < infoPtr->hdpaColumns->nItemCount; nFirstCol++)
3500 LISTVIEW_GetHeaderRect(infoPtr, nFirstCol, &rcItem);
3501 if (rcItem.right + Origin.x >= rcClip.left) break;
3503 for(nLastCol = infoPtr->hdpaColumns->nItemCount - 1; nLastCol >= 0; nLastCol--)
3505 LISTVIEW_GetHeaderRect(infoPtr, nLastCol, &rcItem);
3506 if (rcItem.left + Origin.x < rcClip.right) break;
3509 /* figure out what we need to draw */
3510 iterator_visibleitems(&i, infoPtr, hdc);
3512 /* a last few bits before we start drawing */
3513 TRACE("Colums=(%di - %d)\n", nFirstCol, nLastCol);
3515 /* iterate through the invalidated rows */
3516 while(iterator_prev(&i))
3518 /* iterate through the invalidated columns */
3519 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
3521 LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position);
3522 Position.x += Origin.x;
3523 Position.y += Origin.y;
3525 if (rgntype == COMPLEXREGION)
3527 LISTVIEW_GetHeaderRect(infoPtr, nCol, &rcItem);
3528 rcItem.top = 0;
3529 rcItem.bottom = infoPtr->nItemHeight;
3530 OffsetRect(&rcItem, Position.x, Position.y);
3531 if (!RectVisible(hdc, &rcItem)) continue;
3534 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, nCol, Position, cdmode);
3537 iterator_destroy(&i);
3540 /***
3541 * DESCRIPTION:
3542 * Draws listview items when in list display mode.
3544 * PARAMETER(S):
3545 * [I] infoPtr : valid pointer to the listview structure
3546 * [I] hdc : device context handle
3547 * [I] cdmode : custom draw mode
3549 * RETURN:
3550 * None
3552 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3554 POINT Origin, Position;
3555 ITERATOR i;
3557 /* Get scroll info once before loop */
3558 LISTVIEW_GetOrigin(infoPtr, &Origin);
3560 /* figure out what we need to draw */
3561 iterator_visibleitems(&i, infoPtr, hdc);
3563 while(iterator_prev(&i))
3565 LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position);
3566 Position.x += Origin.x;
3567 Position.y += Origin.y;
3569 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, 0, Position, cdmode);
3571 iterator_destroy(&i);
3575 /***
3576 * DESCRIPTION:
3577 * Draws listview items.
3579 * PARAMETER(S):
3580 * [I] infoPtr : valid pointer to the listview structure
3581 * [I] hdc : device context handle
3583 * RETURN:
3584 * NoneX
3586 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3588 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3589 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3590 NMLVCUSTOMDRAW nmlvcd;
3591 HFONT hOldFont;
3592 DWORD cdmode;
3593 INT oldBkMode;
3594 RECT rcClient;
3596 LISTVIEW_DUMP(infoPtr);
3598 infoPtr->bIsDrawing = TRUE;
3600 /* save dc values we're gonna trash while drawing */
3601 hOldFont = SelectObject(hdc, infoPtr->hFont);
3602 oldBkMode = GetBkMode(hdc);
3603 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3604 oldTextColor = GetTextColor(hdc);
3606 oldClrTextBk = infoPtr->clrTextBk;
3607 oldClrText = infoPtr->clrText;
3609 GetClientRect(infoPtr->hwndSelf, &rcClient);
3610 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient);
3611 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3612 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3614 /* Use these colors to draw the items */
3615 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3616 infoPtr->clrText = nmlvcd.clrText;
3618 /* nothing to draw */
3619 if(infoPtr->nItemCount == 0) goto enddraw;
3621 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3622 LISTVIEW_RefreshOwnerDraw(infoPtr, hdc);
3623 else
3625 if (uView == LVS_REPORT)
3626 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3627 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3628 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3630 /* if we have a focus rect, draw it */
3631 if (infoPtr->bFocus)
3632 DrawFocusRect(hdc, &infoPtr->rcFocus);
3635 enddraw:
3636 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3637 notify_customdraw(infoPtr, CDDS_POSTPAINT, &nmlvcd);
3639 infoPtr->clrTextBk = oldClrTextBk;
3640 infoPtr->clrText = oldClrText;
3642 SelectObject(hdc, hOldFont);
3643 SetBkMode(hdc, oldBkMode);
3644 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3645 SetTextColor(hdc, oldTextColor);
3646 infoPtr->bIsDrawing = FALSE;
3650 /***
3651 * DESCRIPTION:
3652 * Calculates the approximate width and height of a given number of items.
3654 * PARAMETER(S):
3655 * [I] infoPtr : valid pointer to the listview structure
3656 * [I] nItemCount : number of items
3657 * [I] wWidth : width
3658 * [I] wHeight : height
3660 * RETURN:
3661 * Returns a DWORD. The width in the low word and the height in high word.
3663 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3664 WORD wWidth, WORD wHeight)
3666 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3667 INT nItemCountPerColumn = 1;
3668 INT nColumnCount = 0;
3669 DWORD dwViewRect = 0;
3671 if (nItemCount == -1)
3672 nItemCount = infoPtr->nItemCount;
3674 if (uView == LVS_LIST)
3676 if (wHeight == 0xFFFF)
3678 /* use current height */
3679 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3682 if (wHeight < infoPtr->nItemHeight)
3683 wHeight = infoPtr->nItemHeight;
3685 if (nItemCount > 0)
3687 if (infoPtr->nItemHeight > 0)
3689 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3690 if (nItemCountPerColumn == 0)
3691 nItemCountPerColumn = 1;
3693 if (nItemCount % nItemCountPerColumn != 0)
3694 nColumnCount = nItemCount / nItemCountPerColumn;
3695 else
3696 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3700 /* Microsoft padding magic */
3701 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3702 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3704 dwViewRect = MAKELONG(wWidth, wHeight);
3706 else if (uView == LVS_REPORT)
3707 FIXME("uView == LVS_REPORT: not implemented\n");
3708 else if (uView == LVS_SMALLICON)
3709 FIXME("uView == LVS_SMALLICON: not implemented\n");
3710 else if (uView == LVS_ICON)
3711 FIXME("uView == LVS_ICON: not implemented\n");
3713 return dwViewRect;
3716 /***
3717 * DESCRIPTION:
3718 * Arranges listview items in icon display mode.
3720 * PARAMETER(S):
3721 * [I] infoPtr : valid pointer to the listview structure
3722 * [I] nAlignCode : alignment code
3724 * RETURN:
3725 * SUCCESS : TRUE
3726 * FAILURE : FALSE
3728 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3730 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3731 BOOL bResult = FALSE;
3733 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3735 switch (nAlignCode)
3737 case LVA_ALIGNLEFT:
3738 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3739 break;
3740 case LVA_ALIGNTOP:
3741 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3742 break;
3743 case LVA_DEFAULT:
3744 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3745 break;
3746 case LVA_SNAPTOGRID:
3747 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3748 break;
3752 return bResult;
3755 /* << LISTVIEW_CreateDragImage >> */
3758 /***
3759 * DESCRIPTION:
3760 * Removes all listview items and subitems.
3762 * PARAMETER(S):
3763 * [I] infoPtr : valid pointer to the listview structure
3765 * RETURN:
3766 * SUCCESS : TRUE
3767 * FAILURE : FALSE
3769 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3771 LONG lStyle = infoPtr->dwStyle;
3772 UINT uView = lStyle & LVS_TYPEMASK;
3773 LISTVIEW_ITEM *lpItem;
3774 LISTVIEW_SUBITEM *lpSubItem;
3775 NMLISTVIEW nmlv;
3776 BOOL bSuppress;
3777 BOOL bResult = FALSE;
3778 HDPA hdpaSubItems;
3780 TRACE("()\n");
3782 LISTVIEW_DeselectAll(infoPtr);
3783 infoPtr->nSelectionMark=-1;
3784 infoPtr->nFocusedItem=-1;
3785 SetRectEmpty(&infoPtr->rcFocus);
3786 /* But we are supposed to leave nHotItem as is! */
3788 if (lStyle & LVS_OWNERDATA)
3790 infoPtr->nItemCount = 0;
3791 LISTVIEW_InvalidateList(infoPtr);
3792 return TRUE;
3795 if (infoPtr->nItemCount > 0)
3797 INT i, j;
3799 /* send LVN_DELETEALLITEMS notification */
3800 /* verify if subsequent LVN_DELETEITEM notifications should be
3801 suppressed */
3802 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3803 nmlv.iItem = -1;
3804 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3806 for (i = 0; i < infoPtr->nItemCount; i++)
3808 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3809 if (hdpaSubItems != NULL)
3811 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3813 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3814 if (lpSubItem != NULL)
3816 /* free subitem string */
3817 if (is_textW(lpSubItem->hdr.pszText))
3818 COMCTL32_Free(lpSubItem->hdr.pszText);
3820 /* free subitem */
3821 COMCTL32_Free(lpSubItem);
3825 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3826 if (lpItem != NULL)
3828 if (!bSuppress)
3830 /* send LVN_DELETEITEM notification */
3831 nmlv.iItem = i;
3832 nmlv.lParam = lpItem->lParam;
3833 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3836 /* free item string */
3837 if (is_textW(lpItem->hdr.pszText))
3838 COMCTL32_Free(lpItem->hdr.pszText);
3840 /* free item */
3841 COMCTL32_Free(lpItem);
3844 DPA_Destroy(hdpaSubItems);
3848 /* reinitialize listview memory */
3849 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3850 infoPtr->nItemCount = 0;
3851 DPA_DeleteAllPtrs(infoPtr->hdpaPosX);
3852 DPA_DeleteAllPtrs(infoPtr->hdpaPosY);
3854 /* align items (set position of each item) */
3855 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3857 if (lStyle & LVS_ALIGNLEFT)
3859 LISTVIEW_AlignLeft(infoPtr);
3861 else
3863 LISTVIEW_AlignTop(infoPtr);
3867 LISTVIEW_UpdateScroll(infoPtr);
3869 LISTVIEW_InvalidateList(infoPtr);
3872 return bResult;
3875 /***
3876 * DESCRIPTION:
3877 * Scrolls, and updates the columns, when a column is changing width.
3879 * PARAMETER(S):
3880 * [I] infoPtr : valid pointer to the listview structure
3881 * [I] nColumn : column to scroll
3882 * [I] dx : amount of scroll, in pixels
3884 * RETURN:
3885 * None.
3887 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
3889 COLUMN_INFO *lpColumnInfo;
3890 RECT rcOld, rcCol;
3891 INT nCol;
3893 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, infoPtr->hdpaColumns->nItemCount - 1));
3894 rcCol = lpColumnInfo->rcHeader;
3895 if (nColumn >= infoPtr->hdpaColumns->nItemCount)
3896 rcCol.left = rcCol.right;
3898 /* ajust the other columns */
3899 for (nCol = nColumn; nCol < infoPtr->hdpaColumns->nItemCount; nCol++)
3901 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
3902 lpColumnInfo->rcHeader.left += dx;
3903 lpColumnInfo->rcHeader.right += dx;
3906 /* do not update screen if not in report mode */
3907 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
3909 /* if we have a focus, must first erase the focus rect */
3910 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
3912 /* Need to reset the item width when inserting a new column */
3913 infoPtr->nItemWidth += dx;
3915 LISTVIEW_UpdateScroll(infoPtr);
3917 /* scroll to cover the deleted column, and invalidate for redraw */
3918 rcOld = infoPtr->rcList;
3919 rcOld.left = rcCol.left;
3920 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3922 /* we can restore focus now */
3923 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
3926 /***
3927 * DESCRIPTION:
3928 * Removes a column from the listview control.
3930 * PARAMETER(S):
3931 * [I] infoPtr : valid pointer to the listview structure
3932 * [I] nColumn : column index
3934 * RETURN:
3935 * SUCCESS : TRUE
3936 * FAILURE : FALSE
3938 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3940 RECT rcCol;
3942 TRACE("nColumn=%d\n", nColumn);
3944 if (nColumn <= 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3946 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
3948 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3949 return FALSE;
3951 COMCTL32_Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
3952 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
3954 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3956 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3957 HDPA hdpaSubItems;
3958 INT nItem, nSubItem, i;
3960 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3962 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3963 if (!hdpaSubItems) continue;
3964 nSubItem = 0;
3965 lpDelItem = 0;
3966 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3968 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3969 if (!lpSubItem) break;
3970 if (lpSubItem->iSubItem == nColumn)
3972 nSubItem = i;
3973 lpDelItem = lpSubItem;
3975 else if (lpSubItem->iSubItem > nColumn)
3977 lpSubItem->iSubItem--;
3981 /* if we found our subitem, zapp it */
3982 if (nSubItem > 0)
3984 /* free string */
3985 if (is_textW(lpDelItem->hdr.pszText))
3986 COMCTL32_Free(lpDelItem->hdr.pszText);
3988 /* free item */
3989 COMCTL32_Free(lpDelItem);
3991 /* free dpa memory */
3992 DPA_DeletePtr(hdpaSubItems, nSubItem);
3997 /* update the other column info */
3998 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4000 return TRUE;
4003 /***
4004 * DESCRIPTION:
4005 * Removes an item from the listview control.
4007 * PARAMETER(S):
4008 * [I] infoPtr : valid pointer to the listview structure
4009 * [I] nItem : item index
4011 * RETURN:
4012 * SUCCESS : TRUE
4013 * FAILURE : FALSE
4015 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4017 LONG lStyle = infoPtr->dwStyle;
4018 UINT uView = lStyle & LVS_TYPEMASK;
4019 NMLISTVIEW nmlv;
4020 BOOL bResult = FALSE;
4021 HDPA hdpaSubItems;
4022 LISTVIEW_ITEM *lpItem;
4023 LISTVIEW_SUBITEM *lpSubItem;
4024 LVITEMW item;
4025 INT i;
4027 TRACE("(nItem=%d)\n", nItem);
4029 /* remove selection, and focus */
4030 item.state = 0;
4031 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4032 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4034 /* send LVN_DELETEITEM notification. */
4035 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
4036 nmlv.iItem = nItem;
4037 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
4039 if (lStyle & LVS_OWNERDATA)
4041 infoPtr->nItemCount--;
4042 LISTVIEW_InvalidateList(infoPtr); /*FIXME: optimize */
4043 return TRUE;
4046 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
4048 /* initialize memory */
4049 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4051 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4052 if (hdpaSubItems != NULL)
4054 infoPtr->nItemCount--;
4055 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4057 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4058 if (lpSubItem != NULL)
4060 /* free item string */
4061 if (is_textW(lpSubItem->hdr.pszText))
4062 COMCTL32_Free(lpSubItem->hdr.pszText);
4064 /* free item */
4065 COMCTL32_Free(lpSubItem);
4069 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4070 if (lpItem != NULL)
4072 /* free item string */
4073 if (is_textW(lpItem->hdr.pszText))
4074 COMCTL32_Free(lpItem->hdr.pszText);
4076 /* free item */
4077 COMCTL32_Free(lpItem);
4080 bResult = DPA_Destroy(hdpaSubItems);
4081 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4082 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4085 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4087 /* align items (set position of each item) */
4088 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4090 if (lStyle & LVS_ALIGNLEFT)
4091 LISTVIEW_AlignLeft(infoPtr);
4092 else
4093 LISTVIEW_AlignTop(infoPtr);
4096 LISTVIEW_UpdateScroll(infoPtr);
4098 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
4101 return bResult;
4105 /***
4106 * DESCRIPTION:
4107 * Callback implementation for editlabel control
4109 * PARAMETER(S):
4110 * [I] infoPtr : valid pointer to the listview structure
4111 * [I] pszText : modified text
4112 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4114 * RETURN:
4115 * SUCCESS : TRUE
4116 * FAILURE : FALSE
4118 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4120 NMLVDISPINFOW dispInfo;
4122 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4124 ZeroMemory(&dispInfo, sizeof(dispInfo));
4125 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4126 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4127 dispInfo.item.iSubItem = 0;
4128 dispInfo.item.stateMask = ~0;
4129 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4130 /* add the text from the edit in */
4131 dispInfo.item.mask |= LVIF_TEXT;
4132 dispInfo.item.pszText = pszText;
4133 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4135 /* Do we need to update the Item Text */
4136 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4137 if (!pszText) return TRUE;
4139 ZeroMemory(&dispInfo, sizeof(dispInfo));
4140 dispInfo.item.mask = LVIF_TEXT;
4141 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4142 dispInfo.item.iSubItem = 0;
4143 dispInfo.item.pszText = pszText;
4144 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4145 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4148 /***
4149 * DESCRIPTION:
4150 * Begin in place editing of specified list view item
4152 * PARAMETER(S):
4153 * [I] infoPtr : valid pointer to the listview structure
4154 * [I] nItem : item index
4155 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4157 * RETURN:
4158 * SUCCESS : TRUE
4159 * FAILURE : FALSE
4161 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4163 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4164 NMLVDISPINFOW dispInfo;
4165 RECT rect;
4167 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4169 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4170 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4172 infoPtr->nEditLabelItem = nItem;
4174 /* Is the EditBox still there, if so remove it */
4175 if(infoPtr->hwndEdit != 0)
4177 SetFocus(infoPtr->hwndSelf);
4178 infoPtr->hwndEdit = 0;
4181 LISTVIEW_SetSelection(infoPtr, nItem);
4182 LISTVIEW_SetItemFocus(infoPtr, nItem);
4184 rect.left = LVIR_LABEL;
4185 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4187 ZeroMemory(&dispInfo, sizeof(dispInfo));
4188 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4189 dispInfo.item.iItem = nItem;
4190 dispInfo.item.iSubItem = 0;
4191 dispInfo.item.stateMask = ~0;
4192 dispInfo.item.pszText = szDispText;
4193 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4194 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4196 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4197 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4198 if (!infoPtr->hwndEdit) return 0;
4200 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4202 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4203 infoPtr->hwndEdit = 0;
4204 return 0;
4207 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4208 SetFocus(infoPtr->hwndEdit);
4209 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4210 return infoPtr->hwndEdit;
4214 /***
4215 * DESCRIPTION:
4216 * Ensures the specified item is visible, scrolling into view if necessary.
4218 * PARAMETER(S):
4219 * [I] infoPtr : valid pointer to the listview structure
4220 * [I] nItem : item index
4221 * [I] bPartial : partially or entirely visible
4223 * RETURN:
4224 * SUCCESS : TRUE
4225 * FAILURE : FALSE
4227 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4229 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4230 INT nScrollPosHeight = 0;
4231 INT nScrollPosWidth = 0;
4232 INT nHorzAdjust = 0;
4233 INT nVertAdjust = 0;
4234 INT nHorzDiff = 0;
4235 INT nVertDiff = 0;
4236 RECT rcItem, rcTemp;
4238 rcItem.left = LVIR_BOUNDS;
4239 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4241 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4243 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4245 /* scroll left/right, but in LVS_REPORT mode */
4246 if (uView == LVS_LIST)
4247 nScrollPosWidth = infoPtr->nItemWidth;
4248 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4249 nScrollPosWidth = 1;
4251 if (rcItem.left < infoPtr->rcList.left)
4253 nHorzAdjust = -1;
4254 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4256 else
4258 nHorzAdjust = 1;
4259 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4263 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4265 /* scroll up/down, but not in LVS_LIST mode */
4266 if (uView == LVS_REPORT)
4267 nScrollPosHeight = infoPtr->nItemHeight;
4268 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4269 nScrollPosHeight = 1;
4271 if (rcItem.top < infoPtr->rcList.top)
4273 nVertAdjust = -1;
4274 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4276 else
4278 nVertAdjust = 1;
4279 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4283 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4285 if (nScrollPosWidth)
4287 INT diff = nHorzDiff / nScrollPosWidth;
4288 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4289 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4292 if (nScrollPosHeight)
4294 INT diff = nVertDiff / nScrollPosHeight;
4295 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4296 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4299 return TRUE;
4302 /***
4303 * DESCRIPTION:
4304 * Searches for an item with specific characteristics.
4306 * PARAMETER(S):
4307 * [I] hwnd : window handle
4308 * [I] nStart : base item index
4309 * [I] lpFindInfo : item information to look for
4311 * RETURN:
4312 * SUCCESS : index of item
4313 * FAILURE : -1
4315 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4316 LPLVFINDINFOW lpFindInfo)
4318 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4319 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4320 BOOL bWrap = FALSE, bNearest = FALSE;
4321 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4322 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4323 POINT Position, Destination;
4324 LVITEMW lvItem;
4326 if (!lpFindInfo || nItem < 0) return -1;
4328 lvItem.mask = 0;
4329 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4331 lvItem.mask |= LVIF_TEXT;
4332 lvItem.pszText = szDispText;
4333 lvItem.cchTextMax = DISP_TEXT_SIZE;
4336 if (lpFindInfo->flags & LVFI_WRAP)
4337 bWrap = TRUE;
4339 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4340 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4342 POINT Origin;
4344 FIXME("LVFI_NEARESTXY is slow.\n");
4345 LISTVIEW_GetOrigin(infoPtr, &Origin);
4346 Destination.x = lpFindInfo->pt.x - Origin.x;
4347 Destination.y = lpFindInfo->pt.y - Origin.y;
4348 switch(lpFindInfo->vkDirection)
4350 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4351 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4352 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4353 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4354 case VK_HOME: Destination.x = Destination.y = 0; break;
4355 case VK_END: Destination.x = infoPtr->rcView.right; Destination.y = infoPtr->rcView.bottom; break;
4356 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4357 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4358 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4360 bNearest = TRUE;
4363 /* if LVFI_PARAM is specified, all other flags are ignored */
4364 if (lpFindInfo->flags & LVFI_PARAM)
4366 lvItem.mask |= LVIF_PARAM;
4367 bNearest = FALSE;
4368 lvItem.mask &= ~LVIF_TEXT;
4371 again:
4372 for (; nItem < nLast; nItem++)
4374 lvItem.iItem = nItem;
4375 lvItem.iSubItem = 0;
4376 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4378 if (lvItem.mask & LVIF_PARAM && lpFindInfo->lParam == lvItem.lParam)
4379 return nItem;
4381 if (lvItem.mask & LVIF_TEXT)
4383 if (lpFindInfo->flags & LVFI_PARTIAL)
4385 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4387 else
4389 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4393 if (!bNearest) return nItem;
4395 /* This is very inefficient. To do a good job here,
4396 * we need a sorted array of (x,y) item positions */
4397 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4399 /* compute the distance^2 to the destination */
4400 xdist = Destination.x - Position.x;
4401 ydist = Destination.y - Position.y;
4402 dist = xdist * xdist + ydist * ydist;
4404 /* remember the distance, and item if it's closer */
4405 if (dist < mindist)
4407 mindist = dist;
4408 nNearestItem = nItem;
4412 if (bWrap)
4414 nItem = 0;
4415 nLast = min(nStart + 1, infoPtr->nItemCount);
4416 bWrap = FALSE;
4417 goto again;
4420 return nNearestItem;
4423 /***
4424 * DESCRIPTION:
4425 * Searches for an item with specific characteristics.
4427 * PARAMETER(S):
4428 * [I] hwnd : window handle
4429 * [I] nStart : base item index
4430 * [I] lpFindInfo : item information to look for
4432 * RETURN:
4433 * SUCCESS : index of item
4434 * FAILURE : -1
4436 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4437 LPLVFINDINFOA lpFindInfo)
4439 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4440 LVFINDINFOW fiw;
4441 LRESULT res;
4443 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4444 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4445 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4446 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4447 return res;
4450 /***
4451 * DESCRIPTION:
4452 * Retrieves the background image of the listview control.
4454 * PARAMETER(S):
4455 * [I] infoPtr : valid pointer to the listview structure
4456 * [O] lpBkImage : background image attributes
4458 * RETURN:
4459 * SUCCESS : TRUE
4460 * FAILURE : FALSE
4462 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4463 /* { */
4464 /* FIXME (listview, "empty stub!\n"); */
4465 /* return FALSE; */
4466 /* } */
4468 /***
4469 * DESCRIPTION:
4470 * Retrieves column attributes.
4472 * PARAMETER(S):
4473 * [I] infoPtr : valid pointer to the listview structure
4474 * [I] nColumn : column index
4475 * [IO] lpColumn : column information
4476 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4477 * otherwise it is in fact a LPLVCOLUMNA
4479 * RETURN:
4480 * SUCCESS : TRUE
4481 * FAILURE : FALSE
4483 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4485 COLUMN_INFO *lpColumnInfo;
4486 HDITEMW hdi;
4488 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
4489 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4491 /* initialize memory */
4492 ZeroMemory(&hdi, sizeof(hdi));
4494 if (lpColumn->mask & LVCF_TEXT)
4496 hdi.mask |= HDI_TEXT;
4497 hdi.pszText = lpColumn->pszText;
4498 hdi.cchTextMax = lpColumn->cchTextMax;
4501 if (lpColumn->mask & LVCF_IMAGE)
4502 hdi.mask |= HDI_IMAGE;
4504 if (lpColumn->mask & LVCF_ORDER)
4505 hdi.mask |= HDI_ORDER;
4507 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4509 if (lpColumn->mask & LVCF_FMT)
4510 lpColumn->fmt = lpColumnInfo->fmt;
4512 if (lpColumn->mask & LVCF_WIDTH)
4513 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4515 if (lpColumn->mask & LVCF_IMAGE)
4516 lpColumn->iImage = hdi.iImage;
4518 if (lpColumn->mask & LVCF_ORDER)
4519 lpColumn->iOrder = hdi.iOrder;
4521 return TRUE;
4525 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4527 INT i;
4529 if (!lpiArray)
4530 return FALSE;
4532 /* FIXME: little hack */
4533 for (i = 0; i < iCount; i++)
4534 lpiArray[i] = i;
4536 return TRUE;
4539 /***
4540 * DESCRIPTION:
4541 * Retrieves the column width.
4543 * PARAMETER(S):
4544 * [I] infoPtr : valid pointer to the listview structure
4545 * [I] int : column index
4547 * RETURN:
4548 * SUCCESS : column width
4549 * FAILURE : zero
4551 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4553 INT nColumnWidth = 0;
4554 RECT rcHeader;
4556 TRACE("nColumn=%d\n", nColumn);
4558 /* we have a 'column' in LIST and REPORT mode only */
4559 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4561 case LVS_LIST:
4562 nColumnWidth = infoPtr->nItemWidth;
4563 break;
4564 case LVS_REPORT:
4565 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return 0;
4566 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4567 nColumnWidth = rcHeader.right - rcHeader.left;
4568 break;
4571 TRACE("nColumnWidth=%d\n", nColumnWidth);
4572 return nColumnWidth;
4575 /***
4576 * DESCRIPTION:
4577 * In list or report display mode, retrieves the number of items that can fit
4578 * vertically in the visible area. In icon or small icon display mode,
4579 * retrieves the total number of visible items.
4581 * PARAMETER(S):
4582 * [I] infoPtr : valid pointer to the listview structure
4584 * RETURN:
4585 * Number of fully visible items.
4587 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4589 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4590 INT nItemCount = 0;
4592 if (uView == LVS_LIST)
4594 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4596 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4597 LISTVIEW_GetCountPerColumn(infoPtr);
4600 else if (uView == LVS_REPORT)
4602 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4604 else
4606 nItemCount = infoPtr->nItemCount;
4609 return nItemCount;
4613 /***
4614 * DESCRIPTION:
4615 * Retrieves an image list handle.
4617 * PARAMETER(S):
4618 * [I] infoPtr : valid pointer to the listview structure
4619 * [I] nImageList : image list identifier
4621 * RETURN:
4622 * SUCCESS : image list handle
4623 * FAILURE : NULL
4625 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4627 HIMAGELIST himl = NULL;
4629 switch (nImageList)
4631 case LVSIL_NORMAL:
4632 himl = infoPtr->himlNormal;
4633 break;
4634 case LVSIL_SMALL:
4635 himl = infoPtr->himlSmall;
4636 break;
4637 case LVSIL_STATE:
4638 himl = infoPtr->himlState;
4639 break;
4642 return (LRESULT)himl;
4645 /* LISTVIEW_GetISearchString */
4647 /***
4648 * DESCRIPTION:
4649 * Retrieves item attributes.
4651 * PARAMETER(S):
4652 * [I] hwnd : window handle
4653 * [IO] lpLVItem : item info
4654 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4655 * if FALSE, the lpLVItem is a LPLVITEMA.
4657 * NOTE:
4658 * This is the internal 'GetItem' interface -- it tries to
4659 * be smart, and avoids text copies, if possible, by modifing
4660 * lpLVItem->pszText to point to the text string. Please note
4661 * that this is not always possible (e.g. OWNERDATA), so on
4662 * entry you *must* supply valid values for pszText, and cchTextMax.
4663 * The only difference to the documented interface is that upon
4664 * return, you should use *only* the lpLVItem->pszText, rather than
4665 * the buffer pointer you provided on input. Most code already does
4666 * that, so it's not a problem.
4667 * For the two cases when the text must be copied (that is,
4668 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4670 * RETURN:
4671 * SUCCESS : TRUE
4672 * FAILURE : FALSE
4674 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4676 NMLVDISPINFOW dispInfo;
4677 LISTVIEW_ITEM *lpItem;
4678 ITEMHDR* pItemHdr;
4679 HDPA hdpaSubItems;
4681 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4683 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4684 return FALSE;
4686 if (lpLVItem->mask == 0) return TRUE;
4688 /* a quick optimization if all we're asked is the focus state
4689 * these queries are worth optimising since they are common,
4690 * and can be answered in constant time, without the heavy accesses */
4691 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
4692 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4694 lpLVItem->state = 0;
4695 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4696 lpLVItem->state |= LVIS_FOCUSED;
4697 return TRUE;
4700 ZeroMemory(&dispInfo, sizeof(dispInfo));
4702 /* if the app stores all the data, handle it separately */
4703 if (infoPtr->dwStyle & LVS_OWNERDATA)
4705 dispInfo.item.state = 0;
4707 /* if we need to callback, do it now */
4708 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4710 /* NOTE: copy only fields which we _know_ are initialized, some apps
4711 * depend on the uninitialized fields being 0 */
4712 dispInfo.item.mask = lpLVItem->mask;
4713 dispInfo.item.iItem = lpLVItem->iItem;
4714 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4715 if (lpLVItem->mask & LVIF_TEXT)
4717 dispInfo.item.pszText = lpLVItem->pszText;
4718 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4720 if (lpLVItem->mask & LVIF_STATE)
4721 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4722 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4723 dispInfo.item.stateMask = lpLVItem->stateMask;
4724 *lpLVItem = dispInfo.item;
4725 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4728 /* we store only a little state, so if we're not asked, we're done */
4729 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4731 /* if focus is handled by us, report it */
4732 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4734 lpLVItem->state &= ~LVIS_FOCUSED;
4735 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4736 lpLVItem->state |= LVIS_FOCUSED;
4739 /* and do the same for selection, if we handle it */
4740 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4742 lpLVItem->state &= ~LVIS_SELECTED;
4743 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4744 lpLVItem->state |= LVIS_SELECTED;
4747 return TRUE;
4750 /* find the item and subitem structures before we proceed */
4751 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4752 if (hdpaSubItems == NULL) return FALSE;
4754 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4755 return FALSE;
4757 if (lpLVItem->iSubItem)
4759 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4760 if(!lpSubItem) return FALSE;
4761 pItemHdr = &lpSubItem->hdr;
4763 else
4764 pItemHdr = &lpItem->hdr;
4766 /* Do we need to query the state from the app? */
4767 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4769 dispInfo.item.mask |= LVIF_STATE;
4770 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4773 /* Do we need to enquire about the image? */
4774 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK)
4775 dispInfo.item.mask |= LVIF_IMAGE;
4777 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
4778 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4780 dispInfo.item.mask |= LVIF_TEXT;
4781 dispInfo.item.pszText = lpLVItem->pszText;
4782 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4783 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4784 *dispInfo.item.pszText = '\0';
4787 /* If we don't have all the requested info, query the application */
4788 if (dispInfo.item.mask != 0)
4790 dispInfo.item.iItem = lpLVItem->iItem;
4791 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4792 dispInfo.item.lParam = lpItem->lParam;
4793 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4794 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4797 /* Now, handle the iImage field */
4798 if (dispInfo.item.mask & LVIF_IMAGE)
4800 lpLVItem->iImage = dispInfo.item.iImage;
4801 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
4802 pItemHdr->iImage = dispInfo.item.iImage;
4804 else if (lpLVItem->mask & LVIF_IMAGE)
4805 lpLVItem->iImage = pItemHdr->iImage;
4807 /* The pszText field */
4808 if (dispInfo.item.mask & LVIF_TEXT)
4810 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4811 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4813 lpLVItem->pszText = dispInfo.item.pszText;
4815 else if (lpLVItem->mask & LVIF_TEXT)
4817 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4818 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4821 /* if this is a subitem, we're done */
4822 if (lpLVItem->iSubItem) return TRUE;
4824 /* Next is the lParam field */
4825 if (dispInfo.item.mask & LVIF_PARAM)
4827 lpLVItem->lParam = dispInfo.item.lParam;
4828 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4829 lpItem->lParam = dispInfo.item.lParam;
4831 else if (lpLVItem->mask & LVIF_PARAM)
4832 lpLVItem->lParam = lpItem->lParam;
4834 /* ... the state field (this one is different due to uCallbackmask) */
4835 if (lpLVItem->mask & LVIF_STATE)
4837 lpLVItem->state = lpItem->state;
4838 if (dispInfo.item.mask & LVIF_STATE)
4840 lpLVItem->state &= ~dispInfo.item.stateMask;
4841 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4843 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4845 lpLVItem->state &= ~LVIS_FOCUSED;
4846 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4847 lpLVItem->state |= LVIS_FOCUSED;
4849 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4851 lpLVItem->state &= ~LVIS_SELECTED;
4852 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4853 lpLVItem->state |= LVIS_SELECTED;
4857 /* and last, but not least, the indent field */
4858 if (lpLVItem->mask & LVIF_INDENT)
4859 lpLVItem->iIndent = lpItem->iIndent;
4861 return TRUE;
4864 /***
4865 * DESCRIPTION:
4866 * Retrieves item attributes.
4868 * PARAMETER(S):
4869 * [I] hwnd : window handle
4870 * [IO] lpLVItem : item info
4871 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4872 * if FALSE, the lpLVItem is a LPLVITEMA.
4874 * NOTE:
4875 * This is the external 'GetItem' interface -- it properly copies
4876 * the text in the provided buffer.
4878 * RETURN:
4879 * SUCCESS : TRUE
4880 * FAILURE : FALSE
4882 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4884 LPWSTR pszText;
4885 BOOL bResult;
4887 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4888 return FALSE;
4890 pszText = lpLVItem->pszText;
4891 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4892 if (bResult && lpLVItem->pszText != pszText)
4893 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4894 lpLVItem->pszText = pszText;
4896 return bResult;
4900 /***
4901 * DESCRIPTION:
4902 * Retrieves the position (upper-left) of the listview control item.
4903 * Note that for LVS_ICON style, the upper-left is that of the icon
4904 * and not the bounding box.
4906 * PARAMETER(S):
4907 * [I] infoPtr : valid pointer to the listview structure
4908 * [I] nItem : item index
4909 * [O] lpptPosition : coordinate information
4911 * RETURN:
4912 * SUCCESS : TRUE
4913 * FAILURE : FALSE
4915 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4917 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4918 POINT Origin;
4920 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4922 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4924 LISTVIEW_GetOrigin(infoPtr, &Origin);
4925 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
4927 if (uView == LVS_ICON)
4929 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
4930 lpptPosition->y += ICON_TOP_PADDING;
4932 lpptPosition->x += Origin.x;
4933 lpptPosition->y += Origin.y;
4935 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
4936 return TRUE;
4940 /***
4941 * DESCRIPTION:
4942 * Retrieves the bounding rectangle for a listview control item.
4944 * PARAMETER(S):
4945 * [I] infoPtr : valid pointer to the listview structure
4946 * [I] nItem : item index
4947 * [IO] lprc : bounding rectangle coordinates
4948 * lprc->left specifies the portion of the item for which the bounding
4949 * rectangle will be retrieved.
4951 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
4952 * including the icon and label.
4954 * * For LVS_ICON
4955 * * Experiment shows that native control returns:
4956 * * width = min (48, length of text line)
4957 * * .left = position.x - (width - iconsize.cx)/2
4958 * * .right = .left + width
4959 * * height = #lines of text * ntmHeight + icon height + 8
4960 * * .top = position.y - 2
4961 * * .bottom = .top + height
4962 * * separation between items .y = itemSpacing.cy - height
4963 * * .x = itemSpacing.cx - width
4964 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
4966 * * For LVS_ICON
4967 * * Experiment shows that native control returns:
4968 * * width = iconSize.cx + 16
4969 * * .left = position.x - (width - iconsize.cx)/2
4970 * * .right = .left + width
4971 * * height = iconSize.cy + 4
4972 * * .top = position.y - 2
4973 * * .bottom = .top + height
4974 * * separation between items .y = itemSpacing.cy - height
4975 * * .x = itemSpacing.cx - width
4976 * LVIR_LABEL Returns the bounding rectangle of the item text.
4978 * * For LVS_ICON
4979 * * Experiment shows that native control returns:
4980 * * width = text length
4981 * * .left = position.x - width/2
4982 * * .right = .left + width
4983 * * height = ntmH * linecount + 2
4984 * * .top = position.y + iconSize.cy + 6
4985 * * .bottom = .top + height
4986 * * separation between items .y = itemSpacing.cy - height
4987 * * .x = itemSpacing.cx - width
4988 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
4989 * rectangles, but excludes columns in report view.
4991 * RETURN:
4992 * SUCCESS : TRUE
4993 * FAILURE : FALSE
4995 * NOTES
4996 * Note that the bounding rectangle of the label in the LVS_ICON view depends
4997 * upon whether the window has the focus currently and on whether the item
4998 * is the one with the focus. Ensure that the control's record of which
4999 * item has the focus agrees with the items' records.
5001 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5003 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5004 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5005 BOOL doLabel = TRUE, oversizedBox = FALSE;
5006 POINT Position, Origin;
5007 LVITEMW lvItem;
5008 RECT label_rect;
5010 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5012 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5014 LISTVIEW_GetOrigin(infoPtr, &Origin);
5015 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5017 /* Be smart and try to figure out the minimum we have to do */
5018 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5019 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5020 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5021 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5022 oversizedBox = TRUE;
5024 /* get what we need from the item before hand, so we make
5025 * only one request. This can speed up things, if data
5026 * is stored on the app side */
5027 lvItem.mask = 0;
5028 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5029 if (doLabel) lvItem.mask |= LVIF_TEXT;
5030 lvItem.iItem = nItem;
5031 lvItem.iSubItem = 0;
5032 lvItem.pszText = szDispText;
5033 lvItem.cchTextMax = DISP_TEXT_SIZE;
5034 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5035 /* we got the state already up, simulate it here, to avoid a reget */
5036 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5038 lvItem.mask |= LVIF_STATE;
5039 lvItem.stateMask = LVIS_FOCUSED;
5040 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5043 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5044 lprc->left = LVIR_BOUNDS;
5045 switch(lprc->left)
5047 case LVIR_ICON:
5048 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5049 break;
5051 case LVIR_LABEL:
5052 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5053 break;
5055 case LVIR_BOUNDS:
5056 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5057 break;
5059 case LVIR_SELECTBOUNDS:
5060 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5061 UnionRect(lprc, lprc, &label_rect);
5062 break;
5064 default:
5065 WARN("Unknown value: %d\n", lprc->left);
5066 return FALSE;
5069 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5071 TRACE(" rect=%s\n", debugrect(lprc));
5073 return TRUE;
5076 /***
5077 * DESCRIPTION:
5078 * Retrieves the spacing between listview control items.
5080 * PARAMETER(S):
5081 * [I] infoPtr : valid pointer to the listview structure
5082 * [IO] lprc : rectangle to receive the output
5083 * on input, lprc->top = nSubItem
5084 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5086 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5087 * not only those of the first column.
5088 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5090 * RETURN:
5091 * TRUE: success
5092 * FALSE: failure
5094 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5096 POINT Position, Origin;
5097 LVITEMW lvItem;
5099 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5101 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5103 LISTVIEW_GetOrigin(infoPtr, &Origin);
5104 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5106 lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5107 lvItem.iItem = nItem;
5108 lvItem.iSubItem = lprc->top;
5110 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5111 switch(lprc->left)
5113 case LVIR_ICON:
5114 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5115 break;
5117 case LVIR_LABEL:
5118 case LVIR_BOUNDS:
5119 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5120 break;
5122 default:
5123 ERR("Unknown bounds=%d\n", lprc->left);
5124 return FALSE;
5127 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5128 return TRUE;
5132 /***
5133 * DESCRIPTION:
5134 * Retrieves the width of a label.
5136 * PARAMETER(S):
5137 * [I] infoPtr : valid pointer to the listview structure
5139 * RETURN:
5140 * SUCCESS : string width (in pixels)
5141 * FAILURE : zero
5143 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5145 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5146 LVITEMW lvItem;
5148 TRACE("(nItem=%d)\n", nItem);
5150 lvItem.mask = LVIF_TEXT;
5151 lvItem.iItem = nItem;
5152 lvItem.iSubItem = 0;
5153 lvItem.pszText = szDispText;
5154 lvItem.cchTextMax = DISP_TEXT_SIZE;
5155 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5157 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5160 /***
5161 * DESCRIPTION:
5162 * Retrieves the spacing between listview control items.
5164 * PARAMETER(S):
5165 * [I] infoPtr : valid pointer to the listview structure
5166 * [I] bSmall : flag for small or large icon
5168 * RETURN:
5169 * Horizontal + vertical spacing
5171 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5173 LONG lResult;
5175 if (!bSmall)
5177 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5179 else
5181 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5182 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5183 else
5184 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5186 return lResult;
5189 /***
5190 * DESCRIPTION:
5191 * Retrieves the state of a listview control item.
5193 * PARAMETER(S):
5194 * [I] infoPtr : valid pointer to the listview structure
5195 * [I] nItem : item index
5196 * [I] uMask : state mask
5198 * RETURN:
5199 * State specified by the mask.
5201 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5203 LVITEMW lvItem;
5205 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5207 lvItem.iItem = nItem;
5208 lvItem.iSubItem = 0;
5209 lvItem.mask = LVIF_STATE;
5210 lvItem.stateMask = uMask;
5211 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5213 return lvItem.state & uMask;
5216 /***
5217 * DESCRIPTION:
5218 * Retrieves the text of a listview control item or subitem.
5220 * PARAMETER(S):
5221 * [I] hwnd : window handle
5222 * [I] nItem : item index
5223 * [IO] lpLVItem : item information
5224 * [I] isW : TRUE if lpLVItem is Unicode
5226 * RETURN:
5227 * SUCCESS : string length
5228 * FAILURE : 0
5230 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5232 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5234 lpLVItem->mask = LVIF_TEXT;
5235 lpLVItem->iItem = nItem;
5236 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5238 return textlenT(lpLVItem->pszText, isW);
5241 /***
5242 * DESCRIPTION:
5243 * Searches for an item based on properties + relationships.
5245 * PARAMETER(S):
5246 * [I] infoPtr : valid pointer to the listview structure
5247 * [I] nItem : item index
5248 * [I] uFlags : relationship flag
5250 * FIXME:
5251 * This function is very, very inefficient! Needs work.
5253 * RETURN:
5254 * SUCCESS : item index
5255 * FAILURE : -1
5257 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5259 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5260 UINT uMask = 0;
5261 LVFINDINFOW lvFindInfo;
5262 INT nCountPerColumn;
5263 INT i;
5265 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5266 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5268 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5270 if (uFlags & LVNI_CUT)
5271 uMask |= LVIS_CUT;
5273 if (uFlags & LVNI_DROPHILITED)
5274 uMask |= LVIS_DROPHILITED;
5276 if (uFlags & LVNI_FOCUSED)
5277 uMask |= LVIS_FOCUSED;
5279 if (uFlags & LVNI_SELECTED)
5280 uMask |= LVIS_SELECTED;
5282 /* if we're asked for the focused item, that's only one,
5283 * so it's worth optimizing */
5284 if (uFlags & LVNI_FOCUSED)
5286 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5287 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5290 if (uFlags & LVNI_ABOVE)
5292 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5294 while (nItem >= 0)
5296 nItem--;
5297 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5298 return nItem;
5301 else
5303 lvFindInfo.flags = LVFI_NEARESTXY;
5304 lvFindInfo.vkDirection = VK_UP;
5305 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5306 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5308 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5309 return nItem;
5313 else if (uFlags & LVNI_BELOW)
5315 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5317 while (nItem < infoPtr->nItemCount)
5319 nItem++;
5320 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5321 return nItem;
5324 else
5326 lvFindInfo.flags = LVFI_NEARESTXY;
5327 lvFindInfo.vkDirection = VK_DOWN;
5328 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5329 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5331 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5332 return nItem;
5336 else if (uFlags & LVNI_TOLEFT)
5338 if (uView == LVS_LIST)
5340 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5341 while (nItem - nCountPerColumn >= 0)
5343 nItem -= nCountPerColumn;
5344 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5345 return nItem;
5348 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5350 lvFindInfo.flags = LVFI_NEARESTXY;
5351 lvFindInfo.vkDirection = VK_LEFT;
5352 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5353 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5355 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5356 return nItem;
5360 else if (uFlags & LVNI_TORIGHT)
5362 if (uView == LVS_LIST)
5364 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5365 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5367 nItem += nCountPerColumn;
5368 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5369 return nItem;
5372 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5374 lvFindInfo.flags = LVFI_NEARESTXY;
5375 lvFindInfo.vkDirection = VK_RIGHT;
5376 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5377 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5379 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5380 return nItem;
5384 else
5386 nItem++;
5388 /* search by index */
5389 for (i = nItem; i < infoPtr->nItemCount; i++)
5391 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5392 return i;
5396 return -1;
5399 /* LISTVIEW_GetNumberOfWorkAreas */
5401 /***
5402 * DESCRIPTION:
5403 * Retrieves the origin coordinates when in icon or small icon display mode.
5405 * PARAMETER(S):
5406 * [I] infoPtr : valid pointer to the listview structure
5407 * [O] lpptOrigin : coordinate information
5409 * RETURN:
5410 * None.
5412 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5414 DWORD lStyle = infoPtr->dwStyle;
5415 UINT uView = lStyle & LVS_TYPEMASK;
5416 INT nHorzPos = 0, nVertPos = 0;
5417 SCROLLINFO scrollInfo;
5419 scrollInfo.cbSize = sizeof(SCROLLINFO);
5420 scrollInfo.fMask = SIF_POS;
5422 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5423 nHorzPos = scrollInfo.nPos;
5424 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5425 nVertPos = scrollInfo.nPos;
5427 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5429 lpptOrigin->x = infoPtr->rcList.left;
5430 lpptOrigin->y = infoPtr->rcList.top;
5431 if (uView == LVS_LIST)
5432 nHorzPos *= infoPtr->nItemWidth;
5433 else if (uView == LVS_REPORT)
5434 nVertPos *= infoPtr->nItemHeight;
5436 lpptOrigin->x -= nHorzPos;
5437 lpptOrigin->y -= nVertPos;
5439 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5442 /***
5443 * DESCRIPTION:
5444 * Retrieves the width of a string.
5446 * PARAMETER(S):
5447 * [I] hwnd : window handle
5448 * [I] lpszText : text string to process
5449 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5451 * RETURN:
5452 * SUCCESS : string width (in pixels)
5453 * FAILURE : zero
5455 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5457 SIZE stringSize;
5459 stringSize.cx = 0;
5460 if (is_textT(lpszText, isW))
5462 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5463 HDC hdc = GetDC(infoPtr->hwndSelf);
5464 HFONT hOldFont = SelectObject(hdc, hFont);
5466 if (isW)
5467 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5468 else
5469 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5470 SelectObject(hdc, hOldFont);
5471 ReleaseDC(infoPtr->hwndSelf, hdc);
5473 return stringSize.cx;
5476 /***
5477 * DESCRIPTION:
5478 * Determines which listview item is located at the specified position.
5480 * PARAMETER(S):
5481 * [I] infoPtr : valid pointer to the listview structure
5482 * [IO] lpht : hit test information
5483 * [I] subitem : fill out iSubItem.
5484 * [I] select : return the index only if the hit selects the item
5486 * NOTE:
5487 * (mm 20001022): We must not allow iSubItem to be touched, for
5488 * an app might pass only a structure with space up to iItem!
5489 * (MS Office 97 does that for instance in the file open dialog)
5491 * RETURN:
5492 * SUCCESS : item index
5493 * FAILURE : -1
5495 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5497 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5498 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5499 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5500 POINT Origin, Position, opt;
5501 LVITEMW lvItem;
5502 ITERATOR i;
5504 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5506 lpht->flags = 0;
5507 lpht->iItem = -1;
5508 if (subitem) lpht->iSubItem = 0;
5510 if (infoPtr->rcList.left > lpht->pt.x)
5511 lpht->flags |= LVHT_TOLEFT;
5512 else if (infoPtr->rcList.right < lpht->pt.x)
5513 lpht->flags |= LVHT_TORIGHT;
5515 if (infoPtr->rcList.top > lpht->pt.y)
5516 lpht->flags |= LVHT_ABOVE;
5517 else if (infoPtr->rcList.bottom < lpht->pt.y)
5518 lpht->flags |= LVHT_BELOW;
5520 TRACE("lpht->flags=0x%x\n", lpht->flags);
5521 if (lpht->flags) return -1;
5523 lpht->flags |= LVHT_NOWHERE;
5525 LISTVIEW_GetOrigin(infoPtr, &Origin);
5527 /* first deal with the large items */
5528 rcSearch.left = lpht->pt.x;
5529 rcSearch.top = lpht->pt.y;
5530 rcSearch.right = rcSearch.left + 1;
5531 rcSearch.bottom = rcSearch.top + 1;
5533 iterator_frameditems(&i, infoPtr, &rcSearch);
5534 iterator_next(&i); /* go to first item in the sequence */
5535 lpht->iItem = i.nItem;
5536 iterator_destroy(&i);
5538 TRACE("lpht->iItem=%d\n", lpht->iItem);
5539 if (lpht->iItem == -1) return -1;
5541 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5542 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5543 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5544 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5545 lvItem.iItem = lpht->iItem;
5546 lvItem.iSubItem = 0;
5547 lvItem.pszText = szDispText;
5548 lvItem.cchTextMax = DISP_TEXT_SIZE;
5549 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5550 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5552 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5553 LISTVIEW_GetItemOrigin(infoPtr, lpht->iItem, &Position);
5554 opt.x = lpht->pt.x - Position.x - Origin.x;
5555 opt.y = lpht->pt.y - Position.y - Origin.y;
5557 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
5558 rcBounds = rcBox;
5559 else
5560 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5561 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5562 if (!PtInRect(&rcBounds, opt)) return -1;
5564 if (PtInRect(&rcIcon, opt))
5565 lpht->flags |= LVHT_ONITEMICON;
5566 else if (PtInRect(&rcLabel, opt))
5567 lpht->flags |= LVHT_ONITEMLABEL;
5568 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5569 lpht->flags |= LVHT_ONITEMSTATEICON;
5570 if (lpht->flags & LVHT_ONITEM)
5571 lpht->flags &= ~LVHT_NOWHERE;
5573 TRACE("lpht->flags=0x%x\n", lpht->flags);
5574 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5576 INT j;
5578 rcBounds.right = rcBounds.left;
5579 for (j = 0; j < infoPtr->hdpaColumns->nItemCount; j++)
5581 rcBounds.left = rcBounds.right;
5582 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5583 if (PtInRect(&rcBounds, opt))
5585 lpht->iSubItem = j;
5586 break;
5591 if (!select || lpht->iItem == -1) return lpht->iItem;
5593 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5595 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5596 return PtInRect(&rcBounds, opt) ? lpht->iItem : -1;
5600 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5601 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5602 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5603 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5604 their own sort proc. when sending LVM_SORTITEMS.
5606 /* Platform SDK:
5607 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5609 LVS_SORTXXX must be specified,
5610 LVS_OWNERDRAW is not set,
5611 <item>.pszText is not LPSTR_TEXTCALLBACK.
5613 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5614 are sorted based on item text..."
5616 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5618 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5619 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5620 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5622 /* if we're sorting descending, negate the return value */
5623 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5626 /***
5627 * nESCRIPTION:
5628 * Inserts a new item in the listview control.
5630 * PARAMETER(S):
5631 * [I] infoPtr : valid pointer to the listview structure
5632 * [I] lpLVItem : item information
5633 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5635 * RETURN:
5636 * SUCCESS : new item index
5637 * FAILURE : -1
5639 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5641 LONG lStyle = infoPtr->dwStyle;
5642 UINT uView = lStyle & LVS_TYPEMASK;
5643 INT nItem = -1;
5644 HDPA hdpaSubItems;
5645 NMLISTVIEW nmlv;
5646 LISTVIEW_ITEM *lpItem;
5647 BOOL is_sorted, has_changed;
5648 LVITEMW item;
5650 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5652 if (lStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
5654 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5655 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
5657 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5659 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5660 return -1;
5662 /* insert item in listview control data structure */
5663 if ( (hdpaSubItems = DPA_Create(8)) )
5664 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5665 if (nItem == -1) goto fail;
5667 /* FIXME: is the handling of this LVS_OWNERDRAWFIXED correct? */
5668 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5669 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5671 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
5672 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
5673 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
5674 if (nItem == -1) goto fail;
5675 infoPtr->nItemCount++;
5677 /* set the item attributes */
5678 item = *lpLVItem;
5679 item.iItem = nItem;
5680 item.state &= ~LVIS_STATEIMAGEMASK;
5681 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
5683 /* if we're sorted, sort the list, and update the index */
5684 if (is_sorted)
5686 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5687 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5688 assert(nItem != -1);
5691 /* make room for the position, if we are in the right mode */
5692 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5694 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5695 goto undo;
5696 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5698 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5699 goto undo;
5703 /* Add the subitem list to the items array. Do this last in case we go to
5704 * fail during the above.
5706 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5708 /* send LVN_INSERTITEM notification */
5709 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5710 nmlv.iItem = nItem;
5711 nmlv.lParam = lpItem->lParam;
5712 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5714 /* align items (set position of each item) */
5715 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5717 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
5718 else LISTVIEW_AlignTop(infoPtr);
5721 LISTVIEW_UpdateScroll(infoPtr);
5723 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
5725 TRACE(" <- %d\n", nItem);
5726 return nItem;
5728 undo:
5729 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5730 infoPtr->nItemCount--;
5731 fail:
5732 DPA_DeletePtr(hdpaSubItems, 0);
5733 DPA_Destroy (hdpaSubItems);
5734 COMCTL32_Free (lpItem);
5735 return -1;
5738 /***
5739 * DESCRIPTION:
5740 * Redraws a range of items.
5742 * PARAMETER(S):
5743 * [I] infoPtr : valid pointer to the listview structure
5744 * [I] nFirst : first item
5745 * [I] nLast : last item
5747 * RETURN:
5748 * SUCCESS : TRUE
5749 * FAILURE : FALSE
5751 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
5753 INT i;
5755 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
5756 max(nFirst, nLast) >= infoPtr->nItemCount)
5757 return FALSE;
5759 for (i = nFirst; i <= nLast; i++)
5760 LISTVIEW_InvalidateItem(infoPtr, i);
5762 return TRUE;
5765 /***
5766 * DESCRIPTION:
5767 * Scroll the content of a listview.
5769 * PARAMETER(S):
5770 * [I] infoPtr : valid pointer to the listview structure
5771 * [I] dx : horizontal scroll amount in pixels
5772 * [I] dy : vertical scroll amount in pixels
5774 * RETURN:
5775 * SUCCESS : TRUE
5776 * FAILURE : FALSE
5778 * COMMENTS:
5779 * If the control is in report mode (LVS_REPORT) the control can
5780 * be scrolled only in line increments. "dy" will be rounded to the
5781 * nearest number of pixels that are a whole line. Ex: if line height
5782 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
5783 * is passed the the scroll will be 0. (per MSDN 7/2002)
5785 * For: (per experimentaion with native control and CSpy ListView)
5786 * LVS_ICON dy=1 = 1 pixel (vertical only)
5787 * dx ignored
5788 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
5789 * dx ignored
5790 * LVS_LIST dx=1 = 1 column (horizontal only)
5791 * but will only scroll 1 column per message
5792 * no matter what the value.
5793 * dy must be 0 or FALSE returned.
5794 * LVS_REPORT dx=1 = 1 pixel
5795 * dy= see above
5798 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
5800 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
5801 case LVS_REPORT:
5802 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
5803 dy /= infoPtr->nItemHeight;
5804 break;
5805 case LVS_LIST:
5806 if (dy != 0) return FALSE;
5807 break;
5808 default: /* icon */
5809 dx = 0;
5810 break;
5813 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
5814 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
5816 return TRUE;
5819 /***
5820 * DESCRIPTION:
5821 * Sets the background color.
5823 * PARAMETER(S):
5824 * [I] infoPtr : valid pointer to the listview structure
5825 * [I] clrBk : background color
5827 * RETURN:
5828 * SUCCESS : TRUE
5829 * FAILURE : FALSE
5831 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
5833 TRACE("(clrBk=%lx)\n", clrBk);
5835 if(infoPtr->clrBk != clrBk) {
5836 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
5837 infoPtr->clrBk = clrBk;
5838 if (clrBk == CLR_NONE)
5839 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
5840 else
5841 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
5842 LISTVIEW_InvalidateList(infoPtr);
5845 return TRUE;
5848 /* LISTVIEW_SetBkImage */
5850 /*** Helper for {Insert,Set}ColumnT *only* */
5851 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5853 if (lpColumn->mask & LVCF_FMT)
5855 /* format member is valid */
5856 lphdi->mask |= HDI_FORMAT;
5858 /* set text alignment (leftmost column must be left-aligned) */
5859 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5860 lphdi->fmt |= HDF_LEFT;
5861 else if (lpColumn->fmt & LVCFMT_RIGHT)
5862 lphdi->fmt |= HDF_RIGHT;
5863 else if (lpColumn->fmt & LVCFMT_CENTER)
5864 lphdi->fmt |= HDF_CENTER;
5866 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5867 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
5869 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5871 lphdi->fmt |= HDF_IMAGE;
5872 lphdi->iImage = I_IMAGECALLBACK;
5876 if (lpColumn->mask & LVCF_WIDTH)
5878 lphdi->mask |= HDI_WIDTH;
5879 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5881 /* make it fill the remainder of the controls width */
5882 RECT rcHeader;
5883 INT item_index;
5885 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5887 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
5888 lphdi->cxy += rcHeader.right - rcHeader.left;
5891 /* retrieve the layout of the header */
5892 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5893 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
5895 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
5897 else
5898 lphdi->cxy = lpColumn->cx;
5901 if (lpColumn->mask & LVCF_TEXT)
5903 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
5904 lphdi->fmt |= HDF_STRING;
5905 lphdi->pszText = lpColumn->pszText;
5906 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
5909 if (lpColumn->mask & LVCF_IMAGE)
5911 lphdi->mask |= HDI_IMAGE;
5912 lphdi->iImage = lpColumn->iImage;
5915 if (lpColumn->mask & LVCF_ORDER)
5917 lphdi->mask |= HDI_ORDER;
5918 lphdi->iOrder = lpColumn->iOrder;
5923 /***
5924 * DESCRIPTION:
5925 * Inserts a new column.
5927 * PARAMETER(S):
5928 * [I] infoPtr : valid pointer to the listview structure
5929 * [I] nColumn : column index
5930 * [I] lpColumn : column information
5931 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
5933 * RETURN:
5934 * SUCCESS : new column index
5935 * FAILURE : -1
5937 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5938 LPLVCOLUMNW lpColumn, BOOL isW)
5940 COLUMN_INFO *lpColumnInfo;
5941 INT nNewColumn;
5942 HDITEMW hdi;
5944 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5946 if (!lpColumn) return -1;
5948 ZeroMemory(&hdi, sizeof(HDITEMW));
5949 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
5951 /* insert item in header control */
5952 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5953 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5954 (WPARAM)nColumn, (LPARAM)&hdi);
5955 if (nNewColumn == -1) return -1;
5957 /* create our own column info */
5958 if (!(lpColumnInfo = COMCTL32_Alloc(sizeof(COLUMN_INFO)))) goto fail;
5959 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
5961 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
5962 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
5964 /* now we have to actually adjust the data */
5965 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
5967 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
5968 HDPA hdpaSubItems;
5969 INT nItem, i;
5971 /* preallocate memory, so we can fail gracefully */
5972 if (nNewColumn == 0)
5974 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->nItemCount);
5975 if (!lpNewItems) goto fail;
5976 for (i = 0; i < infoPtr->nItemCount; i++)
5977 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
5978 if (i != infoPtr->nItemCount)
5980 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5981 COMCTL32_Free(lpNewItems);
5982 goto fail;
5986 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5988 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5989 if (!hdpaSubItems) continue;
5990 for (i = 1; i < hdpaSubItems->nItemCount; i++)
5992 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
5993 if (!lpSubItem) break;
5994 if (lpSubItem->iSubItem >= nNewColumn)
5995 lpSubItem->iSubItem++;
5998 /* if we found our subitem, zapp it */
5999 if (nNewColumn == 0)
6001 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
6002 lpSubItem = lpNewItems[nItem];
6003 lpSubItem->hdr = lpMainItem->hdr;
6004 lpSubItem->iSubItem = 1;
6005 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
6006 lpMainItem->iSubItem = 0;
6007 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
6011 COMCTL32_Free(lpNewItems);
6014 /* make space for the new column */
6015 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6017 return nNewColumn;
6019 fail:
6020 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6021 if (lpColumnInfo)
6023 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6024 COMCTL32_Free(lpColumnInfo);
6026 return -1;
6029 /***
6030 * DESCRIPTION:
6031 * Sets the attributes of a header item.
6033 * PARAMETER(S):
6034 * [I] infoPtr : valid pointer to the listview structure
6035 * [I] nColumn : column index
6036 * [I] lpColumn : column attributes
6037 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6039 * RETURN:
6040 * SUCCESS : TRUE
6041 * FAILURE : FALSE
6043 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6044 LPLVCOLUMNW lpColumn, BOOL isW)
6046 HDITEMW hdi, hdiget;
6047 BOOL bResult;
6049 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6051 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6053 ZeroMemory(&hdi, sizeof(HDITEMW));
6054 if (lpColumn->mask & LVCF_FMT)
6056 hdi.mask |= HDI_FORMAT;
6057 hdiget.mask = HDI_FORMAT;
6058 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6059 hdi.fmt = hdiget.fmt & HDF_STRING;
6061 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6063 /* set header item attributes */
6064 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6065 if (!bResult) return FALSE;
6067 if (lpColumn->mask & LVCF_FMT)
6068 LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt = lpColumn->fmt;
6070 return TRUE;
6073 /***
6074 * DESCRIPTION:
6075 * Sets the column order array
6077 * PARAMETERS:
6078 * [I] infoPtr : valid pointer to the listview structure
6079 * [I] iCount : number of elements in column order array
6080 * [I] lpiArray : pointer to column order array
6082 * RETURN:
6083 * SUCCESS : TRUE
6084 * FAILURE : FALSE
6086 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6088 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6090 if (!lpiArray)
6091 return FALSE;
6093 return TRUE;
6097 /***
6098 * DESCRIPTION:
6099 * Sets the width of a column
6101 * PARAMETERS:
6102 * [I] infoPtr : valid pointer to the listview structure
6103 * [I] nColumn : column index
6104 * [I] cx : column width
6106 * RETURN:
6107 * SUCCESS : TRUE
6108 * FAILURE : FALSE
6110 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6112 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6113 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6114 INT max_cx = 0;
6115 HDITEMW hdi;
6117 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6119 /* set column width only if in report or list mode */
6120 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6122 /* take care of invalid cx values */
6123 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6124 else if (uView == LVS_LIST && cx < 1) return FALSE;
6126 /* resize all columns if in LVS_LIST mode */
6127 if(uView == LVS_LIST)
6129 infoPtr->nItemWidth = cx;
6130 LISTVIEW_InvalidateList(infoPtr);
6131 return TRUE;
6134 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6136 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < infoPtr->hdpaColumns->nItemCount -1))
6138 INT nLabelWidth;
6139 LVITEMW lvItem;
6141 lvItem.mask = LVIF_TEXT;
6142 lvItem.iItem = 0;
6143 lvItem.iSubItem = nColumn;
6144 lvItem.pszText = szDispText;
6145 lvItem.cchTextMax = DISP_TEXT_SIZE;
6146 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6148 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6149 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6150 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6152 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6153 max_cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6154 max_cx += REPORT_MARGINX + TRAILING_PADDING;
6157 /* autosize based on listview items width */
6158 if(cx == LVSCW_AUTOSIZE)
6159 cx = max_cx;
6160 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6162 /* if iCol is the last column make it fill the remainder of the controls width */
6163 if(nColumn == infoPtr->hdpaColumns->nItemCount - 1)
6165 RECT rcHeader;
6166 POINT Origin;
6168 LISTVIEW_GetOrigin(infoPtr, &Origin);
6169 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6171 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6173 else
6175 /* Despite what the MS docs say, if this is not the last
6176 column, then MS resizes the column to the width of the
6177 largest text string in the column, including headers
6178 and items. This is different from LVSCW_AUTOSIZE in that
6179 LVSCW_AUTOSIZE ignores the header string length. */
6180 cx = 0;
6182 /* retrieve header text */
6183 hdi.mask = HDI_TEXT;
6184 hdi.cchTextMax = DISP_TEXT_SIZE;
6185 hdi.pszText = szDispText;
6186 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6188 HDC hdc = GetDC(infoPtr->hwndSelf);
6189 HFONT old_font = SelectObject(hdc, SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6190 SIZE size;
6192 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6193 cx = size.cx;
6194 /* FIXME: Take into account the header image, if one is present */
6195 SelectObject(hdc, old_font);
6196 ReleaseDC(infoPtr->hwndSelf, hdc);
6198 cx = max (cx, max_cx);
6202 if (cx < 0) return FALSE;
6204 /* call header to update the column change */
6205 hdi.mask = HDI_WIDTH;
6206 hdi.cxy = cx;
6207 TRACE("hdi.cxy=%d\n", hdi.cxy);
6208 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6211 /***
6212 * DESCRIPTION:
6213 * Sets the extended listview style.
6215 * PARAMETERS:
6216 * [I] infoPtr : valid pointer to the listview structure
6217 * [I] dwMask : mask
6218 * [I] dwStyle : style
6220 * RETURN:
6221 * SUCCESS : previous style
6222 * FAILURE : 0
6224 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6226 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6228 /* set new style */
6229 if (dwMask)
6230 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6231 else
6232 infoPtr->dwLvExStyle = dwStyle;
6234 return dwOldStyle;
6237 /***
6238 * DESCRIPTION:
6239 * Sets the new hot cursor used during hot tracking and hover selection.
6241 * PARAMETER(S):
6242 * [I] infoPtr : valid pointer to the listview structure
6243 * [I} hCurosr : the new hot cursor handle
6245 * RETURN:
6246 * Returns the previous hot cursor
6248 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6250 HCURSOR oldCursor = infoPtr->hHotCursor;
6252 infoPtr->hHotCursor = hCursor;
6254 return oldCursor;
6258 /***
6259 * DESCRIPTION:
6260 * Sets the hot item index.
6262 * PARAMETERS:
6263 * [I] infoPtr : valid pointer to the listview structure
6264 * [I] iIndex : index
6266 * RETURN:
6267 * SUCCESS : previous hot item index
6268 * FAILURE : -1 (no hot item)
6270 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6272 INT iOldIndex = infoPtr->nHotItem;
6274 infoPtr->nHotItem = iIndex;
6276 return iOldIndex;
6280 /***
6281 * DESCRIPTION:
6282 * Sets the amount of time the cursor must hover over an item before it is selected.
6284 * PARAMETER(S):
6285 * [I] infoPtr : valid pointer to the listview structure
6286 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6288 * RETURN:
6289 * Returns the previous hover time
6291 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6293 DWORD oldHoverTime = infoPtr->dwHoverTime;
6295 infoPtr->dwHoverTime = dwHoverTime;
6297 return oldHoverTime;
6300 /***
6301 * DESCRIPTION:
6302 * Sets spacing for icons of LVS_ICON style.
6304 * PARAMETER(S):
6305 * [I] infoPtr : valid pointer to the listview structure
6306 * [I] spacing : MAKELONG(cx, cy)
6308 * RETURN:
6309 * MAKELONG(oldcx, oldcy)
6311 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6313 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6314 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6315 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6317 TRACE("requested=(%d,%d)\n", cx, cy);
6319 /* this is supported only for LVS_ICON style */
6320 if (uView != LVS_ICON) return oldspacing;
6322 /* set to defaults, if instructed to */
6323 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6324 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6326 /* if 0 then compute width
6327 * FIXME: Should scan each item and determine max width of
6328 * icon or label, then make that the width */
6329 if (cx == 0)
6330 cx = infoPtr->iconSpacing.cx;
6332 /* if 0 then compute height */
6333 if (cy == 0)
6334 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6335 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6338 infoPtr->iconSpacing.cx = cx;
6339 infoPtr->iconSpacing.cy = cy;
6341 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6342 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6343 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6344 infoPtr->ntmHeight);
6346 /* these depend on the iconSpacing */
6347 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6348 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6350 return oldspacing;
6353 inline void update_icon_size(HIMAGELIST himl, BOOL small, SIZE *size)
6355 INT cx, cy;
6357 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6359 size->cx = cx;
6360 size->cy = cy;
6362 else
6364 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6365 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6369 /***
6370 * DESCRIPTION:
6371 * Sets image lists.
6373 * PARAMETER(S):
6374 * [I] infoPtr : valid pointer to the listview structure
6375 * [I] nType : image list type
6376 * [I] himl : image list handle
6378 * RETURN:
6379 * SUCCESS : old image list
6380 * FAILURE : NULL
6382 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6384 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6385 INT oldHeight = infoPtr->nItemHeight;
6386 HIMAGELIST himlOld = 0;
6388 switch (nType)
6390 case LVSIL_NORMAL:
6391 himlOld = infoPtr->himlNormal;
6392 infoPtr->himlNormal = himl;
6393 if (uView == LVS_ICON) update_icon_size(himl, FALSE, &infoPtr->iconSize);
6394 LISTVIEW_SetIconSpacing(infoPtr, 0);
6395 break;
6397 case LVSIL_SMALL:
6398 himlOld = infoPtr->himlSmall;
6399 infoPtr->himlSmall = himl;
6400 if (uView != LVS_ICON) update_icon_size(himl, TRUE, &infoPtr->iconSize);
6401 break;
6403 case LVSIL_STATE:
6404 himlOld = infoPtr->himlState;
6405 infoPtr->himlState = himl;
6406 update_icon_size(himl, TRUE, &infoPtr->iconStateSize);
6407 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6408 break;
6410 default:
6411 ERR("Unknown icon type=%d\n", nType);
6412 return NULL;
6415 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6416 if (infoPtr->nItemHeight != oldHeight)
6417 LISTVIEW_UpdateScroll(infoPtr);
6419 return himlOld;
6422 /***
6423 * DESCRIPTION:
6424 * Preallocates memory (does *not* set the actual count of items !)
6426 * PARAMETER(S):
6427 * [I] infoPtr : valid pointer to the listview structure
6428 * [I] nItems : item count (projected number of items to allocate)
6429 * [I] dwFlags : update flags
6431 * RETURN:
6432 * SUCCESS : TRUE
6433 * FAILURE : FALSE
6435 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6437 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6439 if (infoPtr->dwStyle & LVS_OWNERDATA)
6441 int precount,topvisible;
6443 TRACE("LVS_OWNERDATA is set!\n");
6444 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6445 FIXME("flags %s %s not implemented\n",
6446 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6447 : "",
6448 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6450 LISTVIEW_DeselectAll(infoPtr);
6452 precount = infoPtr->nItemCount;
6453 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6454 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6456 infoPtr->nItemCount = nItems;
6457 infoPtr->nItemWidth = max(LISTVIEW_CalculateMaxWidth(infoPtr),
6458 DEFAULT_COLUMN_WIDTH);
6460 LISTVIEW_UpdateSize(infoPtr);
6461 LISTVIEW_UpdateScroll(infoPtr);
6463 if (min(precount,infoPtr->nItemCount) < topvisible)
6464 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6466 else
6468 /* According to MSDN for non-LVS_OWNERDATA this is just
6469 * a performance issue. The control allocates its internal
6470 * data structures for the number of items specified. It
6471 * cuts down on the number of memory allocations. Therefore
6472 * we will just issue a WARN here
6474 WARN("for non-ownerdata performance option not implemented.\n");
6477 return TRUE;
6480 /***
6481 * DESCRIPTION:
6482 * Sets the position of an item.
6484 * PARAMETER(S):
6485 * [I] infoPtr : valid pointer to the listview structure
6486 * [I] nItem : item index
6487 * [I] pt : coordinate
6489 * RETURN:
6490 * SUCCESS : TRUE
6491 * FAILURE : FALSE
6493 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6495 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6496 POINT old;
6498 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6500 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6501 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6503 /* This point value seems to be an undocumented feature.
6504 * The best guess is that it means either at the origin,
6505 * or at true beginning of the list. I will assume the origin. */
6506 if ((pt.x == -1) && (pt.y == -1))
6507 LISTVIEW_GetOrigin(infoPtr, &pt);
6508 else if (uView == LVS_ICON)
6510 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6511 pt.y -= ICON_TOP_PADDING;
6514 /* save the old position */
6515 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
6516 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
6518 /* Is the position changing? */
6519 if (pt.x == old.x && pt.y == old.y) return TRUE;
6521 /* FIXME: shouldn't we invalidate, as the item moved? */
6523 /* Allocating a POINTER for every item is too resource intensive,
6524 * so we'll keep the (x,y) in different arrays */
6525 if (DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)pt.x) &&
6526 DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)pt.y) )
6527 return TRUE;
6529 ERR("We should never fail here (nItem=%d, pt=%s), please report.\n",
6530 nItem, debugpoint(&pt));
6531 return FALSE;
6534 /***
6535 * DESCRIPTION:
6536 * Sets the state of one or many items.
6538 * PARAMETER(S):
6539 * [I] infoPtr : valid pointer to the listview structure
6540 * [I] nItem : item index
6541 * [I] lpLVItem : item or subitem info
6543 * RETURN:
6544 * SUCCESS : TRUE
6545 * FAILURE : FALSE
6547 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6549 BOOL bResult = TRUE;
6550 LVITEMW lvItem;
6552 lvItem.iItem = nItem;
6553 lvItem.iSubItem = 0;
6554 lvItem.mask = LVIF_STATE;
6555 lvItem.state = lpLVItem->state;
6556 lvItem.stateMask = lpLVItem->stateMask;
6557 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6559 if (nItem == -1)
6561 /* apply to all items */
6562 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6563 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6565 else
6566 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6568 return bResult;
6571 /***
6572 * DESCRIPTION:
6573 * Sets the text of an item or subitem.
6575 * PARAMETER(S):
6576 * [I] hwnd : window handle
6577 * [I] nItem : item index
6578 * [I] lpLVItem : item or subitem info
6579 * [I] isW : TRUE if input is Unicode
6581 * RETURN:
6582 * SUCCESS : TRUE
6583 * FAILURE : FALSE
6585 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6587 LVITEMW lvItem;
6589 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6591 lvItem.iItem = nItem;
6592 lvItem.iSubItem = lpLVItem->iSubItem;
6593 lvItem.mask = LVIF_TEXT;
6594 lvItem.pszText = lpLVItem->pszText;
6595 lvItem.cchTextMax = lpLVItem->cchTextMax;
6597 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6599 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6602 /***
6603 * DESCRIPTION:
6604 * Set item index that marks the start of a multiple selection.
6606 * PARAMETER(S):
6607 * [I] infoPtr : valid pointer to the listview structure
6608 * [I] nIndex : index
6610 * RETURN:
6611 * Index number or -1 if there is no selection mark.
6613 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6615 INT nOldIndex = infoPtr->nSelectionMark;
6617 TRACE("(nIndex=%d)\n", nIndex);
6619 infoPtr->nSelectionMark = nIndex;
6621 return nOldIndex;
6624 /***
6625 * DESCRIPTION:
6626 * Sets the text background color.
6628 * PARAMETER(S):
6629 * [I] infoPtr : valid pointer to the listview structure
6630 * [I] clrTextBk : text background color
6632 * RETURN:
6633 * SUCCESS : TRUE
6634 * FAILURE : FALSE
6636 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6638 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6640 if (infoPtr->clrTextBk != clrTextBk)
6642 infoPtr->clrTextBk = clrTextBk;
6643 LISTVIEW_InvalidateList(infoPtr);
6646 return TRUE;
6649 /***
6650 * DESCRIPTION:
6651 * Sets the text foreground color.
6653 * PARAMETER(S):
6654 * [I] infoPtr : valid pointer to the listview structure
6655 * [I] clrText : text color
6657 * RETURN:
6658 * SUCCESS : TRUE
6659 * FAILURE : FALSE
6661 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6663 TRACE("(clrText=%lx)\n", clrText);
6665 if (infoPtr->clrText != clrText)
6667 infoPtr->clrText = clrText;
6668 LISTVIEW_InvalidateList(infoPtr);
6671 return TRUE;
6674 /* LISTVIEW_SetToolTips */
6675 /* LISTVIEW_SetUnicodeFormat */
6676 /* LISTVIEW_SetWorkAreas */
6678 /***
6679 * DESCRIPTION:
6680 * Callback internally used by LISTVIEW_SortItems()
6682 * PARAMETER(S):
6683 * [I] first : pointer to first LISTVIEW_ITEM to compare
6684 * [I] second : pointer to second LISTVIEW_ITEM to compare
6685 * [I] lParam : HWND of control
6687 * RETURN:
6688 * if first comes before second : negative
6689 * if first comes after second : positive
6690 * if first and second are equivalent : zero
6692 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6694 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6695 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6696 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6698 /* Forward the call to the client defined callback */
6699 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6702 /***
6703 * DESCRIPTION:
6704 * Sorts the listview items.
6706 * PARAMETER(S):
6707 * [I] infoPtr : valid pointer to the listview structure
6708 * [I] pfnCompare : application-defined value
6709 * [I] lParamSort : pointer to comparision callback
6711 * RETURN:
6712 * SUCCESS : TRUE
6713 * FAILURE : FALSE
6715 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6717 UINT lStyle = infoPtr->dwStyle;
6718 HDPA hdpaSubItems;
6719 LISTVIEW_ITEM *lpItem;
6720 LPVOID selectionMarkItem;
6721 LVITEMW item;
6722 int i;
6724 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6726 if (lStyle & LVS_OWNERDATA) return FALSE;
6728 if (!infoPtr->hdpaItems) return FALSE;
6730 /* if there are 0 or 1 items, there is no need to sort */
6731 if (infoPtr->nItemCount < 2) return TRUE;
6733 if (infoPtr->nFocusedItem >= 0)
6735 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6736 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6737 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6739 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
6740 /* clear the lpItem->state for non-selected ones */
6741 /* remove the selection ranges */
6743 infoPtr->pfnCompare = pfnCompare;
6744 infoPtr->lParamSort = lParamSort;
6745 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6747 /* Adjust selections and indices so that they are the way they should
6748 * be after the sort (otherwise, the list items move around, but
6749 * whatever is at the item's previous original position will be
6750 * selected instead)
6752 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6753 for (i=0; i < infoPtr->nItemCount; i++)
6755 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6756 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6758 if (lpItem->state & LVIS_SELECTED)
6760 item.state = LVIS_SELECTED;
6761 item.stateMask = LVIS_SELECTED;
6762 LISTVIEW_SetItemState(infoPtr, i, &item);
6764 if (lpItem->state & LVIS_FOCUSED)
6766 infoPtr->nFocusedItem = i;
6767 lpItem->state &= ~LVIS_FOCUSED;
6770 if (selectionMarkItem != NULL)
6771 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6772 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6774 /* align the items */
6775 LISTVIEW_AlignTop(infoPtr);
6777 /* refresh the display */
6778 LISTVIEW_InvalidateList(infoPtr); /* FIXME: display should not change for [SMALL]ICON view */
6780 return TRUE;
6783 /***
6784 * DESCRIPTION:
6785 * Updates an items or rearranges the listview control.
6787 * PARAMETER(S):
6788 * [I] infoPtr : valid pointer to the listview structure
6789 * [I] nItem : item index
6791 * RETURN:
6792 * SUCCESS : TRUE
6793 * FAILURE : FALSE
6795 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
6797 LONG lStyle = infoPtr->dwStyle;
6798 UINT uView = lStyle & LVS_TYPEMASK;
6800 TRACE("(nItem=%d)\n", nItem);
6802 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6804 /* rearrange with default alignment style */
6805 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
6806 LISTVIEW_Arrange(infoPtr, 0);
6807 else
6808 LISTVIEW_InvalidateItem(infoPtr, nItem);
6810 return TRUE;
6814 /***
6815 * DESCRIPTION:
6816 * Creates the listview control.
6818 * PARAMETER(S):
6819 * [I] hwnd : window handle
6820 * [I] lpcs : the create parameters
6822 * RETURN:
6823 * Success: 0
6824 * Failure: -1
6826 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
6828 LISTVIEW_INFO *infoPtr;
6829 UINT uView = lpcs->style & LVS_TYPEMASK;
6830 LOGFONTW logFont;
6832 TRACE("(lpcs=%p)\n", lpcs);
6834 /* initialize info pointer */
6835 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
6836 if (!infoPtr) return -1;
6838 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
6840 infoPtr->hwndSelf = hwnd;
6841 infoPtr->dwStyle = lpcs->style;
6842 /* determine the type of structures to use */
6843 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
6844 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
6846 /* initialize color information */
6847 infoPtr->clrBk = CLR_NONE;
6848 infoPtr->clrText = comctl32_color.clrWindowText;
6849 infoPtr->clrTextBk = CLR_DEFAULT;
6850 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
6852 /* set default values */
6853 infoPtr->nFocusedItem = -1;
6854 infoPtr->nSelectionMark = -1;
6855 infoPtr->nHotItem = -1;
6856 infoPtr->bRedraw = TRUE;
6857 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
6858 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
6859 infoPtr->nEditLabelItem = -1;
6861 /* get default font (icon title) */
6862 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
6863 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
6864 infoPtr->hFont = infoPtr->hDefaultFont;
6865 LISTVIEW_SaveTextMetrics(infoPtr);
6867 /* create header */
6868 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
6869 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
6870 0, 0, 0, 0, hwnd, (HMENU)0,
6871 lpcs->hInstance, NULL);
6873 /* set header unicode format */
6874 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
6876 /* set header font */
6877 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
6879 /* allocate memory for the selection ranges */
6880 if (!(infoPtr->selectionRanges = ranges_create(10))) return -1;
6882 infoPtr->hdpaColumns = DPA_Create(10);
6884 if (uView == LVS_ICON)
6886 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
6887 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
6889 else if (uView == LVS_REPORT)
6891 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
6893 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6895 else
6897 /* set HDS_HIDDEN flag to hide the header bar */
6898 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
6899 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
6903 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6904 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6906 else
6908 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6909 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6912 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
6913 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
6915 /* display unsupported listview window styles */
6916 LISTVIEW_UnsupportedStyles(lpcs->style);
6918 /* allocate memory for the data structure */
6919 infoPtr->hdpaItems = DPA_Create(10);
6920 infoPtr->hdpaPosX = DPA_Create(10);
6921 infoPtr->hdpaPosY = DPA_Create(10);
6923 /* initialize size of items */
6924 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6925 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6927 /* initialize the hover time to -1(indicating the default system hover time) */
6928 infoPtr->dwHoverTime = -1;
6930 return 0;
6933 /***
6934 * DESCRIPTION:
6935 * Erases the background of the listview control.
6937 * PARAMETER(S):
6938 * [I] infoPtr : valid pointer to the listview structure
6939 * [I] hdc : device context handle
6941 * RETURN:
6942 * SUCCESS : TRUE
6943 * FAILURE : FALSE
6945 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
6947 RECT rc;
6949 TRACE("(hdc=%x)\n", hdc);
6951 if (!GetClipBox(hdc, &rc)) return FALSE;
6953 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
6957 /***
6958 * DESCRIPTION:
6959 * Helper function for LISTVIEW_[HV]Scroll *only*.
6960 * Performs vertical/horizontal scrolling by a give amount.
6962 * PARAMETER(S):
6963 * [I] infoPtr : valid pointer to the listview structure
6964 * [I] dx : amount of horizontal scroll
6965 * [I] dy : amount of vertical scroll
6967 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6969 /* now we can scroll the list */
6970 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
6971 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
6972 /* if we have focus, adjust rect */
6973 OffsetRect(&infoPtr->rcFocus, dx, dy);
6974 UpdateWindow(infoPtr->hwndSelf);
6977 /***
6978 * DESCRIPTION:
6979 * Performs vertical scrolling.
6981 * PARAMETER(S):
6982 * [I] infoPtr : valid pointer to the listview structure
6983 * [I] nScrollCode : scroll code
6984 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
6985 * [I] hScrollWnd : scrollbar control window handle
6987 * RETURN:
6988 * Zero
6990 * NOTES:
6991 * SB_LINEUP/SB_LINEDOWN:
6992 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
6993 * for LVS_REPORT is 1 line
6994 * for LVS_LIST cannot occur
6997 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
6998 INT nScrollDiff, HWND hScrollWnd)
7000 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7001 INT nOldScrollPos, nNewScrollPos;
7002 SCROLLINFO scrollInfo;
7003 BOOL is_an_icon;
7005 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7007 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7009 scrollInfo.cbSize = sizeof(SCROLLINFO);
7010 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7012 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7014 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7016 nOldScrollPos = scrollInfo.nPos;
7017 switch (nScrollCode)
7019 case SB_INTERNAL:
7020 break;
7022 case SB_LINEUP:
7023 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7024 break;
7026 case SB_LINEDOWN:
7027 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7028 break;
7030 case SB_PAGEUP:
7031 nScrollDiff = -scrollInfo.nPage;
7032 break;
7034 case SB_PAGEDOWN:
7035 nScrollDiff = scrollInfo.nPage;
7036 break;
7038 case SB_THUMBPOSITION:
7039 case SB_THUMBTRACK:
7040 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7041 break;
7043 default:
7044 nScrollDiff = 0;
7047 /* quit right away if pos isn't changing */
7048 if (nScrollDiff == 0) return 0;
7050 /* calculate new position, and handle overflows */
7051 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7052 if (nScrollDiff > 0) {
7053 if (nNewScrollPos < nOldScrollPos ||
7054 nNewScrollPos > scrollInfo.nMax)
7055 nNewScrollPos = scrollInfo.nMax;
7056 } else {
7057 if (nNewScrollPos > nOldScrollPos ||
7058 nNewScrollPos < scrollInfo.nMin)
7059 nNewScrollPos = scrollInfo.nMin;
7062 /* set the new position, and reread in case it changed */
7063 scrollInfo.fMask = SIF_POS;
7064 scrollInfo.nPos = nNewScrollPos;
7065 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7067 /* carry on only if it really changed */
7068 if (nNewScrollPos == nOldScrollPos) return 0;
7070 /* now adjust to client coordinates */
7071 nScrollDiff = nOldScrollPos - nNewScrollPos;
7072 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7074 /* and scroll the window */
7075 scroll_list(infoPtr, 0, nScrollDiff);
7077 return 0;
7080 /***
7081 * DESCRIPTION:
7082 * Performs horizontal scrolling.
7084 * PARAMETER(S):
7085 * [I] infoPtr : valid pointer to the listview structure
7086 * [I] nScrollCode : scroll code
7087 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7088 * [I] hScrollWnd : scrollbar control window handle
7090 * RETURN:
7091 * Zero
7093 * NOTES:
7094 * SB_LINELEFT/SB_LINERIGHT:
7095 * for LVS_ICON, LVS_SMALLICON 1 pixel
7096 * for LVS_REPORT is 1 pixel
7097 * for LVS_LIST is 1 column --> which is a 1 because the
7098 * scroll is based on columns not pixels
7101 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7102 INT nScrollDiff, HWND hScrollWnd)
7104 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7105 INT nOldScrollPos, nNewScrollPos;
7106 SCROLLINFO scrollInfo;
7108 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7110 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7112 scrollInfo.cbSize = sizeof(SCROLLINFO);
7113 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7115 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7117 nOldScrollPos = scrollInfo.nPos;
7119 switch (nScrollCode)
7121 case SB_INTERNAL:
7122 break;
7124 case SB_LINELEFT:
7125 nScrollDiff = -1;
7126 break;
7128 case SB_LINERIGHT:
7129 nScrollDiff = 1;
7130 break;
7132 case SB_PAGELEFT:
7133 nScrollDiff = -scrollInfo.nPage;
7134 break;
7136 case SB_PAGERIGHT:
7137 nScrollDiff = scrollInfo.nPage;
7138 break;
7140 case SB_THUMBPOSITION:
7141 case SB_THUMBTRACK:
7142 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7143 break;
7145 default:
7146 nScrollDiff = 0;
7149 /* quit right away if pos isn't changing */
7150 if (nScrollDiff == 0) return 0;
7152 /* calculate new position, and handle overflows */
7153 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7154 if (nScrollDiff > 0) {
7155 if (nNewScrollPos < nOldScrollPos ||
7156 nNewScrollPos > scrollInfo.nMax)
7157 nNewScrollPos = scrollInfo.nMax;
7158 } else {
7159 if (nNewScrollPos > nOldScrollPos ||
7160 nNewScrollPos < scrollInfo.nMin)
7161 nNewScrollPos = scrollInfo.nMin;
7164 /* set the new position, and reread in case it changed */
7165 scrollInfo.fMask = SIF_POS;
7166 scrollInfo.nPos = nNewScrollPos;
7167 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7169 /* carry on only if it really changed */
7170 if (nNewScrollPos == nOldScrollPos) return 0;
7172 if(uView == LVS_REPORT)
7173 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7175 /* now adjust to client coordinates */
7176 nScrollDiff = nOldScrollPos - nNewScrollPos;
7177 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7179 /* and scroll the window */
7180 scroll_list(infoPtr, nScrollDiff, 0);
7182 return 0;
7185 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7187 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7188 INT gcWheelDelta = 0;
7189 UINT pulScrollLines = 3;
7190 SCROLLINFO scrollInfo;
7192 TRACE("(wheelDelta=%d)\n", wheelDelta);
7194 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7195 gcWheelDelta -= wheelDelta;
7197 scrollInfo.cbSize = sizeof(SCROLLINFO);
7198 scrollInfo.fMask = SIF_POS;
7200 switch(uView)
7202 case LVS_ICON:
7203 case LVS_SMALLICON:
7205 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7206 * should be fixed in the future.
7208 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7209 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7210 scrollInfo.nPos + (gcWheelDelta < 0) ?
7211 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7212 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7213 break;
7215 case LVS_REPORT:
7216 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7218 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7220 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7221 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7222 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7225 break;
7227 case LVS_LIST:
7228 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7229 break;
7231 return 0;
7234 /***
7235 * DESCRIPTION:
7236 * ???
7238 * PARAMETER(S):
7239 * [I] infoPtr : valid pointer to the listview structure
7240 * [I] nVirtualKey : virtual key
7241 * [I] lKeyData : key data
7243 * RETURN:
7244 * Zero
7246 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7248 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7249 INT nItem = -1;
7250 NMLVKEYDOWN nmKeyDown;
7252 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7254 /* send LVN_KEYDOWN notification */
7255 nmKeyDown.wVKey = nVirtualKey;
7256 nmKeyDown.flags = 0;
7257 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7259 switch (nVirtualKey)
7261 case VK_RETURN:
7262 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7264 notify(infoPtr, NM_RETURN);
7265 notify(infoPtr, LVN_ITEMACTIVATE);
7267 break;
7269 case VK_HOME:
7270 if (infoPtr->nItemCount > 0)
7271 nItem = 0;
7272 break;
7274 case VK_END:
7275 if (infoPtr->nItemCount > 0)
7276 nItem = infoPtr->nItemCount - 1;
7277 break;
7279 case VK_LEFT:
7280 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7281 break;
7283 case VK_UP:
7284 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7285 break;
7287 case VK_RIGHT:
7288 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7289 break;
7291 case VK_DOWN:
7292 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7293 break;
7295 case VK_PRIOR:
7296 if (uView == LVS_REPORT)
7297 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7298 else
7299 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7300 * LISTVIEW_GetCountPerRow(infoPtr);
7301 if(nItem < 0) nItem = 0;
7302 break;
7304 case VK_NEXT:
7305 if (uView == LVS_REPORT)
7306 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7307 else
7308 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7309 * LISTVIEW_GetCountPerRow(infoPtr);
7310 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7311 break;
7314 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7315 LISTVIEW_KeySelection(infoPtr, nItem);
7317 return 0;
7320 /***
7321 * DESCRIPTION:
7322 * Kills the focus.
7324 * PARAMETER(S):
7325 * [I] infoPtr : valid pointer to the listview structure
7327 * RETURN:
7328 * Zero
7330 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7332 TRACE("()\n");
7334 /* if we did not have the focus, there's nothing to do */
7335 if (!infoPtr->bFocus) return 0;
7337 /* send NM_KILLFOCUS notification */
7338 notify(infoPtr, NM_KILLFOCUS);
7340 /* if we have a focus rectagle, get rid of it */
7341 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7343 /* set window focus flag */
7344 infoPtr->bFocus = FALSE;
7346 /* invalidate the selected items before reseting focus flag */
7347 LISTVIEW_InvalidateSelectedItems(infoPtr);
7349 return 0;
7352 /***
7353 * DESCRIPTION:
7354 * Processes double click messages (left mouse button).
7356 * PARAMETER(S):
7357 * [I] infoPtr : valid pointer to the listview structure
7358 * [I] wKey : key flag
7359 * [I] pts : mouse coordinate
7361 * RETURN:
7362 * Zero
7364 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7366 LVHITTESTINFO htInfo;
7368 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7370 /* send NM_RELEASEDCAPTURE notification */
7371 notify(infoPtr, NM_RELEASEDCAPTURE);
7373 htInfo.pt.x = pts.x;
7374 htInfo.pt.y = pts.y;
7376 /* send NM_DBLCLK notification */
7377 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7378 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7380 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7381 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7383 return 0;
7386 /***
7387 * DESCRIPTION:
7388 * Processes mouse down messages (left mouse button).
7390 * PARAMETER(S):
7391 * [I] infoPtr : valid pointer to the listview structure
7392 * [I] wKey : key flag
7393 * [I] pts : mouse coordinate
7395 * RETURN:
7396 * Zero
7398 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7400 LVHITTESTINFO lvHitTestInfo;
7401 LONG lStyle = infoPtr->dwStyle;
7402 static BOOL bGroupSelect = TRUE;
7403 POINT pt = { pts.x, pts.y };
7404 INT nItem;
7406 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7408 /* send NM_RELEASEDCAPTURE notification */
7409 notify(infoPtr, NM_RELEASEDCAPTURE);
7411 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7413 /* set left button down flag */
7414 infoPtr->bLButtonDown = TRUE;
7416 lvHitTestInfo.pt.x = pts.x;
7417 lvHitTestInfo.pt.y = pts.y;
7419 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7420 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7421 infoPtr->nEditLabelItem = -1;
7422 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7424 if (lStyle & LVS_SINGLESEL)
7426 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7427 infoPtr->nEditLabelItem = nItem;
7428 else
7429 LISTVIEW_SetSelection(infoPtr, nItem);
7431 else
7433 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7435 if (bGroupSelect)
7436 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7437 else
7439 LVITEMW item;
7441 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7442 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7444 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7445 infoPtr->nSelectionMark = nItem;
7448 else if (wKey & MK_CONTROL)
7450 LVITEMW item;
7452 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7454 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7455 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7456 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7457 infoPtr->nSelectionMark = nItem;
7459 else if (wKey & MK_SHIFT)
7461 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7463 else
7465 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7466 infoPtr->nEditLabelItem = nItem;
7468 /* set selection (clears other pre-existing selections) */
7469 LISTVIEW_SetSelection(infoPtr, nItem);
7473 else
7475 /* remove all selections */
7476 LISTVIEW_DeselectAll(infoPtr);
7479 return 0;
7482 /***
7483 * DESCRIPTION:
7484 * Processes mouse up messages (left mouse button).
7486 * PARAMETER(S):
7487 * [I] infoPtr : valid pointer to the listview structure
7488 * [I] wKey : key flag
7489 * [I] pts : mouse coordinate
7491 * RETURN:
7492 * Zero
7494 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7496 LVHITTESTINFO lvHitTestInfo;
7498 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7500 if (!infoPtr->bLButtonDown) return 0;
7502 lvHitTestInfo.pt.x = pts.x;
7503 lvHitTestInfo.pt.y = pts.y;
7505 /* send NM_CLICK notification */
7506 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7507 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7509 /* set left button flag */
7510 infoPtr->bLButtonDown = FALSE;
7512 /* if we clicked on a selected item, edit the label */
7513 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7514 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7516 return 0;
7519 /***
7520 * DESCRIPTION:
7521 * Destroys the listview control (called after WM_DESTROY).
7523 * PARAMETER(S):
7524 * [I] infoPtr : valid pointer to the listview structure
7526 * RETURN:
7527 * Zero
7529 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7531 LONG lStyle = infoPtr->dwStyle;
7533 TRACE("()\n");
7535 /* delete all items */
7536 LISTVIEW_DeleteAllItems(infoPtr);
7538 /* destroy data structure */
7539 DPA_Destroy(infoPtr->hdpaItems);
7540 ranges_destroy(infoPtr->selectionRanges);
7542 /* destroy image lists */
7543 if (!(lStyle & LVS_SHAREIMAGELISTS))
7545 if (infoPtr->himlNormal)
7546 ImageList_Destroy(infoPtr->himlNormal);
7547 if (infoPtr->himlSmall)
7548 ImageList_Destroy(infoPtr->himlSmall);
7549 if (infoPtr->himlState)
7550 ImageList_Destroy(infoPtr->himlState);
7553 /* destroy font, bkgnd brush */
7554 infoPtr->hFont = 0;
7555 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7556 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7558 /* free listview info pointer*/
7559 COMCTL32_Free(infoPtr);
7561 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7562 return 0;
7565 /***
7566 * DESCRIPTION:
7567 * Handles notifications from children.
7569 * PARAMETER(S):
7570 * [I] infoPtr : valid pointer to the listview structure
7571 * [I] nCtrlId : control identifier
7572 * [I] lpnmh : notification information
7574 * RETURN:
7575 * Zero
7577 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7579 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7581 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7583 /* handle notification from header control */
7584 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7586 LPNMHEADERW lphnm = (LPNMHEADERW)lpnmh;
7588 if (lpnmh->code == HDN_TRACKW || lpnmh->code == HDN_TRACKA ||
7589 lpnmh->code == HDN_ITEMCHANGEDW || lpnmh->code == HDN_ITEMCHANGEDA)
7591 COLUMN_INFO *lpColumnInfo;
7592 RECT rcCol;
7593 INT dx;
7595 if (lphnm->iItem < 0 || lphnm->iItem >= infoPtr->hdpaColumns->nItemCount) return 0;
7596 if (!lphnm->pitem || !(lphnm->pitem->mask & HDI_WIDTH)) return 0;
7598 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lphnm->iItem);
7600 /* determine how much we change since the last know position */
7601 dx = lphnm->pitem->cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7603 /* ajust the column being tracked */
7604 lpColumnInfo->rcHeader.right += dx;
7606 /* compute the rectangle for the tracked column */
7607 rcCol.left = lpColumnInfo->rcHeader.left;
7608 rcCol.top = infoPtr->rcList.top;
7609 rcCol.right = lpColumnInfo->rcHeader.right;
7610 rcCol.bottom = infoPtr->rcList.bottom;
7612 LISTVIEW_ScrollColumns(infoPtr, lphnm->iItem + 1, dx);
7613 if (uView == LVS_REPORT) LISTVIEW_InvalidateRect(infoPtr, &rcCol);
7615 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7617 /* Handle sorting by Header Column */
7618 NMLISTVIEW nmlv;
7620 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7621 nmlv.iItem = -1;
7622 nmlv.iSubItem = lphnm->iItem;
7623 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7627 return 0;
7630 /***
7631 * DESCRIPTION:
7632 * Determines the type of structure to use.
7634 * PARAMETER(S):
7635 * [I] infoPtr : valid pointer to the listview structureof the sender
7636 * [I] hwndFrom : listview window handle
7637 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
7639 * RETURN:
7640 * Zero
7642 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7644 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7646 if (nCommand == NF_REQUERY)
7647 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7648 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7649 return 0;
7652 /***
7653 * DESCRIPTION:
7654 * Paints/Repaints the listview control.
7656 * PARAMETER(S):
7657 * [I] infoPtr : valid pointer to the listview structure
7658 * [I] hdc : device context handle
7660 * RETURN:
7661 * Zero
7663 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7665 TRACE("(hdc=%x)\n", hdc);
7667 if (hdc)
7668 LISTVIEW_Refresh(infoPtr, hdc);
7669 else
7671 PAINTSTRUCT ps;
7673 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7674 if (!hdc) return 1;
7675 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7676 LISTVIEW_Refresh(infoPtr, hdc);
7677 EndPaint(infoPtr->hwndSelf, &ps);
7680 return 0;
7683 /***
7684 * DESCRIPTION:
7685 * Processes double click messages (right mouse button).
7687 * PARAMETER(S):
7688 * [I] infoPtr : valid pointer to the listview structure
7689 * [I] wKey : key flag
7690 * [I] pts : mouse coordinate
7692 * RETURN:
7693 * Zero
7695 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7697 LVHITTESTINFO lvHitTestInfo;
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 /* send NM_RDBLCLK notification */
7705 lvHitTestInfo.pt.x = pts.x;
7706 lvHitTestInfo.pt.y = pts.y;
7707 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7708 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
7710 return 0;
7713 /***
7714 * DESCRIPTION:
7715 * Processes mouse down messages (right mouse button).
7717 * PARAMETER(S):
7718 * [I] infoPtr : valid pointer to the listview structure
7719 * [I] wKey : key flag
7720 * [I] pts : mouse coordinate
7722 * RETURN:
7723 * Zero
7725 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7727 LVHITTESTINFO lvHitTestInfo;
7728 INT nItem;
7730 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7732 /* send NM_RELEASEDCAPTURE notification */
7733 notify(infoPtr, NM_RELEASEDCAPTURE);
7735 /* make sure the listview control window has the focus */
7736 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7738 /* set right button down flag */
7739 infoPtr->bRButtonDown = TRUE;
7741 /* determine the index of the selected item */
7742 lvHitTestInfo.pt.x = pts.x;
7743 lvHitTestInfo.pt.y = pts.y;
7744 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7746 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7748 LISTVIEW_SetItemFocus(infoPtr, nItem);
7749 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7750 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7751 LISTVIEW_SetSelection(infoPtr, nItem);
7753 else
7755 LISTVIEW_DeselectAll(infoPtr);
7758 return 0;
7761 /***
7762 * DESCRIPTION:
7763 * Processes mouse up messages (right mouse button).
7765 * PARAMETER(S):
7766 * [I] infoPtr : valid pointer to the listview structure
7767 * [I] wKey : key flag
7768 * [I] pts : mouse coordinate
7770 * RETURN:
7771 * Zero
7773 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7775 LVHITTESTINFO lvHitTestInfo;
7776 POINT pt;
7778 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7780 if (!infoPtr->bRButtonDown) return 0;
7782 /* set button flag */
7783 infoPtr->bRButtonDown = FALSE;
7785 /* Send NM_RClICK notification */
7786 lvHitTestInfo.pt.x = pts.x;
7787 lvHitTestInfo.pt.y = pts.y;
7788 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7789 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
7791 /* Change to screen coordinate for WM_CONTEXTMENU */
7792 pt = lvHitTestInfo.pt;
7793 ClientToScreen(infoPtr->hwndSelf, &pt);
7795 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
7796 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
7797 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
7799 return 0;
7803 /***
7804 * DESCRIPTION:
7805 * Sets the cursor.
7807 * PARAMETER(S):
7808 * [I] infoPtr : valid pointer to the listview structure
7809 * [I] hwnd : window handle of window containing the cursor
7810 * [I] nHittest : hit-test code
7811 * [I] wMouseMsg : ideintifier of the mouse message
7813 * RETURN:
7814 * TRUE if cursor is set
7815 * FALSE otherwise
7817 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
7819 LVHITTESTINFO lvHitTestInfo;
7821 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
7823 if(!infoPtr->hHotCursor) return FALSE;
7825 GetCursorPos(&lvHitTestInfo.pt);
7826 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
7828 SetCursor(infoPtr->hHotCursor);
7830 return TRUE;
7833 /***
7834 * DESCRIPTION:
7835 * Sets the focus.
7837 * PARAMETER(S):
7838 * [I] infoPtr : valid pointer to the listview structure
7839 * [I] hwndLoseFocus : handle of previously focused window
7841 * RETURN:
7842 * Zero
7844 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
7846 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
7848 /* if we have the focus already, there's nothing to do */
7849 if (infoPtr->bFocus) return 0;
7851 /* send NM_SETFOCUS notification */
7852 notify(infoPtr, NM_SETFOCUS);
7854 /* set window focus flag */
7855 infoPtr->bFocus = TRUE;
7857 /* put the focus rect back on */
7858 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
7860 /* redraw all visible selected items */
7861 LISTVIEW_InvalidateSelectedItems(infoPtr);
7863 return 0;
7866 /***
7867 * DESCRIPTION:
7868 * Sets the font.
7870 * PARAMETER(S):
7871 * [I] infoPtr : valid pointer to the listview structure
7872 * [I] fRedraw : font handle
7873 * [I] fRedraw : redraw flag
7875 * RETURN:
7876 * Zero
7878 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
7880 HFONT oldFont = infoPtr->hFont;
7882 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
7884 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
7885 if (infoPtr->hFont == oldFont) return 0;
7887 LISTVIEW_SaveTextMetrics(infoPtr);
7889 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
7890 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
7892 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
7894 return 0;
7897 /***
7898 * DESCRIPTION:
7899 * Message handling for WM_SETREDRAW.
7900 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
7902 * PARAMETER(S):
7903 * [I] infoPtr : valid pointer to the listview structure
7904 * [I] bRedraw: state of redraw flag
7906 * RETURN:
7907 * DefWinProc return value
7909 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
7911 infoPtr->bRedraw = bRedraw;
7912 if(bRedraw)
7913 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
7914 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
7915 return 0;
7918 /***
7919 * DESCRIPTION:
7920 * Resizes the listview control. This function processes WM_SIZE
7921 * messages. At this time, the width and height are not used.
7923 * PARAMETER(S):
7924 * [I] infoPtr : valid pointer to the listview structure
7925 * [I] Width : new width
7926 * [I] Height : new height
7928 * RETURN:
7929 * Zero
7931 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
7933 LONG lStyle = infoPtr->dwStyle;
7934 UINT uView = lStyle & LVS_TYPEMASK;
7936 TRACE("(width=%d, height=%d)\n", Width, Height);
7938 if (LISTVIEW_UpdateSize(infoPtr))
7940 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
7942 if (lStyle & LVS_ALIGNLEFT)
7943 LISTVIEW_AlignLeft(infoPtr);
7944 else
7945 LISTVIEW_AlignTop(infoPtr);
7948 LISTVIEW_UpdateScroll(infoPtr);
7950 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7953 return 0;
7956 /***
7957 * DESCRIPTION:
7958 * Sets the size information.
7960 * PARAMETER(S):
7961 * [I] infoPtr : valid pointer to the listview structure
7963 * RETURN:
7964 * Zero if no size change
7965 * 1 of size changed
7967 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
7969 LONG lStyle = infoPtr->dwStyle;
7970 UINT uView = lStyle & LVS_TYPEMASK;
7971 RECT rcList;
7972 RECT rcOld;
7974 GetClientRect(infoPtr->hwndSelf, &rcList);
7975 CopyRect(&rcOld,&(infoPtr->rcList));
7976 infoPtr->rcList.left = 0;
7977 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
7978 infoPtr->rcList.top = 0;
7979 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
7981 if (uView == LVS_LIST)
7983 /* Apparently the "LIST" style is supposed to have the same
7984 * number of items in a column even if there is no scroll bar.
7985 * Since if a scroll bar already exists then the bottom is already
7986 * reduced, only reduce if the scroll bar does not currently exist.
7987 * The "2" is there to mimic the native control. I think it may be
7988 * related to either padding or edges. (GLA 7/2002)
7990 if (!(lStyle & WS_HSCROLL))
7992 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
7993 if (infoPtr->rcList.bottom > nHScrollHeight)
7994 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
7996 else
7998 if (infoPtr->rcList.bottom > 2)
7999 infoPtr->rcList.bottom -= 2;
8002 else if (uView == LVS_REPORT)
8004 HDLAYOUT hl;
8005 WINDOWPOS wp;
8007 hl.prc = &rcList;
8008 hl.pwpos = &wp;
8009 Header_Layout(infoPtr->hwndHeader, &hl);
8011 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8013 if (!(LVS_NOCOLUMNHEADER & lStyle))
8014 infoPtr->rcList.top = max(wp.cy, 0);
8016 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8019 /***
8020 * DESCRIPTION:
8021 * Processes WM_STYLECHANGED messages.
8023 * PARAMETER(S):
8024 * [I] infoPtr : valid pointer to the listview structure
8025 * [I] wStyleType : window style type (normal or extended)
8026 * [I] lpss : window style information
8028 * RETURN:
8029 * Zero
8031 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8032 LPSTYLESTRUCT lpss)
8034 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8035 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8037 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8038 wStyleType, lpss->styleOld, lpss->styleNew);
8040 if (wStyleType != GWL_STYLE) return 0;
8042 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8043 /* what if LVS_OWNERDATA changed? */
8044 /* or LVS_SINGLESEL */
8045 /* or LVS_SORT{AS,DES}CENDING */
8047 infoPtr->dwStyle = lpss->styleNew;
8049 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8050 ((lpss->styleNew & WS_HSCROLL) == 0))
8051 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8053 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8054 ((lpss->styleNew & WS_VSCROLL) == 0))
8055 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8057 if (uNewView != uOldView)
8059 SIZE oldIconSize = infoPtr->iconSize;
8060 HIMAGELIST himl;
8062 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8063 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8065 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8066 SetRectEmpty(&infoPtr->rcFocus);
8068 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8069 update_icon_size(himl, uNewView != LVS_ICON, &infoPtr->iconSize);
8071 if (uNewView == LVS_ICON)
8073 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8075 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8076 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8077 LISTVIEW_SetIconSpacing(infoPtr, 0);
8080 else if (uNewView == LVS_REPORT)
8082 HDLAYOUT hl;
8083 WINDOWPOS wp;
8085 hl.prc = &infoPtr->rcList;
8086 hl.pwpos = &wp;
8087 Header_Layout(infoPtr->hwndHeader, &hl);
8088 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8091 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8092 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8095 if (uNewView == LVS_REPORT)
8096 ShowWindow(infoPtr->hwndHeader, (LVS_NOCOLUMNHEADER & lpss->styleNew) ? SW_HIDE : SW_SHOWNORMAL);
8098 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8099 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8101 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
8102 LISTVIEW_AlignLeft(infoPtr);
8103 else
8104 LISTVIEW_AlignTop(infoPtr);
8107 /* update the size of the client area */
8108 LISTVIEW_UpdateSize(infoPtr);
8110 /* add scrollbars if needed */
8111 LISTVIEW_UpdateScroll(infoPtr);
8113 /* invalidate client area + erase background */
8114 LISTVIEW_InvalidateList(infoPtr);
8116 /* print the list of unsupported window styles */
8117 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8119 return 0;
8122 /***
8123 * DESCRIPTION:
8124 * Window procedure of the listview control.
8127 static LRESULT WINAPI
8128 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8130 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8132 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8134 if (!infoPtr && (uMsg != WM_CREATE))
8135 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8137 if (infoPtr)
8139 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8142 switch (uMsg)
8144 case LVM_APPROXIMATEVIEWRECT:
8145 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8146 LOWORD(lParam), HIWORD(lParam));
8147 case LVM_ARRANGE:
8148 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8150 /* case LVN_CANCELEDITLABEL */
8152 /* case LVM_CREATEDRAGIMAGE: */
8154 case LVM_DELETEALLITEMS:
8155 return LISTVIEW_DeleteAllItems(infoPtr);
8157 case LVM_DELETECOLUMN:
8158 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8160 case LVM_DELETEITEM:
8161 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8163 case LVM_EDITLABELW:
8164 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8166 case LVM_EDITLABELA:
8167 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8169 /* case LVN_ENABLEGROUPVIEW: */
8171 case LVM_ENSUREVISIBLE:
8172 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8174 case LVM_FINDITEMW:
8175 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8177 case LVM_FINDITEMA:
8178 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8180 case LVM_GETBKCOLOR:
8181 return infoPtr->clrBk;
8183 /* case LVM_GETBKIMAGE: */
8185 case LVM_GETCALLBACKMASK:
8186 return infoPtr->uCallbackMask;
8188 case LVM_GETCOLUMNA:
8189 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8191 case LVM_GETCOLUMNW:
8192 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8194 case LVM_GETCOLUMNORDERARRAY:
8195 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8197 case LVM_GETCOLUMNWIDTH:
8198 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8200 case LVM_GETCOUNTPERPAGE:
8201 return LISTVIEW_GetCountPerPage(infoPtr);
8203 case LVM_GETEDITCONTROL:
8204 return (LRESULT)infoPtr->hwndEdit;
8206 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8207 return infoPtr->dwLvExStyle;
8209 case LVM_GETHEADER:
8210 return (LRESULT)infoPtr->hwndHeader;
8212 case LVM_GETHOTCURSOR:
8213 return (LRESULT)infoPtr->hHotCursor;
8215 case LVM_GETHOTITEM:
8216 return infoPtr->nHotItem;
8218 case LVM_GETHOVERTIME:
8219 return infoPtr->dwHoverTime;
8221 case LVM_GETIMAGELIST:
8222 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8224 /* case LVN_GETINSERTMARK: */
8226 /* case LVN_GETINSERTMARKCOLOR: */
8228 /* case LVN_GETINSERTMARKRECT: */
8230 case LVM_GETISEARCHSTRINGA:
8231 case LVM_GETISEARCHSTRINGW:
8232 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8233 return FALSE;
8235 case LVM_GETITEMA:
8236 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8238 case LVM_GETITEMW:
8239 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8241 case LVM_GETITEMCOUNT:
8242 return infoPtr->nItemCount;
8244 case LVM_GETITEMPOSITION:
8245 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8247 case LVM_GETITEMRECT:
8248 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8250 case LVM_GETITEMSPACING:
8251 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8253 case LVM_GETITEMSTATE:
8254 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8256 case LVM_GETITEMTEXTA:
8257 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8259 case LVM_GETITEMTEXTW:
8260 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8262 case LVM_GETNEXTITEM:
8263 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8265 case LVM_GETNUMBEROFWORKAREAS:
8266 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8267 return 1;
8269 case LVM_GETORIGIN:
8270 if (!lParam) return FALSE;
8271 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8272 return TRUE;
8274 /* case LVN_GETOUTLINECOLOR: */
8276 /* case LVM_GETSELECTEDCOLUMN: */
8278 case LVM_GETSELECTEDCOUNT:
8279 return LISTVIEW_GetSelectedCount(infoPtr);
8281 case LVM_GETSELECTIONMARK:
8282 return infoPtr->nSelectionMark;
8284 case LVM_GETSTRINGWIDTHA:
8285 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8287 case LVM_GETSTRINGWIDTHW:
8288 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8290 case LVM_GETSUBITEMRECT:
8291 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8293 case LVM_GETTEXTBKCOLOR:
8294 return infoPtr->clrTextBk;
8296 case LVM_GETTEXTCOLOR:
8297 return infoPtr->clrText;
8299 /* case LVN_GETTILEINFO: */
8301 /* case LVN_GETTILEVIEWINFO: */
8303 case LVM_GETTOOLTIPS:
8304 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8305 return FALSE;
8307 case LVM_GETTOPINDEX:
8308 return LISTVIEW_GetTopIndex(infoPtr);
8310 /*case LVM_GETUNICODEFORMAT:
8311 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8312 return FALSE;*/
8314 case LVM_GETVIEWRECT:
8315 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8317 case LVM_GETWORKAREAS:
8318 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8319 return FALSE;
8321 /* case LVN_HASGROUP: */
8323 case LVM_HITTEST:
8324 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8326 case LVM_INSERTCOLUMNA:
8327 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8329 case LVM_INSERTCOLUMNW:
8330 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8332 /* case LVN_INSERTGROUP: */
8334 /* case LVN_INSERTGROUPSORTED: */
8336 case LVM_INSERTITEMA:
8337 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8339 case LVM_INSERTITEMW:
8340 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8342 /* case LVN_INSERTMARKHITTEST: */
8344 /* case LVN_ISGROUPVIEWENABLED: */
8346 /* case LVN_MAPIDTOINDEX: */
8348 /* case LVN_INEDXTOID: */
8350 /* case LVN_MOVEGROUP: */
8352 /* case LVN_MOVEITEMTOGROUP: */
8354 case LVM_REDRAWITEMS:
8355 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8357 /* case LVN_REMOVEALLGROUPS: */
8359 /* case LVN_REMOVEGROUP: */
8361 case LVM_SCROLL:
8362 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8364 case LVM_SETBKCOLOR:
8365 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8367 /* case LVM_SETBKIMAGE: */
8369 case LVM_SETCALLBACKMASK:
8370 infoPtr->uCallbackMask = (UINT)wParam;
8371 return TRUE;
8373 case LVM_SETCOLUMNA:
8374 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8376 case LVM_SETCOLUMNW:
8377 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8379 case LVM_SETCOLUMNORDERARRAY:
8380 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8382 case LVM_SETCOLUMNWIDTH:
8383 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8385 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8386 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8388 /* case LVN_SETGROUPINFO: */
8390 /* case LVN_SETGROUPMETRICS: */
8392 case LVM_SETHOTCURSOR:
8393 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8395 case LVM_SETHOTITEM:
8396 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8398 case LVM_SETHOVERTIME:
8399 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8401 case LVM_SETICONSPACING:
8402 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8404 case LVM_SETIMAGELIST:
8405 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8407 /* case LVN_SETINFOTIP: */
8409 /* case LVN_SETINSERTMARK: */
8411 /* case LVN_SETINSERTMARKCOLOR: */
8413 case LVM_SETITEMA:
8414 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8416 case LVM_SETITEMW:
8417 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8419 case LVM_SETITEMCOUNT:
8420 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8422 case LVM_SETITEMPOSITION:
8424 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8425 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8428 case LVM_SETITEMPOSITION32:
8429 if (lParam == 0) return FALSE;
8430 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8432 case LVM_SETITEMSTATE:
8433 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8435 case LVM_SETITEMTEXTA:
8436 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8438 case LVM_SETITEMTEXTW:
8439 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8441 /* case LVN_SETOUTLINECOLOR: */
8443 /* case LVN_SETSELECTEDCOLUMN: */
8445 case LVM_SETSELECTIONMARK:
8446 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8448 case LVM_SETTEXTBKCOLOR:
8449 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8451 case LVM_SETTEXTCOLOR:
8452 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8454 /* case LVN_SETTILEINFO: */
8456 /* case LVN_SETTILEVIEWINFO: */
8458 /* case LVN_SETTILEWIDTH: */
8460 /* case LVM_SETTOOLTIPS: */
8462 /* case LVM_SETUNICODEFORMAT: */
8464 /* case LVN_SETVIEW: */
8466 /* case LVM_SETWORKAREAS: */
8468 /* case LVN_SORTGROUPS: */
8470 case LVM_SORTITEMS:
8471 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8473 case LVM_SUBITEMHITTEST:
8474 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8476 case LVM_UPDATE:
8477 return LISTVIEW_Update(infoPtr, (INT)wParam);
8479 case WM_CHAR:
8480 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8482 case WM_COMMAND:
8483 return LISTVIEW_Command(infoPtr, wParam, lParam);
8485 case WM_CREATE:
8486 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8488 case WM_ERASEBKGND:
8489 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8491 case WM_GETDLGCODE:
8492 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8494 case WM_GETFONT:
8495 return infoPtr->hFont;
8497 case WM_HSCROLL:
8498 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8500 case WM_KEYDOWN:
8501 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8503 case WM_KILLFOCUS:
8504 return LISTVIEW_KillFocus(infoPtr);
8506 case WM_LBUTTONDBLCLK:
8507 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8509 case WM_LBUTTONDOWN:
8510 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8512 case WM_LBUTTONUP:
8513 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8515 case WM_MOUSEMOVE:
8516 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8518 case WM_MOUSEHOVER:
8519 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8521 case WM_NCDESTROY:
8522 return LISTVIEW_NCDestroy(infoPtr);
8524 case WM_NOTIFY:
8525 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8527 case WM_NOTIFYFORMAT:
8528 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8530 case WM_PAINT:
8531 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8533 case WM_RBUTTONDBLCLK:
8534 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8536 case WM_RBUTTONDOWN:
8537 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8539 case WM_RBUTTONUP:
8540 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8542 case WM_SETCURSOR:
8543 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8544 return TRUE;
8545 goto fwd_msg;
8547 case WM_SETFOCUS:
8548 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8550 case WM_SETFONT:
8551 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8553 case WM_SETREDRAW:
8554 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8556 case WM_SIZE:
8557 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8559 case WM_STYLECHANGED:
8560 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8562 case WM_SYSCOLORCHANGE:
8563 COMCTL32_RefreshSysColors();
8564 return 0;
8566 /* case WM_TIMER: */
8568 case WM_VSCROLL:
8569 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8571 case WM_MOUSEWHEEL:
8572 if (wParam & (MK_SHIFT | MK_CONTROL))
8573 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8574 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8576 case WM_WINDOWPOSCHANGED:
8577 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8578 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8579 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8580 LISTVIEW_UpdateSize(infoPtr);
8581 LISTVIEW_UpdateScroll(infoPtr);
8583 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8585 /* case WM_WININICHANGE: */
8587 default:
8588 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8589 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8591 fwd_msg:
8592 /* call default window procedure */
8593 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8596 return 0;
8599 /***
8600 * DESCRIPTION:
8601 * Registers the window class.
8603 * PARAMETER(S):
8604 * None
8606 * RETURN:
8607 * None
8609 void LISTVIEW_Register(void)
8611 WNDCLASSW wndClass;
8613 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8614 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8615 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8616 wndClass.cbClsExtra = 0;
8617 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8618 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8619 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8620 wndClass.lpszClassName = WC_LISTVIEWW;
8621 RegisterClassW(&wndClass);
8624 /***
8625 * DESCRIPTION:
8626 * Unregisters the window class.
8628 * PARAMETER(S):
8629 * None
8631 * RETURN:
8632 * None
8634 void LISTVIEW_Unregister(void)
8636 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8639 /***
8640 * DESCRIPTION:
8641 * Handle any WM_COMMAND messages
8643 * PARAMETER(S):
8644 * [I] infoPtr : valid pointer to the listview structure
8645 * [I] wParam : the first message parameter
8646 * [I] lParam : the second message parameter
8648 * RETURN:
8650 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8652 switch (HIWORD(wParam))
8654 case EN_UPDATE:
8657 * Adjust the edit window size
8659 WCHAR buffer[1024];
8660 HDC hdc = GetDC(infoPtr->hwndEdit);
8661 HFONT hFont, hOldFont = 0;
8662 RECT rect;
8663 SIZE sz;
8664 int len;
8666 if (!infoPtr->hwndEdit || !hdc) return 0;
8667 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8668 GetWindowRect(infoPtr->hwndEdit, &rect);
8670 /* Select font to get the right dimension of the string */
8671 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8672 if(hFont != 0)
8674 hOldFont = SelectObject(hdc, hFont);
8677 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8679 TEXTMETRICW textMetric;
8681 /* Add Extra spacing for the next character */
8682 GetTextMetricsW(hdc, &textMetric);
8683 sz.cx += (textMetric.tmMaxCharWidth * 2);
8685 SetWindowPos (
8686 infoPtr->hwndEdit,
8687 HWND_TOP,
8690 sz.cx,
8691 rect.bottom - rect.top,
8692 SWP_DRAWFRAME|SWP_NOMOVE);
8694 if(hFont != 0)
8695 SelectObject(hdc, hOldFont);
8697 ReleaseDC(infoPtr->hwndSelf, hdc);
8699 break;
8702 default:
8703 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8706 return 0;
8710 /***
8711 * DESCRIPTION:
8712 * Subclassed edit control windproc function
8714 * PARAMETER(S):
8715 * [I] hwnd : the edit window handle
8716 * [I] uMsg : the message that is to be processed
8717 * [I] wParam : first message parameter
8718 * [I] lParam : second message parameter
8719 * [I] isW : TRUE if input is Unicode
8721 * RETURN:
8723 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
8725 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8726 BOOL cancel = FALSE;
8728 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8729 hwnd, uMsg, wParam, lParam, isW);
8731 switch (uMsg)
8733 case WM_GETDLGCODE:
8734 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8736 case WM_KILLFOCUS:
8737 break;
8739 case WM_DESTROY:
8741 WNDPROC editProc = infoPtr->EditWndProc;
8742 infoPtr->EditWndProc = 0;
8743 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
8744 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
8747 case WM_KEYDOWN:
8748 if (VK_ESCAPE == (INT)wParam)
8750 cancel = TRUE;
8751 break;
8753 else if (VK_RETURN == (INT)wParam)
8754 break;
8756 default:
8757 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
8760 /* kill the edit */
8761 if (infoPtr->hwndEdit)
8763 LPWSTR buffer = NULL;
8765 infoPtr->hwndEdit = 0;
8766 if (!cancel)
8768 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
8770 if (len)
8772 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
8774 if (isW) GetWindowTextW(hwnd, buffer, len+1);
8775 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
8779 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
8781 if (buffer) COMCTL32_Free(buffer);
8785 SendMessageW(hwnd, WM_CLOSE, 0, 0);
8786 return TRUE;
8789 /***
8790 * DESCRIPTION:
8791 * Subclassed edit control Unicode windproc function
8793 * PARAMETER(S):
8794 * [I] hwnd : the edit window handle
8795 * [I] uMsg : the message that is to be processed
8796 * [I] wParam : first message parameter
8797 * [I] lParam : second message parameter
8799 * RETURN:
8801 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8803 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
8806 /***
8807 * DESCRIPTION:
8808 * Subclassed edit control ANSI windproc function
8810 * PARAMETER(S):
8811 * [I] hwnd : the edit window handle
8812 * [I] uMsg : the message that is to be processed
8813 * [I] wParam : first message parameter
8814 * [I] lParam : second message parameter
8816 * RETURN:
8818 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8820 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
8823 /***
8824 * DESCRIPTION:
8825 * Creates a subclassed edit cotrol
8827 * PARAMETER(S):
8828 * [I] infoPtr : valid pointer to the listview structure
8829 * [I] text : initial text for the edit
8830 * [I] style : the window style
8831 * [I] isW : TRUE if input is Unicode
8833 * RETURN:
8835 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
8836 INT x, INT y, INT width, INT height, BOOL isW)
8838 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
8839 HWND hedit;
8840 SIZE sz;
8841 HDC hdc;
8842 HDC hOldFont=0;
8843 TEXTMETRICW textMetric;
8844 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
8846 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
8848 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
8849 hdc = GetDC(infoPtr->hwndSelf);
8851 /* Select the font to get appropriate metric dimensions */
8852 if(infoPtr->hFont != 0)
8853 hOldFont = SelectObject(hdc, infoPtr->hFont);
8855 /*Get String Lenght in pixels */
8856 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
8858 /*Add Extra spacing for the next character */
8859 GetTextMetricsW(hdc, &textMetric);
8860 sz.cx += (textMetric.tmMaxCharWidth * 2);
8862 if(infoPtr->hFont != 0)
8863 SelectObject(hdc, hOldFont);
8865 ReleaseDC(infoPtr->hwndSelf, hdc);
8866 if (isW)
8867 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8868 else
8869 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8871 if (!hedit) return 0;
8873 infoPtr->EditWndProc = (WNDPROC)
8874 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
8875 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
8877 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
8879 return hedit;