Keep track of per-column information inside the listview.
[wine/testsucceed.git] / dlls / comctl32 / listview.c
blobd09ac0ad418cc8b731a2d0e0a0223578fb7ad929
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 typedef struct tagCOLUMN_INFO
78 RECT rcHeader; /* tracks the header's rectangle */
79 UINT align; /* one of DT_{LEFT,CENTER,RIGHT} */
80 BOOL hasImage; /* on/off switch for column images */
81 } COLUMN_INFO;
83 typedef struct tagITEMHDR
85 LPWSTR pszText;
86 INT iImage;
87 } ITEMHDR, *LPITEMHDR;
89 typedef struct tagLISTVIEW_SUBITEM
91 ITEMHDR hdr;
92 INT iSubItem;
93 } LISTVIEW_SUBITEM;
95 typedef struct tagLISTVIEW_ITEM
97 ITEMHDR hdr;
98 UINT state;
99 LPARAM lParam;
100 INT iIndent;
101 } LISTVIEW_ITEM;
103 typedef struct tagRANGE
105 INT lower;
106 INT upper;
107 } RANGE;
109 typedef struct tagRANGES
111 HDPA hdpa;
112 } *RANGES;
114 typedef struct tagITERATOR
116 INT nItem;
117 INT nSpecial;
118 RANGE range;
119 RANGES ranges;
120 INT index;
121 } ITERATOR;
123 typedef struct tagLISTVIEW_INFO
125 HWND hwndSelf;
126 HBRUSH hBkBrush;
127 COLORREF clrBk;
128 COLORREF clrText;
129 COLORREF clrTextBk;
130 COLORREF clrTextBkDefault;
131 HIMAGELIST himlNormal;
132 HIMAGELIST himlSmall;
133 HIMAGELIST himlState;
134 BOOL bLButtonDown;
135 BOOL bRButtonDown;
136 INT nItemHeight;
137 INT nItemWidth;
138 RANGES selectionRanges;
139 INT nSelectionMark;
140 INT nHotItem;
141 SHORT notifyFormat;
142 RECT rcList; /* This rectangle is really the window
143 * client rectangle possibly reduced by the
144 * horizontal scroll bar and/or header - see
145 * LISTVIEW_UpdateSize. This rectangle offset
146 * by the LISTVIEW_GetOrigin value is in
147 * client coordinates */
148 RECT rcView; /* This rectangle contains all items -
149 * contructed in LISTVIEW_AlignTop and
150 * LISTVIEW_AlignLeft */
151 SIZE iconSize;
152 SIZE iconSpacing;
153 SIZE iconStateSize;
154 UINT uCallbackMask;
155 HWND hwndHeader;
156 HFONT hDefaultFont;
157 HCURSOR hHotCursor;
158 HFONT hFont;
159 INT ntmHeight; /* from GetTextMetrics from above font */
160 BOOL bRedraw;
161 BOOL bFocus;
162 INT nFocusedItem;
163 RECT rcFocus;
164 DWORD dwStyle; /* the cached window GWL_STYLE */
165 DWORD dwLvExStyle; /* extended listview style */
166 INT nItemCount; /* the number of items in the list */
167 HDPA hdpaItems; /* array ITEM_INFO pointers */
168 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
169 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
170 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
171 PFNLVCOMPARE pfnCompare;
172 LPARAM lParamSort;
173 HWND hwndEdit;
174 WNDPROC EditWndProc;
175 INT nEditLabelItem;
176 DWORD dwHoverTime;
178 DWORD lastKeyPressTimestamp;
179 WPARAM charCode;
180 INT nSearchParamLength;
181 WCHAR szSearchParam[ MAX_PATH ];
182 BOOL bIsDrawing;
183 } LISTVIEW_INFO;
186 * constants
188 /* How many we debug buffer to allocate */
189 #define DEBUG_BUFFERS 20
190 /* The size of a single debug bbuffer */
191 #define DEBUG_BUFFER_SIZE 256
193 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
194 #define SB_INTERNAL -1
196 /* maximum size of a label */
197 #define DISP_TEXT_SIZE 512
199 /* padding for items in list and small icon display modes */
200 #define WIDTH_PADDING 12
202 /* padding for items in list, report and small icon display modes */
203 #define HEIGHT_PADDING 1
205 /* offset of items in report display mode */
206 #define REPORT_MARGINX 2
208 /* padding for icon in large icon display mode
209 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
210 * that HITTEST will see.
211 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
212 * ICON_TOP_PADDING - sum of the two above.
213 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
214 * LABEL_VERT_PADDING - between bottom of text and end of box
216 * ICON_LR_PADDING - additional width above icon size.
217 * ICON_LR_HALF - half of the above value
219 #define ICON_TOP_PADDING_NOTHITABLE 2
220 #define ICON_TOP_PADDING_HITABLE 2
221 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
222 #define ICON_BOTTOM_PADDING 4
223 #define LABEL_VERT_PADDING 7
224 #define ICON_LR_PADDING 16
225 #define ICON_LR_HALF (ICON_LR_PADDING/2)
227 /* default label width for items in list and small icon display modes */
228 #define DEFAULT_LABEL_WIDTH 40
230 /* default column width for items in list display mode */
231 #define DEFAULT_COLUMN_WIDTH 128
233 /* Size of "line" scroll for V & H scrolls */
234 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
236 /* Padding betwen image and label */
237 #define IMAGE_PADDING 2
239 /* Padding behind the label */
240 #define TRAILING_PADDING 5
242 /* Border for the icon caption */
243 #define CAPTION_BORDER 2
245 /* Standard DrawText flags */
246 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
247 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
248 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
250 /* The time in milisecods to reset the search in the list */
251 #define KEY_DELAY 450
253 /* Dump the LISTVIEW_INFO structure to the debug channel */
254 #define LISTVIEW_DUMP(iP) do { \
255 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
256 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
257 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
258 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
259 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
260 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
261 (iP->bFocus) ? "true" : "false"); \
262 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
263 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
264 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
265 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
266 iP->hwndSelf, \
267 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
268 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
269 } while(0)
273 * forward declarations
275 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
276 static BOOL LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
277 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
278 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
279 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
280 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *);
281 static BOOL LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
282 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
283 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
284 static INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *);
285 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
286 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *, INT);
287 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
288 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
289 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
290 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
291 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, POINT);
292 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
293 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
294 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
295 static void LISTVIEW_UnsupportedStyles(LONG);
296 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
297 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
298 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
299 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
300 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
301 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
302 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
303 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
304 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
305 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
306 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
307 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
308 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
310 /******** Text handling functions *************************************/
312 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
313 * text string. The string may be ANSI or Unicode, in which case
314 * the boolean isW tells us the type of the string.
316 * The name of the function tell what type of strings it expects:
317 * W: Unicode, T: ANSI/Unicode - function of isW
320 static inline BOOL is_textW(LPCWSTR text)
322 return text != NULL && text != LPSTR_TEXTCALLBACKW;
325 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
327 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
328 return is_textW(text);
331 static inline int textlenT(LPCWSTR text, BOOL isW)
333 return !is_textT(text, isW) ? 0 :
334 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
337 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
339 if (isDestW)
340 if (isSrcW) lstrcpynW(dest, src, max);
341 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
342 else
343 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
344 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
347 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
349 LPWSTR wstr = (LPWSTR)text;
351 if (!isW && is_textT(text, isW))
353 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
354 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
355 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
357 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
358 return wstr;
361 static inline void textfreeT(LPWSTR wstr, BOOL isW)
363 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
367 * dest is a pointer to a Unicode string
368 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
370 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
372 BOOL bResult = TRUE;
374 if (src == LPSTR_TEXTCALLBACKW)
376 if (is_textW(*dest)) COMCTL32_Free(*dest);
377 *dest = LPSTR_TEXTCALLBACKW;
379 else
381 LPWSTR pszText = textdupTtoW(src, isW);
382 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
383 bResult = Str_SetPtrW(dest, pszText);
384 textfreeT(pszText, isW);
386 return bResult;
390 * compares a Unicode to a Unicode/ANSI text string
392 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
394 if (!aw) return bt ? -1 : 0;
395 if (!bt) return aw ? 1 : 0;
396 if (aw == LPSTR_TEXTCALLBACKW)
397 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
398 if (bt != LPSTR_TEXTCALLBACKW)
400 LPWSTR bw = textdupTtoW(bt, isW);
401 int r = bw ? lstrcmpW(aw, bw) : 1;
402 textfreeT(bw, isW);
403 return r;
406 return 1;
409 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
411 int res;
413 n = min(min(n, strlenW(s1)), strlenW(s2));
414 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
415 return res ? res - sizeof(WCHAR) : res;
418 /******** Debugging functions *****************************************/
420 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
422 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
423 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
426 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
428 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
429 n = min(textlenT(text, isW), n);
430 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
433 static char* debug_getbuf()
435 static int index = 0;
436 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
437 return buffers[index++ % DEBUG_BUFFERS];
440 static inline char* debugrange(const RANGE* lprng)
442 if (lprng)
444 char* buf = debug_getbuf();
445 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
446 return buf;
447 } else return "(null)";
450 static inline char* debugpoint(const POINT* lppt)
452 if (lppt)
454 char* buf = debug_getbuf();
455 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
456 return buf;
457 } else return "(null)";
460 static inline char* debugrect(const RECT* rect)
462 if (rect)
464 char* buf = debug_getbuf();
465 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
466 rect->left, rect->top, rect->right, rect->bottom);
467 return buf;
468 } else return "(null)";
471 static char* debugnmlistview(LPNMLISTVIEW plvnm)
473 if (plvnm)
475 char* buf = debug_getbuf();
476 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
477 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
478 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
479 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
480 return buf;
481 } else return "(null)";
484 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
486 char* buf = debug_getbuf(), *text = buf;
487 int len, size = DEBUG_BUFFER_SIZE;
489 if (lpLVItem == NULL) return "(null)";
490 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
491 if (len == -1) goto end; buf += len; size -= len;
492 if (lpLVItem->mask & LVIF_STATE)
493 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
494 else len = 0;
495 if (len == -1) goto end; buf += len; size -= len;
496 if (lpLVItem->mask & LVIF_TEXT)
497 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
498 else len = 0;
499 if (len == -1) goto end; buf += len; size -= len;
500 if (lpLVItem->mask & LVIF_IMAGE)
501 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
502 else len = 0;
503 if (len == -1) goto end; buf += len; size -= len;
504 if (lpLVItem->mask & LVIF_PARAM)
505 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
506 else len = 0;
507 if (len == -1) goto end; buf += len; size -= len;
508 if (lpLVItem->mask & LVIF_INDENT)
509 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
510 else len = 0;
511 if (len == -1) goto end; buf += len; size -= len;
512 goto undo;
513 end:
514 buf = text + strlen(text);
515 undo:
516 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
517 return text;
520 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
522 char* buf = debug_getbuf(), *text = buf;
523 int len, size = DEBUG_BUFFER_SIZE;
525 if (lpColumn == NULL) return "(null)";
526 len = snprintf(buf, size, "{");
527 if (len == -1) goto end; buf += len; size -= len;
528 if (lpColumn->mask & LVCF_SUBITEM)
529 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
530 else len = 0;
531 if (len == -1) goto end; buf += len; size -= len;
532 if (lpColumn->mask & LVCF_FMT)
533 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
534 else len = 0;
535 if (len == -1) goto end; buf += len; size -= len;
536 if (lpColumn->mask & LVCF_WIDTH)
537 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
538 else len = 0;
539 if (len == -1) goto end; buf += len; size -= len;
540 if (lpColumn->mask & LVCF_TEXT)
541 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
542 else len = 0;
543 if (len == -1) goto end; buf += len; size -= len;
544 if (lpColumn->mask & LVCF_IMAGE)
545 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
546 else len = 0;
547 if (len == -1) goto end; buf += len; size -= len;
548 if (lpColumn->mask & LVCF_ORDER)
549 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
550 else len = 0;
551 if (len == -1) goto end; buf += len; size -= len;
552 goto undo;
553 end:
554 buf = text + strlen(text);
555 undo:
556 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
557 return text;
561 /******** Notification functions i************************************/
563 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
565 LRESULT result;
567 TRACE("(code=%d)\n", code);
569 pnmh->hwndFrom = infoPtr->hwndSelf;
570 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
571 pnmh->code = code;
572 result = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
573 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
575 TRACE(" <= %ld\n", result);
577 return result;
580 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
582 NMHDR nmh;
583 return notify_hdr(infoPtr, code, &nmh);
586 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
588 notify(infoPtr, LVN_ITEMACTIVATE);
591 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
593 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
594 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
597 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
599 NMLISTVIEW nmlv;
601 ZeroMemory(&nmlv, sizeof(nmlv));
602 nmlv.iItem = lvht->iItem;
603 nmlv.iSubItem = lvht->iSubItem;
604 nmlv.ptAction = lvht->pt;
605 return notify_listview(infoPtr, code, &nmlv);
608 static int get_ansi_notification(INT unicodeNotificationCode)
610 switch (unicodeNotificationCode)
612 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
613 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
614 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
615 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
616 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
617 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
619 ERR("unknown notification %x\n", unicodeNotificationCode);
620 assert(FALSE);
624 Send notification. depends on dispinfoW having same
625 structure as dispinfoA.
626 infoPtr : listview struct
627 notificationCode : *Unicode* notification code
628 pdi : dispinfo structure (can be unicode or ansi)
629 isW : TRUE if dispinfo is Unicode
631 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
633 BOOL bResult = FALSE;
634 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
635 INT realNotifCode;
636 INT cchTempBufMax = 0, savCchTextMax = 0;
637 LPWSTR pszTempBuf = NULL, savPszText = NULL;
639 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
641 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
642 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
645 if (convertToAnsi || convertToUnicode)
647 if (notificationCode != LVN_GETDISPINFOW)
649 cchTempBufMax = convertToUnicode ?
650 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
651 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
653 else
655 cchTempBufMax = pdi->item.cchTextMax;
656 *pdi->item.pszText = 0; /* make sure we don't process garbage */
659 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
660 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
661 if (!pszTempBuf) return FALSE;
662 if (convertToUnicode)
663 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
664 pszTempBuf, cchTempBufMax);
665 else
666 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
667 cchTempBufMax, NULL, NULL);
668 savCchTextMax = pdi->item.cchTextMax;
669 savPszText = pdi->item.pszText;
670 pdi->item.pszText = pszTempBuf;
671 pdi->item.cchTextMax = cchTempBufMax;
674 if (infoPtr->notifyFormat == NFR_ANSI)
675 realNotifCode = get_ansi_notification(notificationCode);
676 else
677 realNotifCode = notificationCode;
678 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
679 bResult = notify_hdr(infoPtr, realNotifCode, (LPNMHDR)pdi);
681 if (convertToUnicode || convertToAnsi)
683 if (convertToUnicode) /* note : pointer can be changed by app ! */
684 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
685 savCchTextMax, NULL, NULL);
686 else
687 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
688 savPszText, savCchTextMax);
689 pdi->item.pszText = savPszText; /* restores our buffer */
690 pdi->item.cchTextMax = savCchTextMax;
691 HeapFree(GetProcessHeap(), 0, pszTempBuf);
693 return bResult;
696 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc, LPRECT rcBounds)
698 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
699 lpnmlvcd->nmcd.hdc = hdc;
700 lpnmlvcd->nmcd.rc = *rcBounds;
701 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
702 lpnmlvcd->clrText = infoPtr->clrText;
705 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
707 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
708 return notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
711 /******** Item iterator functions **********************************/
713 static RANGES ranges_create(int count);
714 static void ranges_destroy(RANGES ranges);
715 static BOOL ranges_add(RANGES ranges, RANGE range);
716 static BOOL ranges_del(RANGES ranges, RANGE range);
717 static void ranges_dump(RANGES ranges);
719 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
721 RANGE range = { nItem, nItem + 1 };
723 return ranges_add(ranges, range);
726 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
728 RANGE range = { nItem, nItem + 1 };
730 return ranges_del(ranges, range);
733 /***
734 * ITERATOR DOCUMENTATION
736 * The iterator functions allow for easy, and convenient iteration
737 * over items of iterest in the list. Typically, you create a
738 * iterator, use it, and destroy it, as such:
739 * ITERATOR i;
741 * iterator_xxxitems(&i, ...);
742 * while (iterator_{prev,next}(&i)
744 * //code which uses i.nItem
746 * iterator_destroy(&i);
748 * where xxx is either: framed, or visible.
749 * Note that it is important that the code destroys the iterator
750 * after it's done with it, as the creation of the iterator may
751 * allocate memory, which thus needs to be freed.
753 * You can iterate both forwards, and backwards through the list,
754 * by using iterator_next or iterator_prev respectively.
756 * Lower numbered items are draw on top of higher number items in
757 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
758 * items may overlap). So, to test items, you should use
759 * iterator_next
760 * which lists the items top to bottom (in Z-order).
761 * For drawing items, you should use
762 * iterator_prev
763 * which lists the items bottom to top (in Z-order).
764 * If you keep iterating over the items after the end-of-items
765 * marker (-1) is returned, the iterator will start from the
766 * beginning. Typically, you don't need to test for -1,
767 * because iterator_{next,prev} will return TRUE if more items
768 * are to be iterated over, or FALSE otherwise.
770 * Note: the iterator is defined to be bidirectional. That is,
771 * any number of prev followed by any number of next, or
772 * five versa, should leave the iterator at the same item:
773 * prev * n, next * n = next * n, prev * n
775 * The iterator has a notion of a out-of-order, special item,
776 * which sits at the start of the list. This is used in
777 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
778 * which needs to be first, as it may overlap other items.
780 * The code is a bit messy because we have:
781 * - a special item to deal with
782 * - simple range, or composite range
783 * - empty range.
784 * If find bugs, or want to add features, please make sure you
785 * always check/modify *both* iterator_prev, and iterator_next.
788 /****
789 * This function iterates through the items in increasing order,
790 * but prefixed by the special item, then -1. That is:
791 * special, 1, 2, 3, ..., n, -1.
792 * Each item is listed only once.
794 static inline BOOL iterator_next(ITERATOR* i)
796 if (i->nItem == -1)
798 i->nItem = i->nSpecial;
799 if (i->nItem != -1) return TRUE;
801 if (i->nItem == i->nSpecial)
803 if (i->ranges) i->index = 0;
804 goto pickarange;
807 i->nItem++;
808 testitem:
809 if (i->nItem == i->nSpecial) i->nItem++;
810 if (i->nItem < i->range.upper) return TRUE;
812 pickarange:
813 if (i->ranges)
815 if (i->index < i->ranges->hdpa->nItemCount)
816 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
817 else goto end;
819 else if (i->nItem >= i->range.upper) goto end;
821 i->nItem = i->range.lower;
822 if (i->nItem >= 0) goto testitem;
823 end:
824 i->nItem = -1;
825 return FALSE;
828 /****
829 * This function iterates through the items in decreasing order,
830 * followed by the special item, then -1. That is:
831 * n, n-1, ..., 3, 2, 1, special, -1.
832 * Each item is listed only once.
834 static inline BOOL iterator_prev(ITERATOR* i)
836 BOOL start = FALSE;
838 if (i->nItem == -1)
840 start = TRUE;
841 if (i->ranges) i->index = i->ranges->hdpa->nItemCount;
842 goto pickarange;
844 if (i->nItem == i->nSpecial)
846 i->nItem = -1;
847 return FALSE;
850 testitem:
851 i->nItem--;
852 if (i->nItem == i->nSpecial) i->nItem--;
853 if (i->nItem >= i->range.lower) return TRUE;
855 pickarange:
856 if (i->ranges)
858 if (i->index > 0)
859 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
860 else goto end;
862 else if (!start && i->nItem < i->range.lower) goto end;
864 i->nItem = i->range.upper;
865 if (i->nItem > 0) goto testitem;
866 end:
867 return (i->nItem = i->nSpecial) != -1;
870 static RANGE iterator_range(ITERATOR* i)
872 RANGE range;
874 if (!i->ranges) return i->range;
876 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
877 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->ranges->hdpa->nItemCount - 1)).upper;
878 return range;
881 /***
882 * Releases resources associated with this ierator.
884 static inline void iterator_destroy(ITERATOR* i)
886 if (i->ranges) ranges_destroy(i->ranges);
889 /***
890 * Create an empty iterator.
892 static inline BOOL iterator_empty(ITERATOR* i)
894 ZeroMemory(i, sizeof(*i));
895 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
896 return TRUE;
900 /***
901 * Create an iterator over a bunch of ranges.
902 * Please note that the iterator will take ownership of the ranges,
903 * and will free them upon destruction.
905 static inline BOOL iterator_ranges(ITERATOR* i, RANGES ranges)
907 iterator_empty(i);
908 i->ranges = ranges;
909 return TRUE;
912 /***
913 * Creates an iterator over the items which intersect lprc.
915 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
917 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
918 RECT frame = *lprc, rcItem, rcTemp;
919 POINT Origin;
921 /* in case we fail, we want to return an empty iterator */
922 if (!iterator_empty(i)) return FALSE;
924 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
926 OffsetRect(&frame, -Origin.x, -Origin.y);
928 if (uView == LVS_ICON || uView == LVS_SMALLICON)
930 INT nItem;
932 if (uView == LVS_ICON)
934 if (LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem) && IntersectRect(&rcTemp, &rcItem, lprc))
935 i->nSpecial = infoPtr->nFocusedItem;
937 if (!(i->ranges = ranges_create(50))) return FALSE;
938 /* to do better here, we need to have PosX, and PosY sorted */
939 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
941 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
942 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
943 rcItem.right = rcItem.left + infoPtr->nItemWidth;
944 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
945 if (IntersectRect(&rcTemp, &rcItem, &frame))
946 ranges_additem(i->ranges, nItem);
948 if (TRACE_ON(listview))
950 TRACE(" icon items:\n");
951 ranges_dump(i->ranges);
953 return TRUE;
955 else if (uView == LVS_REPORT)
957 INT lower, upper;
959 if (frame.left >= infoPtr->nItemWidth) return TRUE;
960 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
962 lower = max(frame.top / infoPtr->nItemHeight, 0);
963 upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1);
964 if (upper < lower) return TRUE;
965 i->range.lower = lower;
966 i->range.upper = upper + 1;
967 TRACE(" report=%s\n", debugrange(&i->range));
969 else
971 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
972 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
973 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
974 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
975 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
976 INT lower = nFirstCol * nPerCol + nFirstRow;
977 RANGE item_range;
978 INT nCol;
980 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
981 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
983 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
985 if (!(i->ranges = ranges_create(nLastCol - nFirstCol + 1))) return FALSE;
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 (LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem) && !RectVisible(hdc, &rcItem))
1015 i->nSpecial = -1;
1017 /* if we can't deal with the region, we'll just go with the simple range */
1018 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return TRUE;
1019 if (!i->ranges)
1021 if (!(i->ranges = ranges_create(50))) return TRUE;
1022 if (!ranges_add(i->ranges, i->range))
1024 ranges_destroy(i->ranges);
1025 i->ranges = 0;
1026 return TRUE;
1030 /* now delete the invisible items from the list */
1031 while(iterator_next(i))
1033 if (!LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position)) continue;
1034 rcItem.left = Position.x + Origin.x;
1035 rcItem.top = Position.y + Origin.y;
1036 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1037 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1038 if (!RectVisible(hdc, &rcItem))
1039 ranges_delitem(i->ranges, i->nItem);
1041 /* the iterator should restart on the next iterator_next */
1043 return TRUE;
1046 /******** Misc helper functions ************************************/
1048 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1049 WPARAM wParam, LPARAM lParam, BOOL isW)
1051 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1052 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1055 /******** Internal API functions ************************************/
1057 /* The Invalidate* are macros, so we preserve the caller location */
1058 #define LISTVIEW_InvalidateRect(infoPtr, rect) while(infoPtr->bRedraw) { \
1059 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
1060 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
1061 break;\
1064 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
1065 RECT rcBox; \
1066 if(LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox)) \
1067 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
1068 } while (0)
1070 #define LISTVIEW_InvalidateSubItem(infoPtr, nItem, nSubItem) do { \
1071 POINT Origin, Position; \
1072 RECT rcBox; \
1073 if (LISTVIEW_GetOrigin(infoPtr, &Origin) && \
1074 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position) && \
1075 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox)) { \
1076 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y); \
1077 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
1079 } while (0)
1081 #define LISTVIEW_InvalidateList(infoPtr)\
1082 LISTVIEW_InvalidateRect(infoPtr, NULL)
1085 static inline BOOL LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1087 COLUMN_INFO *columnInfo;
1089 columnInfo = (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1090 if (!columnInfo) return FALSE;
1091 *lprc = columnInfo->rcHeader;
1092 return TRUE;
1095 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1097 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1100 /***
1101 * DESCRIPTION:
1102 * Retrieves the number of items that can fit vertically in the client area.
1104 * PARAMETER(S):
1105 * [I] infoPtr : valid pointer to the listview structure
1107 * RETURN:
1108 * Number of items per row.
1110 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1112 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1114 return max(nListWidth/infoPtr->nItemWidth, 1);
1117 /***
1118 * DESCRIPTION:
1119 * Retrieves the number of items that can fit horizontally in the client
1120 * area.
1122 * PARAMETER(S):
1123 * [I] infoPtr : valid pointer to the listview structure
1125 * RETURN:
1126 * Number of items per column.
1128 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1130 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1132 return max(nListHeight / infoPtr->nItemHeight, 1);
1136 /*************************************************************************
1137 * LISTVIEW_ProcessLetterKeys
1139 * Processes keyboard messages generated by pressing the letter keys
1140 * on the keyboard.
1141 * What this does is perform a case insensitive search from the
1142 * current position with the following quirks:
1143 * - If two chars or more are pressed in quick succession we search
1144 * for the corresponding string (e.g. 'abc').
1145 * - If there is a delay we wipe away the current search string and
1146 * restart with just that char.
1147 * - If the user keeps pressing the same character, whether slowly or
1148 * fast, so that the search string is entirely composed of this
1149 * character ('aaaaa' for instance), then we search for first item
1150 * that starting with that character.
1151 * - If the user types the above character in quick succession, then
1152 * we must also search for the corresponding string ('aaaaa'), and
1153 * go to that string if there is a match.
1155 * PARAMETERS
1156 * [I] hwnd : handle to the window
1157 * [I] charCode : the character code, the actual character
1158 * [I] keyData : key data
1160 * RETURNS
1162 * Zero.
1164 * BUGS
1166 * - The current implementation has a list of characters it will
1167 * accept and it ignores averything else. In particular it will
1168 * ignore accentuated characters which seems to match what
1169 * Windows does. But I'm not sure it makes sense to follow
1170 * Windows there.
1171 * - We don't sound a beep when the search fails.
1173 * SEE ALSO
1175 * TREEVIEW_ProcessLetterKeys
1177 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1179 INT nItem;
1180 INT endidx,idx;
1181 LVITEMW item;
1182 WCHAR buffer[MAX_PATH];
1183 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1185 /* simple parameter checking */
1186 if (!charCode || !keyData) return 0;
1188 /* only allow the valid WM_CHARs through */
1189 if (!isalnum(charCode) &&
1190 charCode != '.' && charCode != '`' && charCode != '!' &&
1191 charCode != '@' && charCode != '#' && charCode != '$' &&
1192 charCode != '%' && charCode != '^' && charCode != '&' &&
1193 charCode != '*' && charCode != '(' && charCode != ')' &&
1194 charCode != '-' && charCode != '_' && charCode != '+' &&
1195 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1196 charCode != '}' && charCode != '[' && charCode != '{' &&
1197 charCode != '/' && charCode != '?' && charCode != '>' &&
1198 charCode != '<' && charCode != ',' && charCode != '~')
1199 return 0;
1201 /* if there's one item or less, there is no where to go */
1202 if (infoPtr->nItemCount <= 1) return 0;
1204 /* update the search parameters */
1205 infoPtr->lastKeyPressTimestamp = GetTickCount();
1206 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1207 if (infoPtr->nSearchParamLength < MAX_PATH)
1208 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1209 if (infoPtr->charCode != charCode)
1210 infoPtr->charCode = charCode = 0;
1211 } else {
1212 infoPtr->charCode=charCode;
1213 infoPtr->szSearchParam[0]=charCode;
1214 infoPtr->nSearchParamLength=1;
1215 /* Redundant with the 1 char string */
1216 charCode=0;
1219 /* and search from the current position */
1220 nItem=-1;
1221 if (infoPtr->nFocusedItem >= 0) {
1222 endidx=infoPtr->nFocusedItem;
1223 idx=endidx;
1224 /* if looking for single character match,
1225 * then we must always move forward
1227 if (infoPtr->nSearchParamLength == 1)
1228 idx++;
1229 } else {
1230 endidx=infoPtr->nItemCount;
1231 idx=0;
1233 do {
1234 if (idx == infoPtr->nItemCount) {
1235 if (endidx == infoPtr->nItemCount || endidx == 0)
1236 break;
1237 idx=0;
1240 /* get item */
1241 item.mask = LVIF_TEXT;
1242 item.iItem = idx;
1243 item.iSubItem = 0;
1244 item.pszText = buffer;
1245 item.cchTextMax = MAX_PATH;
1246 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1248 /* check for a match */
1249 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1250 nItem=idx;
1251 break;
1252 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1253 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1254 /* This would work but we must keep looking for a longer match */
1255 nItem=idx;
1257 idx++;
1258 } while (idx != endidx);
1260 if (nItem != -1)
1261 LISTVIEW_KeySelection(infoPtr, nItem);
1263 return 0;
1266 /*************************************************************************
1267 * LISTVIEW_UpdateHeaderSize [Internal]
1269 * Function to resize the header control
1271 * PARAMS
1272 * hwnd [I] handle to a window
1273 * nNewScrollPos [I] Scroll Pos to Set
1275 * RETURNS
1276 * nothing
1278 * NOTES
1280 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1282 RECT winRect;
1283 POINT point[2];
1285 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1287 GetWindowRect(infoPtr->hwndHeader, &winRect);
1288 point[0].x = winRect.left;
1289 point[0].y = winRect.top;
1290 point[1].x = winRect.right;
1291 point[1].y = winRect.bottom;
1293 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1294 point[0].x = -nNewScrollPos;
1295 point[1].x += nNewScrollPos;
1297 SetWindowPos(infoPtr->hwndHeader,0,
1298 point[0].x,point[0].y,point[1].x,point[1].y,
1299 SWP_NOZORDER | SWP_NOACTIVATE);
1302 /***
1303 * DESCRIPTION:
1304 * Update the scrollbars. This functions should be called whenever
1305 * the content, size or view changes.
1307 * PARAMETER(S):
1308 * [I] infoPtr : valid pointer to the listview structure
1310 * RETURN:
1311 * None
1313 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1315 LONG lStyle = infoPtr->dwStyle;
1316 UINT uView = lStyle & LVS_TYPEMASK;
1317 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1318 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1319 SCROLLINFO scrollInfo;
1321 if (lStyle & LVS_NOSCROLL) return;
1323 scrollInfo.cbSize = sizeof(SCROLLINFO);
1325 if (uView == LVS_LIST)
1327 /* update horizontal scrollbar */
1328 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1329 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1331 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1332 infoPtr->nItemCount, nCountPerColumn, nCountPerRow);
1334 scrollInfo.nMin = 0;
1335 scrollInfo.nMax = infoPtr->nItemCount / nCountPerColumn;
1336 if((infoPtr->nItemCount % nCountPerColumn) == 0)
1337 scrollInfo.nMax--;
1338 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1339 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1340 scrollInfo.nPage = nCountPerRow;
1341 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1342 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1343 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1345 else if (uView == LVS_REPORT)
1347 BOOL test;
1349 /* update vertical scrollbar */
1350 scrollInfo.nMin = 0;
1351 scrollInfo.nMax = infoPtr->nItemCount - 1;
1352 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1353 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1354 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1355 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1356 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1357 scrollInfo.nMax, scrollInfo.nPage, test);
1358 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1359 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1361 /* update horizontal scrollbar */
1362 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1363 scrollInfo.fMask = SIF_POS;
1364 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1365 || infoPtr->nItemCount == 0)
1367 scrollInfo.nPos = 0;
1369 scrollInfo.nMin = 0;
1370 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1371 scrollInfo.nPage = nListWidth;
1372 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1373 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1374 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1375 scrollInfo.nMax, scrollInfo.nPage, test);
1376 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1377 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1379 /* Update the Header Control */
1380 scrollInfo.fMask = SIF_POS;
1381 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1382 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1385 else
1387 RECT rcView;
1389 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1391 INT nViewWidth = rcView.right - rcView.left;
1392 INT nViewHeight = rcView.bottom - rcView.top;
1394 /* Update Horizontal Scrollbar */
1395 scrollInfo.fMask = SIF_POS;
1396 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1397 || infoPtr->nItemCount == 0)
1399 scrollInfo.nPos = 0;
1401 scrollInfo.nMin = 0;
1402 scrollInfo.nMax = max(nViewWidth, 0)-1;
1403 scrollInfo.nPage = nListWidth;
1404 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1405 TRACE("LVS_ICON/SMALLICON Horz.\n");
1406 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1408 /* Update Vertical Scrollbar */
1409 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1410 scrollInfo.fMask = SIF_POS;
1411 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1412 || infoPtr->nItemCount == 0)
1414 scrollInfo.nPos = 0;
1416 scrollInfo.nMin = 0;
1417 scrollInfo.nMax = max(nViewHeight,0)-1;
1418 scrollInfo.nPage = nListHeight;
1419 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1420 TRACE("LVS_ICON/SMALLICON Vert.\n");
1421 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1427 /***
1428 * DESCRIPTION:
1429 * Shows/hides the focus rectangle.
1431 * PARAMETER(S):
1432 * [I] infoPtr : valid pointer to the listview structure
1433 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1435 * RETURN:
1436 * None
1438 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1440 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1441 HDC hdc;
1443 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1445 if (infoPtr->nFocusedItem < 0) return;
1447 /* we need some gymnastics in ICON mode to handle large items */
1448 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1450 RECT rcBox;
1452 if (!LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox))
1453 return;
1454 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1456 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1457 return;
1461 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1463 /* for some reason, owner draw should work only in report mode */
1464 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1466 DRAWITEMSTRUCT dis;
1467 LVITEMW item;
1469 item.iItem = infoPtr->nFocusedItem;
1470 item.iSubItem = 0;
1471 item.mask = LVIF_PARAM;
1472 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1474 ZeroMemory(&dis, sizeof(dis));
1475 dis.CtlType = ODT_LISTVIEW;
1476 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1477 dis.itemID = item.iItem;
1478 dis.itemAction = ODA_FOCUS;
1479 if (fShow) dis.itemState |= ODS_FOCUS;
1480 dis.hwndItem = infoPtr->hwndSelf;
1481 dis.hDC = hdc;
1482 if (!LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem)) goto done;
1483 dis.itemData = item.lParam;
1485 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1487 else
1489 DrawFocusRect(hdc, &infoPtr->rcFocus);
1491 done:
1492 ReleaseDC(infoPtr->hwndSelf, hdc);
1495 /***
1496 * Invalidates all visible selected items.
1498 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1500 ITERATOR i;
1502 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1503 while(iterator_next(&i))
1505 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1506 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1508 iterator_destroy(&i);
1512 /***
1513 * DESCRIPTION:
1514 * Prints a message for unsupported window styles.
1515 * A kind of TODO list for window styles.
1517 * PARAMETER(S):
1518 * [I] LONG : window style
1520 * RETURN:
1521 * None
1523 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1525 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1526 FIXME(" LVS_NOSCROLL\n");
1528 if (lStyle & LVS_NOLABELWRAP)
1529 FIXME(" LVS_NOLABELWRAP\n");
1531 if (lStyle & LVS_SORTASCENDING)
1532 FIXME(" LVS_SORTASCENDING\n");
1534 if (lStyle & LVS_SORTDESCENDING)
1535 FIXME(" LVS_SORTDESCENDING\n");
1539 /***
1540 * DESCRIPTION: [INTERNAL]
1541 * Computes an item's (left,top) corner, relative to rcView.
1542 * That is, the position has NOT been made relative to the Origin.
1543 * This is deliberate, to avoid computing the Origin over, and
1544 * over again, when this function is call in a loop. Instead,
1545 * one ca factor the computation of the Origin before the loop,
1546 * and offset the value retured by this function, on every iteration.
1548 * PARAMETER(S):
1549 * [I] infoPtr : valid pointer to the listview structure
1550 * [I] nItem : item number
1551 * [O] lpptOrig : item top, left corner
1553 * RETURN:
1554 * TRUE if computations OK
1555 * FALSE otherwise
1557 static BOOL LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1559 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1561 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
1563 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1565 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1566 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1568 else if (uView == LVS_LIST)
1570 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1571 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1572 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1574 else /* LVS_REPORT */
1576 lpptPosition->x = REPORT_MARGINX;
1577 lpptPosition->y = nItem * infoPtr->nItemHeight;
1580 return TRUE;
1583 /***
1584 * DESCRIPTION: [INTERNAL]
1585 * Compute the rectangles of an item. This is to localize all
1586 * the computations in one place. If you are not interested in some
1587 * of these values, simply pass in a NULL -- the fucntion is smart
1588 * enough to compute only what's necessary. The function computes
1589 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1590 * one, the BOX rectangle. This rectangle is very cheap to compute,
1591 * and is guaranteed to contain all the other rectangles. Computing
1592 * the ICON rect is also cheap, but all the others are potentaily
1593 * expensive. This gives an easy and effective optimization when
1594 * searching (like point inclusion, or rectangle intersection):
1595 * first test against the BOX, and if TRUE, test agains the desired
1596 * rectangle.
1597 * If the function does not have all the necessary information
1598 * to computed the requested rectangles, will crash with a
1599 * failed assertion. This is done so we catch all programming
1600 * errors, given that the function is called only from our code.
1602 * We have the following 'special' meanings for a few fields:
1603 * * If LVIS_FOCUSED is set, we assume the item has the focus
1604 * This is important in ICON mode, where it might get a larger
1605 * then usual rectange
1607 * Please note that subitem support works only in REPORT mode.
1609 * PARAMETER(S):
1610 * [I] infoPtr : valid pointer to the listview structure
1611 * [I] lpLVItem : item to compute the measures for
1612 * [O] lprcBox : ptr to Box rectangle
1613 * The internal LVIR_BOX rectangle
1614 * [0] lprcState : ptr to State icon rectangle
1615 * The internal LVIR_STATE rectangle
1616 * [O] lprcIcon : ptr to Icon rectangle
1617 * Same as LVM_GETITEMRECT with LVIR_ICON
1618 * [O] lprcLabel : ptr to Label rectangle
1619 * Same as LVM_GETITEMRECT with LVIR_LABEL
1621 * RETURN:
1622 * TRUE if computations OK
1623 * FALSE otherwise
1625 static BOOL LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem,
1626 LPRECT lprcBox, LPRECT lprcState,
1627 LPRECT lprcIcon, LPRECT lprcLabel)
1629 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1630 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1631 RECT Box, State, Icon, Label;
1633 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1635 /* Be smart and try to figure out the minimum we have to do */
1636 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1637 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1639 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1640 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1642 if (lprcLabel) doLabel = TRUE;
1643 if (doLabel || lprcIcon) doIcon = TRUE;
1644 if (doIcon || lprcState) doState = TRUE;
1646 /************************************************************/
1647 /* compute the box rectangle (it should be cheap to do) */
1648 /************************************************************/
1649 if (lpLVItem->iSubItem)
1651 if (!LISTVIEW_GetHeaderRect(infoPtr, lpLVItem->iSubItem, &Box)) return FALSE;
1653 else
1655 Box.left = 0;
1656 Box.right = infoPtr->nItemWidth;
1658 Box.top = 0;
1659 Box.bottom = infoPtr->nItemHeight;
1661 /************************************************************/
1662 /* compute STATEICON bounding box */
1663 /************************************************************/
1664 if (doState)
1666 if (uView == LVS_ICON)
1668 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1669 if (infoPtr->himlNormal)
1670 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1671 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1673 else
1675 /* we need the ident in report mode, if we don't have it, we fail */
1676 State.left = Box.left;
1677 if (uView == LVS_REPORT)
1679 State.left += REPORT_MARGINX;
1680 if (lpLVItem->iSubItem == 0)
1682 assert(lpLVItem->mask & LVIF_INDENT);
1683 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1686 State.top = Box.top;
1688 State.right = State.left;
1689 State.bottom = State.top;
1690 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1692 State.right += infoPtr->iconStateSize.cx;
1693 State.bottom += infoPtr->iconStateSize.cy;
1695 if (lprcState) *lprcState = State;
1696 TRACE(" - state=%s\n", debugrect(&State));
1699 /************************************************************/
1700 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1701 /************************************************************/
1702 if (doIcon)
1704 if (uView == LVS_ICON)
1706 Icon.left = Box.left;
1707 if (infoPtr->himlNormal)
1708 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1709 Icon.top = Box.top + ICON_TOP_PADDING;
1710 Icon.right = Icon.left;
1711 Icon.bottom = Icon.top;
1712 if (infoPtr->himlNormal)
1714 Icon.right += infoPtr->iconSize.cx;
1715 Icon.bottom += infoPtr->iconSize.cy;
1718 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1720 Icon.left = State.right;
1721 if (!IsRectEmpty(&State)) Icon.left += IMAGE_PADDING;
1722 Icon.top = Box.top;
1723 Icon.right = Icon.left;
1724 /* FIXME: add suport for icons for subitems */
1725 if (infoPtr->himlSmall && lpLVItem->iSubItem == 0) Icon.right += infoPtr->iconSize.cx;
1726 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1728 if(lprcIcon) *lprcIcon = Icon;
1729 TRACE(" - icon=%s\n", debugrect(&Icon));
1732 /************************************************************/
1733 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1734 /************************************************************/
1735 if (doLabel)
1737 SIZE labelSize = { 0, 0 };
1739 /* calculate how far to the right can the label strech */
1740 Label.right = Box.right;
1741 if (uView == LVS_REPORT)
1743 if (lpLVItem->iSubItem == 0 && !LISTVIEW_GetHeaderRect(infoPtr, 0, &Label)) return FALSE;
1744 Label.right -= REPORT_MARGINX;
1747 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1749 labelSize.cx = infoPtr->nItemWidth;
1750 labelSize.cy = infoPtr->nItemHeight;
1751 goto calc_label;
1754 /* we need the text in non owner draw mode */
1755 assert(lpLVItem->mask & LVIF_TEXT);
1756 if (is_textT(lpLVItem->pszText, TRUE))
1758 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1759 HDC hdc = GetDC(infoPtr->hwndSelf);
1760 HFONT hOldFont = SelectObject(hdc, hFont);
1761 UINT uFormat;
1762 RECT rcText;
1764 /* compute rough rectangle where the label will go */
1765 SetRectEmpty(&rcText);
1766 rcText.right = infoPtr->nItemWidth - TRAILING_PADDING;
1767 rcText.bottom = infoPtr->nItemHeight;
1768 if (uView == LVS_ICON)
1769 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1771 /* now figure out the flags */
1772 if (uView == LVS_ICON)
1773 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1774 else
1775 uFormat = LV_SL_DT_FLAGS;
1777 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1779 labelSize.cx = min(rcText.right - rcText.left + TRAILING_PADDING, infoPtr->nItemWidth);
1780 labelSize.cy = rcText.bottom - rcText.top;
1782 SelectObject(hdc, hOldFont);
1783 ReleaseDC(infoPtr->hwndSelf, hdc);
1786 calc_label:
1787 if (uView == LVS_ICON)
1789 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1790 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1791 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1792 Label.right = Label.left + labelSize.cx;
1793 Label.bottom = Label.top + infoPtr->nItemHeight;
1794 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1796 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1797 labelSize.cy /= infoPtr->ntmHeight;
1798 labelSize.cy = max(labelSize.cy, 1);
1799 labelSize.cy *= infoPtr->ntmHeight;
1801 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1803 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1805 Label.left = Icon.right;
1806 if (!IsRectEmpty(&Icon) || !IsRectEmpty(&State)) Label.left += IMAGE_PADDING;
1807 Label.top = Box.top;
1808 Label.right = min(Label.left + labelSize.cx, Label.right);
1809 Label.bottom = Label.top + infoPtr->nItemHeight;
1812 if (lprcLabel) *lprcLabel = Label;
1813 TRACE(" - label=%s\n", debugrect(&Label));
1816 /* Fix the Box if necessary */
1817 if (lprcBox)
1819 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1820 else *lprcBox = Box;
1822 TRACE(" - box=%s\n", debugrect(&Box));
1824 return TRUE;
1827 /***
1828 * DESCRIPTION: [INTERNAL]
1830 * PARAMETER(S):
1831 * [I] infoPtr : valid pointer to the listview structure
1832 * [I] nItem : item number
1833 * [O] lprcBox : ptr to Box rectangle
1835 * RETURN:
1836 * TRUE if computations OK
1837 * FALSE otherwise
1839 static BOOL LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
1841 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1842 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1843 POINT Position, Origin;
1844 LVITEMW lvItem;
1846 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) return FALSE;
1847 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
1849 /* Be smart and try to figure out the minimum we have to do */
1850 lvItem.mask = 0;
1851 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1852 lvItem.mask |= LVIF_TEXT;
1853 lvItem.iItem = nItem;
1854 lvItem.iSubItem = 0;
1855 lvItem.pszText = szDispText;
1856 lvItem.cchTextMax = DISP_TEXT_SIZE;
1857 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
1858 if (uView == LVS_ICON)
1860 lvItem.mask |= LVIF_STATE;
1861 lvItem.stateMask = LVIS_FOCUSED;
1862 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
1864 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0)) return FALSE;
1866 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
1868 return TRUE;
1871 /***
1872 * DESCRIPTION:
1873 * Aligns the items with the top edge of the window.
1875 * PARAMETER(S):
1876 * [I] infoPtr : valid pointer to the listview structure
1878 * RETURN:
1879 * None
1881 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1883 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1884 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1885 POINT ptItem;
1886 RECT rcView;
1887 INT i, off_x=0, off_y=0;
1889 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1891 /* Since SetItemPosition uses upper-left of icon, and for
1892 style=LVS_ICON the icon is not left adjusted, get the offset */
1893 if (uView == LVS_ICON)
1895 off_y = ICON_TOP_PADDING;
1896 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1898 ptItem.x = off_x;
1899 ptItem.y = off_y;
1900 ZeroMemory(&rcView, sizeof(RECT));
1901 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1902 off_x, off_y,
1903 infoPtr->rcList.left, infoPtr->rcList.right);
1905 if (nListWidth > infoPtr->nItemWidth)
1907 for (i = 0; i < infoPtr->nItemCount; i++)
1909 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1911 ptItem.x = off_x;
1912 ptItem.y += infoPtr->nItemHeight;
1915 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1916 ptItem.x += infoPtr->nItemWidth;
1917 rcView.right = max(rcView.right, ptItem.x);
1920 rcView.right -= off_x;
1921 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1923 else
1925 for (i = 0; i < infoPtr->nItemCount; i++)
1927 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1928 ptItem.y += infoPtr->nItemHeight;
1931 rcView.right = infoPtr->nItemWidth;
1932 rcView.bottom = ptItem.y-off_y;
1935 infoPtr->rcView = rcView;
1939 /***
1940 * DESCRIPTION:
1941 * Aligns the items with the left edge of the window.
1943 * PARAMETER(S):
1944 * [I] infoPtr : valid pointer to the listview structure
1946 * RETURN:
1947 * None
1949 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1951 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1952 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1953 POINT ptItem;
1954 RECT rcView;
1955 INT i, off_x=0, off_y=0;
1957 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1959 /* Since SetItemPosition uses upper-left of icon, and for
1960 style=LVS_ICON the icon is not left adjusted, get the offset */
1961 if (uView == LVS_ICON)
1963 off_y = ICON_TOP_PADDING;
1964 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1966 ptItem.x = off_x;
1967 ptItem.y = off_y;
1968 ZeroMemory(&rcView, sizeof(RECT));
1969 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1971 if (nListHeight > infoPtr->nItemHeight)
1973 for (i = 0; i < infoPtr->nItemCount; i++)
1975 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1977 ptItem.y = off_y;
1978 ptItem.x += infoPtr->nItemWidth;
1981 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1982 ptItem.y += infoPtr->nItemHeight;
1983 rcView.bottom = max(rcView.bottom, ptItem.y);
1986 rcView.right = ptItem.x + infoPtr->nItemWidth;
1988 else
1990 for (i = 0; i < infoPtr->nItemCount; i++)
1992 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1993 ptItem.x += infoPtr->nItemWidth;
1996 rcView.bottom = infoPtr->nItemHeight;
1997 rcView.right = ptItem.x;
2000 infoPtr->rcView = rcView;
2005 /***
2006 * DESCRIPTION:
2007 * Retrieves the bounding rectangle of all the items.
2009 * PARAMETER(S):
2010 * [I] infoPtr : valid pointer to the listview structure
2011 * [O] lprcView : bounding rectangle
2013 * RETURN:
2014 * SUCCESS : TRUE
2015 * FAILURE : FALSE
2017 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2019 POINT ptOrigin;
2021 TRACE("(lprcView=%p)\n", lprcView);
2023 if (!lprcView) return FALSE;
2025 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return FALSE;
2027 *lprcView = infoPtr->rcView;
2028 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2030 TRACE("lprcView=%s\n", debugrect(lprcView));
2032 return TRUE;
2035 /***
2036 * DESCRIPTION:
2037 * Retrieves the subitem pointer associated with the subitem index.
2039 * PARAMETER(S):
2040 * [I] HDPA : DPA handle for a specific item
2041 * [I] INT : index of subitem
2043 * RETURN:
2044 * SUCCESS : subitem pointer
2045 * FAILURE : NULL
2047 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2049 LISTVIEW_SUBITEM *lpSubItem;
2050 INT i;
2052 /* we should binary search here if need be */
2053 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2055 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
2056 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
2057 return lpSubItem;
2060 return NULL;
2064 /***
2065 * DESCRIPTION:
2066 * Calculates the width of a specific item.
2068 * PARAMETER(S):
2069 * [I] infoPtr : valid pointer to the listview structure
2070 * [I] nItem : item to calculate width, or -1 for max of all
2072 * RETURN:
2073 * Returns the width of an item width an item.
2075 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr, INT nItem)
2077 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2078 INT nItemWidth = 0, i;
2080 if (uView == LVS_ICON)
2081 nItemWidth = infoPtr->iconSpacing.cx;
2082 else if (uView == LVS_REPORT)
2084 RECT rcHeaderItem;
2086 /* calculate width of header */
2087 for (i = 0; i < infoPtr->hdpaColumns->nItemCount; i++)
2088 if (LISTVIEW_GetHeaderRect(infoPtr, i, &rcHeaderItem))
2089 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
2091 else
2093 INT nLabelWidth;
2095 if (infoPtr->nItemCount == 0) return DEFAULT_COLUMN_WIDTH;
2097 /* get width of string */
2098 if (nItem == -1)
2100 for (i = 0; i < infoPtr->nItemCount; i++)
2102 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
2103 nItemWidth = max(nItemWidth, nLabelWidth);
2106 else
2107 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
2108 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
2109 nItemWidth += WIDTH_PADDING;
2110 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2111 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2112 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
2115 return max(nItemWidth, 1);
2118 /***
2119 * DESCRIPTION:
2120 * Calculates the max width of any item in the list.
2122 * PARAMETER(S):
2123 * [I] infoPtr : valid pointer to the listview structure
2124 * [I] LONG : window style
2126 * RETURN:
2127 * Returns item width.
2129 static inline INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *infoPtr)
2131 return LISTVIEW_CalculateItemWidth(infoPtr, -1);
2134 /***
2135 * DESCRIPTION:
2136 * Retrieves and saves important text metrics info for the current
2137 * Listview font.
2139 * PARAMETER(S):
2140 * [I] infoPtr : valid pointer to the listview structure
2143 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2145 HDC hdc = GetDC(infoPtr->hwndSelf);
2146 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2147 HFONT hOldFont = SelectObject(hdc, hFont);
2148 TEXTMETRICW tm;
2150 if (GetTextMetricsW(hdc, &tm))
2151 infoPtr->ntmHeight = tm.tmHeight;
2152 SelectObject(hdc, hOldFont);
2153 ReleaseDC(infoPtr->hwndSelf, hdc);
2155 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2159 /***
2160 * DESCRIPTION:
2161 * Calculates the height of an item.
2163 * PARAMETER(S):
2164 * [I] infoPtr : valid pointer to the listview structure
2166 * RETURN:
2167 * Returns item height.
2169 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *infoPtr)
2171 INT nItemHeight;
2173 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
2174 nItemHeight = infoPtr->iconSpacing.cy;
2175 else
2177 nItemHeight = infoPtr->ntmHeight;
2178 if (infoPtr->himlState)
2179 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2180 if (infoPtr->himlSmall)
2181 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2182 if (infoPtr->himlState || infoPtr->himlSmall)
2183 nItemHeight += HEIGHT_PADDING;
2185 return nItemHeight;
2188 #if 0
2189 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
2191 ERR("Selections are:\n");
2192 ranges_dump(infoPtr->selectionRanges);
2194 #endif
2196 /***
2197 * DESCRIPTION:
2198 * A compare function for ranges
2200 * PARAMETER(S)
2201 * [I] range1 : pointer to range 1;
2202 * [I] range2 : pointer to range 2;
2203 * [I] flags : flags
2205 * RETURNS:
2206 * > 0 : if range 1 > range 2
2207 * < 0 : if range 2 > range 1
2208 * = 0 : if range intersects range 2
2210 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2212 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2213 return -1;
2214 if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2215 return 1;
2216 return 0;
2219 static RANGES ranges_create(int count)
2221 RANGES ranges = (RANGES)COMCTL32_Alloc(sizeof(struct tagRANGES));
2222 if (!ranges) return NULL;
2223 ranges->hdpa = DPA_Create(count);
2224 if (ranges->hdpa) return ranges;
2225 COMCTL32_Free(ranges);
2226 return NULL;
2229 static void ranges_destroy(RANGES ranges)
2231 if (!ranges) return;
2232 if (ranges->hdpa)
2234 INT i;
2236 for(i = 0; i < ranges->hdpa->nItemCount; i++)
2237 COMCTL32_Free(DPA_GetPtr(ranges->hdpa, i));
2238 DPA_Destroy(ranges->hdpa);
2239 ranges->hdpa = NULL;
2241 COMCTL32_Free(ranges);
2244 static RANGES ranges_clone(RANGES ranges)
2246 RANGES clone;
2247 INT i;
2249 if (!ranges) return NULL;
2250 clone = ranges_create(ranges->hdpa->nItemCount);
2251 if (!clone) return NULL;
2253 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2255 RANGE *newrng = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2256 if (!newrng)
2258 ranges_destroy(clone);
2259 return NULL;
2261 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2262 DPA_InsertPtr(clone->hdpa, i, newrng);
2264 return clone;
2267 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2269 INT i;
2271 if (!ranges || !sub) return ranges;
2273 for (i = 0; i < sub->hdpa->nItemCount; i++)
2274 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2276 return ranges;
2279 static void ranges_dump(RANGES ranges)
2281 INT i;
2283 if (!ranges) return;
2284 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2285 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2288 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2290 RANGE srchrng = { nItem, nItem + 1 };
2292 TRACE("(nItem=%d)\n", nItem);
2293 if (!ranges) return FALSE;
2294 if (TRACE_ON(listview)) ranges_dump(ranges);
2295 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2298 static INT ranges_itemcount(RANGES ranges)
2300 INT i, count = 0;
2302 if (!ranges) return 0;
2303 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2305 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2306 count += sel->upper - sel->lower;
2309 return count;
2312 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2314 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2315 INT index;
2317 if (!ranges) return FALSE;
2318 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2319 if (index == -1) return TRUE;
2321 for (;index < ranges->hdpa->nItemCount; index++)
2323 chkrng = DPA_GetPtr(ranges->hdpa, index);
2324 if (chkrng->lower >= nItem)
2325 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2326 if (chkrng->upper > nItem)
2327 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2329 return TRUE;
2332 static BOOL ranges_add(RANGES ranges, RANGE range)
2334 RANGE srchrgn;
2335 INT index;
2337 TRACE("(%s)\n", debugrange(&range));
2338 if (!ranges) return FALSE;
2339 if (TRACE_ON(listview)) ranges_dump(ranges);
2341 /* try find overlapping regions first */
2342 srchrgn.lower = range.lower - 1;
2343 srchrgn.upper = range.upper + 1;
2344 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, 0);
2346 if (index == -1)
2348 RANGE *newrgn;
2350 TRACE("Adding new range\n");
2352 /* create the brand new range to insert */
2353 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2354 if(!newrgn) return FALSE;
2355 *newrgn = range;
2357 /* figure out where to insert it */
2358 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_INSERTAFTER);
2359 if (index == -1) index = 0;
2361 /* and get it over with */
2362 DPA_InsertPtr(ranges->hdpa, index, newrgn);
2364 else
2366 RANGE *chkrgn, *mrgrgn;
2367 INT fromindex, mergeindex;
2369 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2370 if (!chkrgn) return FALSE;
2371 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2373 chkrgn->lower = min(range.lower, chkrgn->lower);
2374 chkrgn->upper = max(range.upper, chkrgn->upper);
2376 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2378 /* merge now common anges */
2379 fromindex = 0;
2380 srchrgn.lower = chkrgn->lower - 1;
2381 srchrgn.upper = chkrgn->upper + 1;
2385 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2386 if (mergeindex == -1) break;
2387 if (mergeindex == index)
2389 fromindex = index + 1;
2390 continue;
2393 TRACE("Merge with index %i\n", mergeindex);
2395 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2396 if (!mrgrgn) return FALSE;
2398 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2399 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2400 COMCTL32_Free(mrgrgn);
2401 DPA_DeletePtr(ranges->hdpa, mergeindex);
2402 if (mergeindex < index) index --;
2403 } while(1);
2406 if (TRACE_ON(listview)) ranges_dump(ranges);
2407 return TRUE;
2410 static BOOL ranges_del(RANGES ranges, RANGE range)
2412 RANGE remrgn, tmprgn, *chkrgn;
2413 BOOL done = FALSE;
2414 INT index;
2416 TRACE("(%s)\n", debugrange(&range));
2417 if (!ranges) return FALSE;
2419 remrgn = range;
2422 index = DPA_Search(ranges->hdpa, &remrgn, 0, ranges_cmp, 0, 0);
2423 if (index == -1) return TRUE;
2425 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2426 if (!chkrgn) return FALSE;
2428 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2430 /* case 1: Same range */
2431 if ( (chkrgn->upper == remrgn.upper) &&
2432 (chkrgn->lower == remrgn.lower) )
2434 DPA_DeletePtr(ranges->hdpa, index);
2435 done = TRUE;
2437 /* case 2: engulf */
2438 else if ( (chkrgn->upper <= remrgn.upper) &&
2439 (chkrgn->lower >= remrgn.lower) )
2441 DPA_DeletePtr(ranges->hdpa, index);
2443 /* case 3: overlap upper */
2444 else if ( (chkrgn->upper <= remrgn.upper) &&
2445 (chkrgn->lower < remrgn.lower) )
2447 chkrgn->upper = remrgn.lower;
2449 /* case 4: overlap lower */
2450 else if ( (chkrgn->upper > remrgn.upper) &&
2451 (chkrgn->lower >= remrgn.lower) )
2453 chkrgn->lower = remrgn.upper;
2455 /* case 5: fully internal */
2456 else
2458 RANGE *newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2459 if (!newrgn) return FALSE;
2460 tmprgn = *chkrgn;
2461 newrgn->lower = chkrgn->lower;
2462 newrgn->upper = remrgn.lower;
2463 chkrgn->lower = remrgn.upper;
2464 DPA_InsertPtr(ranges->hdpa, index, newrgn);
2465 chkrgn = &tmprgn;
2468 while(!done);
2470 return TRUE;
2473 /***
2474 * DESCRIPTION:
2475 * Removes all selection ranges
2477 * Parameters(s):
2478 * [I] infoPtr : valid pointer to the listview structure
2479 * [I] toSkip : item range to skip removing the selection
2481 * RETURNS:
2482 * SUCCESS : TRUE
2483 * FAILURE : TRUE
2485 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2487 LVITEMW lvItem;
2488 ITERATOR i;
2490 TRACE("()\n");
2491 if (TRACE_ON(listview)) ranges_dump(toSkip);
2493 lvItem.state = 0;
2494 lvItem.stateMask = LVIS_SELECTED;
2496 /* need to clone the DPA because callbacks can change it */
2497 iterator_ranges(&i, ranges_diff(ranges_clone(infoPtr->selectionRanges), toSkip));
2498 while(iterator_next(&i))
2499 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2500 /* note that the iterator destructor will free the cloned range */
2501 iterator_destroy(&i);
2503 return TRUE;
2506 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2508 RANGES toSkip = ranges_create(1);
2510 if (!toSkip) return FALSE;
2511 ranges_additem(toSkip, nItem);
2512 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2513 ranges_destroy(toSkip);
2514 return TRUE;
2517 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2519 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2522 /***
2523 * DESCRIPTION:
2524 * Retrieves the number of items that are marked as selected.
2526 * PARAMETER(S):
2527 * [I] infoPtr : valid pointer to the listview structure
2529 * RETURN:
2530 * Number of items selected.
2532 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2534 INT nSelectedCount = 0;
2536 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2538 INT i;
2539 for (i = 0; i < infoPtr->nItemCount; i++)
2541 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2542 nSelectedCount++;
2545 else
2546 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2548 TRACE("nSelectedCount=%d\n", nSelectedCount);
2549 return nSelectedCount;
2552 /***
2553 * DESCRIPTION:
2554 * Manages the item focus.
2556 * PARAMETER(S):
2557 * [I] infoPtr : valid pointer to the listview structure
2558 * [I] INT : item index
2560 * RETURN:
2561 * TRUE : focused item changed
2562 * FALSE : focused item has NOT changed
2564 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2566 INT oldFocus = infoPtr->nFocusedItem;
2567 LVITEMW lvItem;
2569 if (nItem == infoPtr->nFocusedItem) return FALSE;
2571 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2572 lvItem.stateMask = LVIS_FOCUSED;
2573 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2575 return oldFocus != infoPtr->nFocusedItem;
2578 /* Helper function for LISTVIEW_ShiftIndices *only* */
2579 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2581 if (nShiftItem < nItem) return nShiftItem;
2583 if (nShiftItem > nItem) return nShiftItem + direction;
2585 if (direction > 0) return nShiftItem + direction;
2587 return min(nShiftItem, infoPtr->nItemCount - 1);
2591 * DESCRIPTION:
2592 * Updates the various indices after an item has been inserted or deleted.
2594 * PARAMETER(S):
2595 * [I] infoPtr : valid pointer to the listview structure
2596 * [I] nItem : item index
2597 * [I] direction : Direction of shift, +1 or -1.
2599 * RETURN:
2600 * None
2602 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2604 INT nNewFocus;
2606 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2608 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2610 assert(abs(direction) == 1);
2612 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2614 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2615 if (nNewFocus != infoPtr->nFocusedItem)
2616 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2618 /* But we are not supposed to modify nHotItem! */
2623 * DESCRIPTION:
2624 * Adds a block of selections.
2626 * PARAMETER(S):
2627 * [I] infoPtr : valid pointer to the listview structure
2628 * [I] INT : item index
2630 * RETURN:
2631 * None
2633 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2635 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2636 INT nLast = max(infoPtr->nSelectionMark, nItem);
2637 INT i;
2638 LVITEMW item;
2640 if (nFirst == -1)
2641 nFirst = nItem;
2643 item.state = LVIS_SELECTED;
2644 item.stateMask = LVIS_SELECTED;
2646 /* FIXME: this is not correct LVS_OWNERDATA
2647 * See docu for LVN_ITEMCHANGED. Is there something similar for
2648 * RemoveGroupSelection (is there such a thing?)?
2650 for (i = nFirst; i <= nLast; i++)
2651 LISTVIEW_SetItemState(infoPtr,i,&item);
2653 LISTVIEW_SetItemFocus(infoPtr, nItem);
2654 infoPtr->nSelectionMark = nItem;
2658 /***
2659 * DESCRIPTION:
2660 * Sets a single group selection.
2662 * PARAMETER(S):
2663 * [I] infoPtr : valid pointer to the listview structure
2664 * [I] INT : item index
2666 * RETURN:
2667 * None
2669 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2671 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2672 RANGES selection;
2673 LVITEMW item;
2674 ITERATOR i;
2676 if (!(selection = ranges_create(100))) return;
2678 item.state = LVIS_SELECTED;
2679 item.stateMask = LVIS_SELECTED;
2681 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2683 if (infoPtr->nSelectionMark == -1)
2685 infoPtr->nSelectionMark = nItem;
2686 ranges_additem(selection, nItem);
2688 else
2690 RANGE sel;
2692 sel.lower = min(infoPtr->nSelectionMark, nItem);
2693 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
2694 ranges_add(selection, sel);
2697 else
2699 RECT rcItem, rcSel, rcSelMark;
2700 POINT ptItem;
2702 rcItem.left = LVIR_BOUNDS;
2703 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2704 rcSelMark.left = LVIR_BOUNDS;
2705 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2706 UnionRect(&rcSel, &rcItem, &rcSelMark);
2707 iterator_frameditems(&i, infoPtr, &rcSel);
2708 while(iterator_next(&i))
2710 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
2711 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
2713 iterator_destroy(&i);
2716 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
2717 iterator_ranges(&i, selection);
2718 while(iterator_next(&i))
2719 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
2720 /* this will also destroy the selection */
2721 iterator_destroy(&i);
2723 LISTVIEW_SetItemFocus(infoPtr, nItem);
2726 /***
2727 * DESCRIPTION:
2728 * Sets a single selection.
2730 * PARAMETER(S):
2731 * [I] infoPtr : valid pointer to the listview structure
2732 * [I] INT : item index
2734 * RETURN:
2735 * None
2737 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2739 LVITEMW lvItem;
2741 TRACE("nItem=%d\n", nItem);
2743 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
2745 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2746 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2747 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2749 infoPtr->nSelectionMark = nItem;
2752 /***
2753 * DESCRIPTION:
2754 * Set selection(s) with keyboard.
2756 * PARAMETER(S):
2757 * [I] infoPtr : valid pointer to the listview structure
2758 * [I] INT : item index
2760 * RETURN:
2761 * SUCCESS : TRUE (needs to be repainted)
2762 * FAILURE : FALSE (nothing has changed)
2764 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2766 /* FIXME: pass in the state */
2767 LONG lStyle = infoPtr->dwStyle;
2768 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2769 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2770 BOOL bResult = FALSE;
2772 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2774 if (lStyle & LVS_SINGLESEL)
2776 bResult = TRUE;
2777 LISTVIEW_SetSelection(infoPtr, nItem);
2779 else
2781 if (wShift)
2783 bResult = TRUE;
2784 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2786 else if (wCtrl)
2788 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2790 else
2792 bResult = TRUE;
2793 LISTVIEW_SetSelection(infoPtr, nItem);
2796 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2799 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2800 return bResult;
2804 /***
2805 * DESCRIPTION:
2806 * Called when the mouse is being actively tracked and has hovered for a specified
2807 * amount of time
2809 * PARAMETER(S):
2810 * [I] infoPtr : valid pointer to the listview structure
2811 * [I] fwKeys : key indicator
2812 * [I] pts : mouse position
2814 * RETURN:
2815 * 0 if the message was processed, non-zero if there was an error
2817 * INFO:
2818 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2819 * over the item for a certain period of time.
2822 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2824 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2825 /* FIXME: select the item!!! */
2826 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2828 return 0;
2831 /***
2832 * DESCRIPTION:
2833 * Called whenever WM_MOUSEMOVE is received.
2835 * PARAMETER(S):
2836 * [I] infoPtr : valid pointer to the listview structure
2837 * [I] fwKeys : key indicator
2838 * [I] pts : mouse position
2840 * RETURN:
2841 * 0 if the message is processed, non-zero if there was an error
2843 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2845 TRACKMOUSEEVENT trackinfo;
2847 /* see if we are supposed to be tracking mouse hovering */
2848 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2849 /* fill in the trackinfo struct */
2850 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2851 trackinfo.dwFlags = TME_QUERY;
2852 trackinfo.hwndTrack = infoPtr->hwndSelf;
2853 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2855 /* see if we are already tracking this hwnd */
2856 _TrackMouseEvent(&trackinfo);
2858 if(!(trackinfo.dwFlags & TME_HOVER)) {
2859 trackinfo.dwFlags = TME_HOVER;
2861 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2862 _TrackMouseEvent(&trackinfo);
2866 return 0;
2870 /***
2871 * Tests wheather the item is assignable to a list with style lStyle
2873 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2875 if ( (lpLVItem->mask & LVIF_TEXT) &&
2876 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2877 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2879 return TRUE;
2883 /***
2884 * DESCRIPTION:
2885 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2887 * PARAMETER(S):
2888 * [I] infoPtr : valid pointer to the listview structure
2889 * [I] lpLVItem : valid pointer to new item atttributes
2890 * [I] isNew : the item being set is being inserted
2891 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2892 * [O] bChanged : will be set to TRUE if the item really changed
2894 * RETURN:
2895 * SUCCESS : TRUE
2896 * FAILURE : FALSE
2898 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
2900 LISTVIEW_ITEM *lpItem;
2901 NMLISTVIEW nmlv;
2902 UINT uChanged = 0;
2903 LVITEMW item;
2905 TRACE("()\n");
2907 if (lpLVItem->mask == 0) return TRUE;
2909 if (infoPtr->dwStyle & LVS_OWNERDATA)
2911 /* a virtual listview we stores only selection and focus */
2912 if ((lpLVItem->mask & ~LVIF_STATE) || (lpLVItem->stateMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
2913 return FALSE;
2914 lpItem = NULL;
2916 else
2918 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2919 if (!hdpaSubItems) return FALSE;
2920 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0))) return FALSE;
2923 /* we need to get the lParam and state of the item */
2924 item.iItem = lpLVItem->iItem;
2925 item.iSubItem = lpLVItem->iSubItem;
2926 item.mask = LVIF_STATE | LVIF_PARAM;
2927 item.stateMask = ~0;
2928 item.state = 0;
2929 item.lParam = 0;
2930 if (!isNew && !LISTVIEW_GetItemT(infoPtr, &item, TRUE)) return FALSE;
2932 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
2933 /* determine what fields will change */
2934 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
2935 uChanged |= LVIF_STATE;
2937 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2938 uChanged |= LVIF_IMAGE;
2940 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2941 uChanged |= LVIF_PARAM;
2943 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2944 uChanged |= LVIF_INDENT;
2946 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2947 uChanged |= LVIF_TEXT;
2949 TRACE("uChanged=0x%x\n", uChanged);
2950 if (!uChanged) return TRUE;
2951 *bChanged = TRUE;
2953 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2954 nmlv.iItem = lpLVItem->iItem;
2955 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
2956 nmlv.uOldState = item.state;
2957 nmlv.uChanged = uChanged;
2958 nmlv.lParam = item.lParam;
2960 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2961 /* and we are _NOT_ virtual (LVS_OWERNDATA) */
2962 if(lpItem && !isNew && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
2963 return FALSE;
2965 /* copy information */
2966 if (lpLVItem->mask & LVIF_TEXT)
2967 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
2969 if (lpLVItem->mask & LVIF_IMAGE)
2970 lpItem->hdr.iImage = lpLVItem->iImage;
2972 if (lpLVItem->mask & LVIF_PARAM)
2973 lpItem->lParam = lpLVItem->lParam;
2975 if (lpLVItem->mask & LVIF_INDENT)
2976 lpItem->iIndent = lpLVItem->iIndent;
2978 if (uChanged & LVIF_STATE)
2980 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED))
2982 lpItem->state &= ~lpLVItem->stateMask;
2983 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2985 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
2987 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
2988 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
2990 else if (lpLVItem->stateMask & LVIS_SELECTED)
2991 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
2993 /* if we are asked to change focus, and we manage it, do it */
2994 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
2996 if (lpLVItem->state & LVIS_FOCUSED)
2998 LISTVIEW_SetItemFocus(infoPtr, -1);
2999 infoPtr->nFocusedItem = lpLVItem->iItem;
3000 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
3002 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3003 infoPtr->nFocusedItem = -1;
3007 /* if we're inserting the item, we're done */
3008 if (isNew) return TRUE;
3010 /* send LVN_ITEMCHANGED notification */
3011 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3012 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3014 return TRUE;
3017 /***
3018 * DESCRIPTION:
3019 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3021 * PARAMETER(S):
3022 * [I] infoPtr : valid pointer to the listview structure
3023 * [I] lpLVItem : valid pointer to new subitem atttributes
3024 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3025 * [O] bChanged : will be set to TRUE if the item really changed
3027 * RETURN:
3028 * SUCCESS : TRUE
3029 * FAILURE : FALSE
3031 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged)
3033 HDPA hdpaSubItems;
3034 LISTVIEW_SUBITEM *lpSubItem;
3036 /* we do not support subitems for virtual listviews */
3037 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3039 /* set subitem only if column is present */
3040 if (lpLVItem->iSubItem >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3042 /* First do some sanity checks */
3043 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3044 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3046 /* get the subitem structure, and create it if not there */
3047 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3048 if (!hdpaSubItems) return FALSE;
3050 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3051 if (!lpSubItem)
3053 LISTVIEW_SUBITEM *tmpSubItem;
3054 INT i;
3056 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
3057 if (!lpSubItem) return FALSE;
3058 /* we could binary search here, if need be...*/
3059 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3061 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3062 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3064 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3066 COMCTL32_Free(lpSubItem);
3067 return FALSE;
3069 lpSubItem->iSubItem = lpLVItem->iSubItem;
3070 *bChanged = TRUE;
3073 if (lpLVItem->mask & LVIF_IMAGE)
3074 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3076 lpSubItem->hdr.iImage = lpLVItem->iImage;
3077 *bChanged = TRUE;
3080 if (lpLVItem->mask & LVIF_TEXT)
3081 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3083 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3084 *bChanged = TRUE;
3087 return TRUE;
3090 /***
3091 * DESCRIPTION:
3092 * Sets item attributes.
3094 * PARAMETER(S):
3095 * [I] infoPtr : valid pointer to the listview structure
3096 * [I] LPLVITEM : new item atttributes
3097 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3099 * RETURN:
3100 * SUCCESS : TRUE
3101 * FAILURE : FALSE
3103 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
3105 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3106 LPWSTR pszText = NULL;
3107 BOOL bResult, bChanged = FALSE;
3109 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3111 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3112 return FALSE;
3114 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3115 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3117 pszText = lpLVItem->pszText;
3118 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3121 /* actually set the fields */
3122 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3124 if (lpLVItem->iSubItem)
3125 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3126 else
3127 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3129 /* redraw item, if necessary */
3130 if (bChanged && !infoPtr->bIsDrawing)
3132 /* this little optimization eliminates some nasty flicker */
3133 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3134 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3135 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3136 else
3137 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3139 /* restore text */
3140 if (pszText)
3142 textfreeT(lpLVItem->pszText, isW);
3143 lpLVItem->pszText = pszText;
3146 return bResult;
3149 /***
3150 * DESCRIPTION:
3151 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3153 * PARAMETER(S):
3154 * [I] infoPtr : valid pointer to the listview structure
3156 * RETURN:
3157 * item index
3159 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3161 LONG lStyle = infoPtr->dwStyle;
3162 UINT uView = lStyle & LVS_TYPEMASK;
3163 INT nItem = 0;
3164 SCROLLINFO scrollInfo;
3166 scrollInfo.cbSize = sizeof(SCROLLINFO);
3167 scrollInfo.fMask = SIF_POS;
3169 if (uView == LVS_LIST)
3171 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3172 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3174 else if (uView == LVS_REPORT)
3176 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3177 nItem = scrollInfo.nPos;
3179 else
3181 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3182 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3185 TRACE("nItem=%d\n", nItem);
3187 return nItem;
3191 /***
3192 * DESCRIPTION:
3193 * Erases the background of the given rectangle
3195 * PARAMETER(S):
3196 * [I] infoPtr : valid pointer to the listview structure
3197 * [I] hdc : device context handle
3198 * [I] lprcBox : clipping rectangle
3200 * RETURN:
3201 * Success: TRUE
3202 * Failure: FALSE
3204 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
3206 if (!infoPtr->hBkBrush) return FALSE;
3208 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3210 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3213 /***
3214 * DESCRIPTION:
3215 * Draws an item.
3217 * PARAMETER(S):
3218 * [I] infoPtr : valid pointer to the listview structure
3219 * [I] hdc : device context handle
3220 * [I] nItem : item index
3221 * [I] nSubItem : subitem index
3222 * [I] pos : item position in client coordinates
3223 * [I] cdmode : custom draw mode
3225 * RETURN:
3226 * Success: TRUE
3227 * Failure: FALSE
3229 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3231 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3232 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3233 DWORD cditemmode = CDRF_DODEFAULT;
3234 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3235 NMLVCUSTOMDRAW nmlvcd;
3236 HIMAGELIST himl;
3237 LVITEMW lvItem;
3239 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3241 /* get information needed for drawing the item */
3242 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3243 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3244 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3245 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3246 lvItem.iItem = nItem;
3247 lvItem.iSubItem = nSubItem;
3248 lvItem.cchTextMax = DISP_TEXT_SIZE;
3249 lvItem.pszText = szDispText;
3250 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3251 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3253 /* now check if we need to update the focus rectangle */
3254 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3256 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3257 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel)) return FALSE;
3258 OffsetRect(&rcBox, pos.x, pos.y);
3259 OffsetRect(&rcState, pos.x, pos.y);
3260 OffsetRect(&rcIcon, pos.x, pos.y);
3261 OffsetRect(&rcLabel, pos.x, pos.y);
3262 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3263 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3265 /* fill in the custom draw structure */
3266 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox);
3267 nmlvcd.nmcd.dwItemSpec = lvItem.iItem;
3268 nmlvcd.iSubItem = lvItem.iSubItem;
3269 if (lvItem.state & LVIS_SELECTED) nmlvcd.nmcd.uItemState |= CDIS_SELECTED;
3270 if (lvItem.state & LVIS_FOCUSED) nmlvcd.nmcd.uItemState |= CDIS_FOCUS;
3271 if (lvItem.iItem == infoPtr->nHotItem) nmlvcd.nmcd.uItemState |= CDIS_HOT;
3272 nmlvcd.nmcd.lItemlParam = lvItem.lParam;
3273 if ((lvItem.state & LVIS_SELECTED) &&
3274 (lvItem.iSubItem == 0 ||
3275 (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))))
3277 if (infoPtr->bFocus)
3279 nmlvcd.clrTextBk = comctl32_color.clrHighlight;
3280 nmlvcd.clrText = comctl32_color.clrHighlightText;
3282 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
3284 nmlvcd.clrTextBk = comctl32_color.clr3dFace;
3285 nmlvcd.clrText = comctl32_color.clrBtnText;
3289 if (cdmode & CDRF_NOTIFYITEMDRAW)
3290 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3291 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3293 /* state icons */
3294 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3296 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3297 if (uStateImage)
3298 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3301 /* small icons */
3302 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3303 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3304 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3305 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3307 /* Don't bother painting item being edited */
3308 if (infoPtr->hwndEdit && lprcFocus && nSubItem == 0) goto postpaint;
3310 /* Set the text attributes */
3311 SetBkMode(hdc, nmlvcd.clrTextBk == CLR_NONE ? TRANSPARENT : OPAQUE);
3312 if (nmlvcd.clrTextBk != CLR_NONE)
3313 SetBkColor(hdc, nmlvcd.clrTextBk == CLR_DEFAULT ? infoPtr->clrTextBkDefault : nmlvcd.clrTextBk);
3314 SetTextColor(hdc, nmlvcd.clrText);
3316 /* draw the selection background, if we're drawing the main item */
3317 if (nSubItem == 0)
3319 rcSelect = rcLabel;
3320 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3321 rcSelect.right = rcBox.right;
3323 if (lvItem.state & LVIS_SELECTED)
3324 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3325 if(lprcFocus) *lprcFocus = rcSelect;
3328 /* figure out the text drawing flags */
3329 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3330 if (uView == LVS_ICON)
3331 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3332 else
3334 INT align = DT_LEFT;
3336 if (nSubItem)
3338 LVCOLUMNW lvColumn;
3339 lvColumn.mask = LVCF_FMT;
3340 LISTVIEW_GetColumnT(infoPtr, nSubItem, &lvColumn, TRUE);
3341 TRACE("lvColumn=%s\n", debuglvcolumn_t(&lvColumn, TRUE));
3342 if (lvColumn.fmt & LVCFMT_RIGHT) align = DT_RIGHT;
3343 else if (lvColumn.fmt & LVCFMT_CENTER) align = DT_CENTER;
3345 uFormat |= align;
3347 if (!(uFormat & (DT_RIGHT | DT_CENTER))) rcLabel.left += 2;
3348 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3350 postpaint:
3351 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3352 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3353 return TRUE;
3356 /***
3357 * DESCRIPTION:
3358 * Draws listview items when in owner draw mode.
3360 * PARAMETER(S):
3361 * [I] infoPtr : valid pointer to the listview structure
3362 * [I] hdc : device context handle
3364 * RETURN:
3365 * None
3367 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
3369 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3370 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3371 POINT Origin, Position;
3372 DRAWITEMSTRUCT dis;
3373 LVITEMW item;
3374 ITERATOR i;
3376 TRACE("()\n");
3378 ZeroMemory(&dis, sizeof(dis));
3380 /* Get scroll info once before loop */
3381 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3383 /* figure out what we need to draw */
3384 iterator_visibleitems(&i, infoPtr, hdc);
3386 /* send cache hint notification */
3387 if (infoPtr->dwStyle & LVS_OWNERDATA)
3389 RANGE range = iterator_range(&i);
3390 NMLVCACHEHINT nmlv;
3392 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3393 nmlv.iFrom = range.lower;
3394 nmlv.iTo = range.upper - 1;
3395 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3398 /* iterate through the invalidated rows */
3399 while(iterator_prev(&i))
3401 item.iItem = i.nItem;
3402 item.iSubItem = 0;
3403 item.mask = LVIF_PARAM | LVIF_STATE;
3404 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3405 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3407 dis.CtlType = ODT_LISTVIEW;
3408 dis.CtlID = uID;
3409 dis.itemID = item.iItem;
3410 dis.itemAction = ODA_DRAWENTIRE;
3411 dis.itemState = 0;
3412 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3413 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3414 dis.hwndItem = infoPtr->hwndSelf;
3415 dis.hDC = hdc;
3416 if (!LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position)) continue;
3417 dis.rcItem.left = Position.x + Origin.x;
3418 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3419 dis.rcItem.top = Position.y + Origin.y;
3420 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3421 dis.itemData = item.lParam;
3423 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3424 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3426 iterator_destroy(&i);
3429 /***
3430 * DESCRIPTION:
3431 * Draws listview items when in report display mode.
3433 * PARAMETER(S):
3434 * [I] infoPtr : valid pointer to the listview structure
3435 * [I] hdc : device context handle
3436 * [I] cdmode : custom draw mode
3438 * RETURN:
3439 * None
3441 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3443 INT rgntype, nFirstCol, nLastCol, nCol;
3444 RECT rcClip, rcItem;
3445 POINT Origin, Position;
3446 ITERATOR i;
3448 TRACE("()\n");
3450 /* figure out what to draw */
3451 rgntype = GetClipBox(hdc, &rcClip);
3452 if (rgntype == NULLREGION) return;
3454 /* Get scroll info once before loop */
3455 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3457 /* narrow down the columns we need to paint */
3458 for(nFirstCol = 0; nFirstCol < infoPtr->hdpaColumns->nItemCount; nFirstCol++)
3459 if (LISTVIEW_GetHeaderRect(infoPtr, nFirstCol, &rcItem) &&
3460 rcItem.right + Origin.x >= rcClip.left) break;
3461 for(nLastCol = infoPtr->hdpaColumns->nItemCount - 1; nLastCol >= 0; nLastCol--)
3462 if (LISTVIEW_GetHeaderRect(infoPtr, nLastCol, &rcItem) &&
3463 rcItem.left + Origin.x < rcClip.right) break;
3465 /* figure out what we need to draw */
3466 iterator_visibleitems(&i, infoPtr, hdc);
3468 /* a last few bits before we start drawing */
3469 TRACE("Colums=(%di - %d)\n", nFirstCol, nLastCol);
3471 /* iterate through the invalidated rows */
3472 while(iterator_prev(&i))
3474 /* iterate through the invalidated columns */
3475 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
3477 if (!LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position)) continue;
3478 Position.x += Origin.x;
3479 Position.y += Origin.y;
3481 if (rgntype == COMPLEXREGION && LISTVIEW_GetHeaderRect(infoPtr, nCol, &rcItem))
3483 rcItem.top = 0;
3484 rcItem.bottom = infoPtr->nItemHeight;
3485 OffsetRect(&rcItem, Position.x, Position.y);
3486 if (!RectVisible(hdc, &rcItem)) continue;
3489 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, nCol, Position, cdmode);
3492 iterator_destroy(&i);
3495 /***
3496 * DESCRIPTION:
3497 * Draws listview items when in list display mode.
3499 * PARAMETER(S):
3500 * [I] infoPtr : valid pointer to the listview structure
3501 * [I] hdc : device context handle
3502 * [I] cdmode : custom draw mode
3504 * RETURN:
3505 * None
3507 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3509 POINT Origin, Position;
3510 ITERATOR i;
3512 /* Get scroll info once before loop */
3513 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3515 /* figure out what we need to draw */
3516 iterator_visibleitems(&i, infoPtr, hdc);
3518 while(iterator_prev(&i))
3520 if (!LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position)) continue;
3521 Position.x += Origin.x;
3522 Position.y += Origin.y;
3524 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, 0, Position, cdmode);
3526 iterator_destroy(&i);
3530 /***
3531 * DESCRIPTION:
3532 * Draws listview items.
3534 * PARAMETER(S):
3535 * [I] infoPtr : valid pointer to the listview structure
3536 * [I] HDC : device context handle
3538 * RETURN:
3539 * NoneX
3541 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3543 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3544 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3545 NMLVCUSTOMDRAW nmlvcd;
3546 HFONT hOldFont;
3547 DWORD cdmode;
3548 INT oldBkMode;
3549 RECT rcClient;
3551 LISTVIEW_DUMP(infoPtr);
3553 infoPtr->bIsDrawing = TRUE;
3555 /* save dc values we're gonna trash while drawing */
3556 hOldFont = SelectObject(hdc, infoPtr->hFont);
3557 oldBkMode = GetBkMode(hdc);
3558 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3559 oldTextColor = GetTextColor(hdc);
3561 oldClrTextBk = infoPtr->clrTextBk;
3562 oldClrText = infoPtr->clrText;
3564 GetClientRect(infoPtr->hwndSelf, &rcClient);
3565 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient);
3566 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3567 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3569 /* Use these colors to draw the items */
3570 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3571 infoPtr->clrText = nmlvcd.clrText;
3573 /* nothing to draw */
3574 if(infoPtr->nItemCount == 0) goto enddraw;
3576 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3577 LISTVIEW_RefreshOwnerDraw(infoPtr, hdc);
3578 else
3580 if (uView == LVS_REPORT)
3581 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3582 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3583 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3585 /* if we have a focus rect, draw it */
3586 if (infoPtr->bFocus)
3587 DrawFocusRect(hdc, &infoPtr->rcFocus);
3590 enddraw:
3591 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3592 notify_customdraw(infoPtr, CDDS_POSTPAINT, &nmlvcd);
3594 infoPtr->clrTextBk = oldClrTextBk;
3595 infoPtr->clrText = oldClrText;
3597 SelectObject(hdc, hOldFont);
3598 SetBkMode(hdc, oldBkMode);
3599 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3600 SetTextColor(hdc, oldTextColor);
3601 infoPtr->bIsDrawing = FALSE;
3605 /***
3606 * DESCRIPTION:
3607 * Calculates the approximate width and height of a given number of items.
3609 * PARAMETER(S):
3610 * [I] infoPtr : valid pointer to the listview structure
3611 * [I] INT : number of items
3612 * [I] INT : width
3613 * [I] INT : height
3615 * RETURN:
3616 * Returns a DWORD. The width in the low word and the height in high word.
3618 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3619 WORD wWidth, WORD wHeight)
3621 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3622 INT nItemCountPerColumn = 1;
3623 INT nColumnCount = 0;
3624 DWORD dwViewRect = 0;
3626 if (nItemCount == -1)
3627 nItemCount = infoPtr->nItemCount;
3629 if (uView == LVS_LIST)
3631 if (wHeight == 0xFFFF)
3633 /* use current height */
3634 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3637 if (wHeight < infoPtr->nItemHeight)
3638 wHeight = infoPtr->nItemHeight;
3640 if (nItemCount > 0)
3642 if (infoPtr->nItemHeight > 0)
3644 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3645 if (nItemCountPerColumn == 0)
3646 nItemCountPerColumn = 1;
3648 if (nItemCount % nItemCountPerColumn != 0)
3649 nColumnCount = nItemCount / nItemCountPerColumn;
3650 else
3651 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3655 /* Microsoft padding magic */
3656 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3657 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3659 dwViewRect = MAKELONG(wWidth, wHeight);
3661 else if (uView == LVS_REPORT)
3662 FIXME("uView == LVS_REPORT: not implemented\n");
3663 else if (uView == LVS_SMALLICON)
3664 FIXME("uView == LVS_SMALLICON: not implemented\n");
3665 else if (uView == LVS_ICON)
3666 FIXME("uView == LVS_ICON: not implemented\n");
3668 return dwViewRect;
3671 /***
3672 * DESCRIPTION:
3673 * Arranges listview items in icon display mode.
3675 * PARAMETER(S):
3676 * [I] infoPtr : valid pointer to the listview structure
3677 * [I] INT : alignment code
3679 * RETURN:
3680 * SUCCESS : TRUE
3681 * FAILURE : FALSE
3683 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3685 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3686 BOOL bResult = FALSE;
3688 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3690 switch (nAlignCode)
3692 case LVA_ALIGNLEFT:
3693 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3694 break;
3695 case LVA_ALIGNTOP:
3696 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3697 break;
3698 case LVA_DEFAULT:
3699 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3700 break;
3701 case LVA_SNAPTOGRID:
3702 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3703 break;
3707 return bResult;
3710 /* << LISTVIEW_CreateDragImage >> */
3713 /***
3714 * DESCRIPTION:
3715 * Removes all listview items and subitems.
3717 * PARAMETER(S):
3718 * [I] infoPtr : valid pointer to the listview structure
3720 * RETURN:
3721 * SUCCESS : TRUE
3722 * FAILURE : FALSE
3724 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3726 LONG lStyle = infoPtr->dwStyle;
3727 UINT uView = lStyle & LVS_TYPEMASK;
3728 LISTVIEW_ITEM *lpItem;
3729 LISTVIEW_SUBITEM *lpSubItem;
3730 NMLISTVIEW nmlv;
3731 BOOL bSuppress;
3732 BOOL bResult = FALSE;
3733 HDPA hdpaSubItems;
3735 TRACE("()\n");
3737 LISTVIEW_DeselectAll(infoPtr);
3738 infoPtr->nSelectionMark=-1;
3739 infoPtr->nFocusedItem=-1;
3740 SetRectEmpty(&infoPtr->rcFocus);
3741 /* But we are supposed to leave nHotItem as is! */
3743 if (lStyle & LVS_OWNERDATA)
3745 infoPtr->nItemCount = 0;
3746 LISTVIEW_InvalidateList(infoPtr);
3747 return TRUE;
3750 if (infoPtr->nItemCount > 0)
3752 INT i, j;
3754 /* send LVN_DELETEALLITEMS notification */
3755 /* verify if subsequent LVN_DELETEITEM notifications should be
3756 suppressed */
3757 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3758 nmlv.iItem = -1;
3759 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3761 for (i = 0; i < infoPtr->nItemCount; i++)
3763 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3764 if (hdpaSubItems != NULL)
3766 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3768 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3769 if (lpSubItem != NULL)
3771 /* free subitem string */
3772 if (is_textW(lpSubItem->hdr.pszText))
3773 COMCTL32_Free(lpSubItem->hdr.pszText);
3775 /* free subitem */
3776 COMCTL32_Free(lpSubItem);
3780 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3781 if (lpItem != NULL)
3783 if (!bSuppress)
3785 /* send LVN_DELETEITEM notification */
3786 nmlv.iItem = i;
3787 nmlv.lParam = lpItem->lParam;
3788 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3791 /* free item string */
3792 if (is_textW(lpItem->hdr.pszText))
3793 COMCTL32_Free(lpItem->hdr.pszText);
3795 /* free item */
3796 COMCTL32_Free(lpItem);
3799 DPA_Destroy(hdpaSubItems);
3803 /* reinitialize listview memory */
3804 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3805 infoPtr->nItemCount = 0;
3806 DPA_DeleteAllPtrs(infoPtr->hdpaPosX);
3807 DPA_DeleteAllPtrs(infoPtr->hdpaPosY);
3809 /* align items (set position of each item) */
3810 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3812 if (lStyle & LVS_ALIGNLEFT)
3814 LISTVIEW_AlignLeft(infoPtr);
3816 else
3818 LISTVIEW_AlignTop(infoPtr);
3822 LISTVIEW_UpdateScroll(infoPtr);
3824 LISTVIEW_InvalidateList(infoPtr);
3827 return bResult;
3830 /***
3831 * DESCRIPTION:
3832 * Scrolls, and updates the columns, when a column is changing width.
3834 * PARAMETER(S):
3835 * [I] infoPtr : valid pointer to the listview structure
3836 * [I] nColumn : column to scroll
3837 * [I] dx : amount of scroll, in pixels
3839 * RETURN:
3840 * SUCCESS : TRUE
3841 * FAILURE : FALSE
3843 static BOOL LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
3845 COLUMN_INFO *lpColumnInfo;
3846 RECT rcOld, rcCol;
3847 INT nCol;
3849 if ((lpColumnInfo = DPA_GetPtr(infoPtr->hdpaColumns, nColumn)))
3850 rcCol = lpColumnInfo->rcHeader;
3852 /* ajust the other columns */
3853 for (nCol = nColumn; (lpColumnInfo = DPA_GetPtr(infoPtr->hdpaColumns, nCol)); nCol++)
3855 lpColumnInfo->rcHeader.left += dx;
3856 lpColumnInfo->rcHeader.right += dx;
3859 /* do not update screen if not in report mode */
3860 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return TRUE;
3862 /* if we have a focus, must first erase the focus rect */
3863 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
3865 /* Need to reset the item width when inserting a new column */
3866 infoPtr->nItemWidth += dx;
3868 LISTVIEW_UpdateScroll(infoPtr);
3870 /* scroll to cover the deleted column, and invalidate for redraw */
3871 rcOld = infoPtr->rcList;
3872 rcOld.left = rcCol.left;
3873 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3875 /* we can restore focus now */
3876 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
3878 return TRUE;
3881 /***
3882 * DESCRIPTION:
3883 * Removes a column from the listview control.
3885 * PARAMETER(S):
3886 * [I] infoPtr : valid pointer to the listview structure
3887 * [I] INT : column index
3889 * RETURN:
3890 * SUCCESS : TRUE
3891 * FAILURE : FALSE
3893 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3895 RECT rcCol;
3897 TRACE("nColumn=%d\n", nColumn);
3899 if (nColumn <= 0) return FALSE;
3901 if (!LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol))
3902 return FALSE;
3904 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3905 return FALSE;
3907 COMCTL32_Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
3908 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
3910 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3912 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3913 HDPA hdpaSubItems;
3914 INT nItem, nSubItem, i;
3916 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3918 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3919 if (!hdpaSubItems) continue;
3920 nSubItem = 0;
3921 lpDelItem = 0;
3922 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3924 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3925 if (!lpSubItem) break;
3926 if (lpSubItem->iSubItem == nColumn)
3928 nSubItem = i;
3929 lpDelItem = lpSubItem;
3931 else if (lpSubItem->iSubItem > nColumn)
3933 lpSubItem->iSubItem--;
3937 /* if we found our subitem, zapp it */
3938 if (nSubItem > 0)
3940 /* free string */
3941 if (is_textW(lpDelItem->hdr.pszText))
3942 COMCTL32_Free(lpDelItem->hdr.pszText);
3944 /* free item */
3945 COMCTL32_Free(lpDelItem);
3947 /* free dpa memory */
3948 DPA_DeletePtr(hdpaSubItems, nSubItem);
3953 /* update the other column info */
3954 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
3956 return TRUE;
3959 /***
3960 * DESCRIPTION:
3961 * Removes an item from the listview control.
3963 * PARAMETER(S):
3964 * [I] infoPtr : valid pointer to the listview structure
3965 * [I] INT : item index
3967 * RETURN:
3968 * SUCCESS : TRUE
3969 * FAILURE : FALSE
3971 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
3973 LONG lStyle = infoPtr->dwStyle;
3974 UINT uView = lStyle & LVS_TYPEMASK;
3975 NMLISTVIEW nmlv;
3976 BOOL bResult = FALSE;
3977 HDPA hdpaSubItems;
3978 LISTVIEW_ITEM *lpItem;
3979 LISTVIEW_SUBITEM *lpSubItem;
3980 LVITEMW item;
3981 INT i;
3983 TRACE("(nItem=%d)\n", nItem);
3986 /* First, send LVN_DELETEITEM notification. */
3987 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
3988 nmlv.iItem = nItem;
3989 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3991 if (nItem == infoPtr->nFocusedItem)
3993 infoPtr->nFocusedItem = -1;
3994 SetRectEmpty(&infoPtr->rcFocus);
3997 /* remove it from the selection range */
3998 item.state = LVIS_SELECTED;
3999 item.stateMask = LVIS_SELECTED;
4000 LISTVIEW_SetItemState(infoPtr,nItem,&item);
4002 if (lStyle & LVS_OWNERDATA)
4004 infoPtr->nItemCount--;
4005 LISTVIEW_InvalidateList(infoPtr); /*FIXME: optimize */
4006 return TRUE;
4009 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
4011 /* initialize memory */
4012 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4014 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4015 if (hdpaSubItems != NULL)
4017 infoPtr->nItemCount--;
4018 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4020 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4021 if (lpSubItem != NULL)
4023 /* free item string */
4024 if (is_textW(lpSubItem->hdr.pszText))
4025 COMCTL32_Free(lpSubItem->hdr.pszText);
4027 /* free item */
4028 COMCTL32_Free(lpSubItem);
4032 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4033 if (lpItem != NULL)
4035 /* free item string */
4036 if (is_textW(lpItem->hdr.pszText))
4037 COMCTL32_Free(lpItem->hdr.pszText);
4039 /* free item */
4040 COMCTL32_Free(lpItem);
4043 bResult = DPA_Destroy(hdpaSubItems);
4044 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4045 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4048 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4050 /* align items (set position of each item) */
4051 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4053 if (lStyle & LVS_ALIGNLEFT)
4054 LISTVIEW_AlignLeft(infoPtr);
4055 else
4056 LISTVIEW_AlignTop(infoPtr);
4059 LISTVIEW_UpdateScroll(infoPtr);
4061 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
4064 return bResult;
4068 /***
4069 * DESCRIPTION:
4070 * Callback implementation for editlabel control
4072 * PARAMETER(S):
4073 * [I] infoPtr : valid pointer to the listview structure
4074 * [I] pszText : modified text
4075 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4077 * RETURN:
4078 * SUCCESS : TRUE
4079 * FAILURE : FALSE
4081 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4083 NMLVDISPINFOW dispInfo;
4085 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4087 ZeroMemory(&dispInfo, sizeof(dispInfo));
4088 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4089 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4090 dispInfo.item.iSubItem = 0;
4091 dispInfo.item.stateMask = ~0;
4092 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4093 /* add the text from the edit in */
4094 dispInfo.item.mask |= LVIF_TEXT;
4095 dispInfo.item.pszText = pszText;
4096 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4098 /* Do we need to update the Item Text */
4099 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4100 if (!pszText) return TRUE;
4102 ZeroMemory(&dispInfo, sizeof(dispInfo));
4103 dispInfo.item.mask = LVIF_TEXT;
4104 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4105 dispInfo.item.iSubItem = 0;
4106 dispInfo.item.pszText = pszText;
4107 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4108 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4111 /***
4112 * DESCRIPTION:
4113 * Begin in place editing of specified list view item
4115 * PARAMETER(S):
4116 * [I] infoPtr : valid pointer to the listview structure
4117 * [I] INT : item index
4118 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4120 * RETURN:
4121 * SUCCESS : TRUE
4122 * FAILURE : FALSE
4124 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4126 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4127 NMLVDISPINFOW dispInfo;
4128 RECT rect;
4130 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4132 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4133 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4135 infoPtr->nEditLabelItem = nItem;
4137 /* Is the EditBox still there, if so remove it */
4138 if(infoPtr->hwndEdit != 0)
4140 SetFocus(infoPtr->hwndSelf);
4141 infoPtr->hwndEdit = 0;
4144 LISTVIEW_SetSelection(infoPtr, nItem);
4145 LISTVIEW_SetItemFocus(infoPtr, nItem);
4147 rect.left = LVIR_LABEL;
4148 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4150 ZeroMemory(&dispInfo, sizeof(dispInfo));
4151 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4152 dispInfo.item.iItem = nItem;
4153 dispInfo.item.iSubItem = 0;
4154 dispInfo.item.stateMask = ~0;
4155 dispInfo.item.pszText = szDispText;
4156 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4157 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4159 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4160 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4161 if (!infoPtr->hwndEdit) return 0;
4163 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4165 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4166 infoPtr->hwndEdit = 0;
4167 return 0;
4170 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4171 SetFocus(infoPtr->hwndEdit);
4172 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4173 return infoPtr->hwndEdit;
4177 /***
4178 * DESCRIPTION:
4179 * Ensures the specified item is visible, scrolling into view if necessary.
4181 * PARAMETER(S):
4182 * [I] infoPtr : valid pointer to the listview structure
4183 * [I] nItem : item index
4184 * [I] bPartial : partially or entirely visible
4186 * RETURN:
4187 * SUCCESS : TRUE
4188 * FAILURE : FALSE
4190 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4192 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4193 INT nScrollPosHeight = 0;
4194 INT nScrollPosWidth = 0;
4195 INT nHorzAdjust = 0;
4196 INT nVertAdjust = 0;
4197 INT nHorzDiff = 0;
4198 INT nVertDiff = 0;
4199 RECT rcItem, rcTemp;
4201 rcItem.left = LVIR_BOUNDS;
4202 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4204 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4206 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4208 /* scroll left/right, but in LVS_REPORT mode */
4209 if (uView == LVS_LIST)
4210 nScrollPosWidth = infoPtr->nItemWidth;
4211 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4212 nScrollPosWidth = 1;
4214 if (rcItem.left < infoPtr->rcList.left)
4216 nHorzAdjust = -1;
4217 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4219 else
4221 nHorzAdjust = 1;
4222 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4226 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4228 /* scroll up/down, but not in LVS_LIST mode */
4229 if (uView == LVS_REPORT)
4230 nScrollPosHeight = infoPtr->nItemHeight;
4231 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4232 nScrollPosHeight = 1;
4234 if (rcItem.top < infoPtr->rcList.top)
4236 nVertAdjust = -1;
4237 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4239 else
4241 nVertAdjust = 1;
4242 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4246 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4248 if (nScrollPosWidth)
4250 INT diff = nHorzDiff / nScrollPosWidth;
4251 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4252 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4255 if (nScrollPosHeight)
4257 INT diff = nVertDiff / nScrollPosHeight;
4258 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4259 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4262 return TRUE;
4265 /***
4266 * DESCRIPTION:
4267 * Searches for an item with specific characteristics.
4269 * PARAMETER(S):
4270 * [I] hwnd : window handle
4271 * [I] nStart : base item index
4272 * [I] lpFindInfo : item information to look for
4274 * RETURN:
4275 * SUCCESS : index of item
4276 * FAILURE : -1
4278 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4279 LPLVFINDINFOW lpFindInfo)
4281 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4282 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4283 BOOL bWrap = FALSE, bNearest = FALSE;
4284 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4285 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4286 POINT Position, Destination;
4287 LVITEMW lvItem;
4289 if (!lpFindInfo || nItem < 0) return -1;
4291 lvItem.mask = 0;
4292 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4294 lvItem.mask |= LVIF_TEXT;
4295 lvItem.pszText = szDispText;
4296 lvItem.cchTextMax = DISP_TEXT_SIZE;
4299 if (lpFindInfo->flags & LVFI_WRAP)
4300 bWrap = TRUE;
4302 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4303 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4305 POINT Origin;
4307 FIXME("LVFI_NEARESTXY is slow.\n");
4308 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
4309 Destination.x = lpFindInfo->pt.x - Origin.x;
4310 Destination.y = lpFindInfo->pt.y - Origin.y;
4311 switch(lpFindInfo->vkDirection)
4313 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4314 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4315 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4316 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4317 case VK_HOME: Destination.x = Destination.y = 0; break;
4318 case VK_END: Destination.x = infoPtr->rcView.right; Destination.y = infoPtr->rcView.bottom; break;
4319 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4320 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4321 default: FIXME("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4323 bNearest = TRUE;
4326 /* if LVFI_PARAM is specified, all other flags are ignored */
4327 if (lpFindInfo->flags & LVFI_PARAM)
4329 lvItem.mask |= LVIF_PARAM;
4330 bNearest = FALSE;
4331 lvItem.mask &= ~LVIF_TEXT;
4334 again:
4335 for (; nItem < nLast; nItem++)
4337 lvItem.iItem = nItem;
4338 lvItem.iSubItem = 0;
4339 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4341 if (lvItem.mask & LVIF_PARAM && lpFindInfo->lParam == lvItem.lParam)
4342 return nItem;
4344 if (lvItem.mask & LVIF_TEXT)
4346 if (lpFindInfo->flags & LVFI_PARTIAL)
4348 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4350 else
4352 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4356 if (!bNearest) return nItem;
4358 /* This is very inefficient. To do a good job here,
4359 * we need a sorted array of (x,y) item positions */
4360 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) continue;
4362 /* compute the distance^2 to the destination */
4363 xdist = Destination.x - Position.x;
4364 ydist = Destination.y - Position.y;
4365 dist = xdist * xdist + ydist * ydist;
4367 /* remember the distance, and item if it's closer */
4368 if (dist < mindist)
4370 mindist = dist;
4371 nNearestItem = nItem;
4375 if (bWrap)
4377 nItem = 0;
4378 nLast = min(nStart + 1, infoPtr->nItemCount);
4379 bWrap = FALSE;
4380 goto again;
4383 return nNearestItem;
4386 /***
4387 * DESCRIPTION:
4388 * Searches for an item with specific characteristics.
4390 * PARAMETER(S):
4391 * [I] hwnd : window handle
4392 * [I] nStart : base item index
4393 * [I] lpFindInfo : item information to look for
4395 * RETURN:
4396 * SUCCESS : index of item
4397 * FAILURE : -1
4399 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4400 LPLVFINDINFOA lpFindInfo)
4402 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4403 LVFINDINFOW fiw;
4404 LRESULT res;
4406 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4407 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4408 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4409 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4410 return res;
4413 /***
4414 * DESCRIPTION:
4415 * Retrieves the background image of the listview control.
4417 * PARAMETER(S):
4418 * [I] infoPtr : valid pointer to the listview structure
4419 * [O] LPLVMKBIMAGE : background image attributes
4421 * RETURN:
4422 * SUCCESS : TRUE
4423 * FAILURE : FALSE
4425 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4426 /* { */
4427 /* FIXME (listview, "empty stub!\n"); */
4428 /* return FALSE; */
4429 /* } */
4431 /***
4432 * DESCRIPTION:
4433 * Retrieves column attributes.
4435 * PARAMETER(S):
4436 * [I] infoPtr : valid pointer to the listview structure
4437 * [I] INT : column index
4438 * [IO] LPLVCOLUMNW : column information
4439 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4440 * otherwise it is in fact a LPLVCOLUMNA
4442 * RETURN:
4443 * SUCCESS : TRUE
4444 * FAILURE : FALSE
4446 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4448 HDITEMW hdi;
4449 BOOL bResult = FALSE;
4451 if (lpColumn != NULL)
4454 /* initialize memory */
4455 ZeroMemory(&hdi, sizeof(hdi));
4457 if (lpColumn->mask & LVCF_FMT)
4458 hdi.mask |= HDI_FORMAT;
4460 if (lpColumn->mask & LVCF_WIDTH)
4461 hdi.mask |= HDI_WIDTH;
4463 if (lpColumn->mask & LVCF_TEXT)
4465 hdi.mask |= HDI_TEXT;
4466 hdi.cchTextMax = lpColumn->cchTextMax;
4467 hdi.pszText = lpColumn->pszText;
4470 if (lpColumn->mask & LVCF_IMAGE)
4471 hdi.mask |= HDI_IMAGE;
4473 if (lpColumn->mask & LVCF_ORDER)
4474 hdi.mask |= HDI_ORDER;
4476 if (isW)
4477 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4478 else
4479 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4481 if (bResult)
4483 if (lpColumn->mask & LVCF_FMT)
4485 lpColumn->fmt = 0;
4487 if (hdi.fmt & HDF_LEFT)
4488 lpColumn->fmt |= LVCFMT_LEFT;
4489 else if (hdi.fmt & HDF_RIGHT)
4490 lpColumn->fmt |= LVCFMT_RIGHT;
4491 else if (hdi.fmt & HDF_CENTER)
4492 lpColumn->fmt |= LVCFMT_CENTER;
4494 if (hdi.fmt & HDF_IMAGE)
4495 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4497 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4498 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4501 if (lpColumn->mask & LVCF_WIDTH)
4502 lpColumn->cx = hdi.cxy;
4504 if (lpColumn->mask & LVCF_IMAGE)
4505 lpColumn->iImage = hdi.iImage;
4507 if (lpColumn->mask & LVCF_ORDER)
4508 lpColumn->iOrder = hdi.iOrder;
4510 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4511 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4516 return bResult;
4520 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4522 INT i;
4524 if (!lpiArray)
4525 return FALSE;
4527 /* FIXME: little hack */
4528 for (i = 0; i < iCount; i++)
4529 lpiArray[i] = i;
4531 return TRUE;
4534 /***
4535 * DESCRIPTION:
4536 * Retrieves the column width.
4538 * PARAMETER(S):
4539 * [I] infoPtr : valid pointer to the listview structure
4540 * [I] int : column index
4542 * RETURN:
4543 * SUCCESS : column width
4544 * FAILURE : zero
4546 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4548 INT nColumnWidth = 0;
4549 RECT rcHeader;
4551 TRACE("nColumn=%d\n", nColumn);
4553 /* we have a 'column' in LIST and REPORT mode only */
4554 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4556 case LVS_LIST:
4557 nColumnWidth = infoPtr->nItemWidth;
4558 break;
4559 case LVS_REPORT:
4560 if (LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader))
4561 nColumnWidth = rcHeader.right - rcHeader.left;
4562 break;
4565 TRACE("nColumnWidth=%d\n", nColumnWidth);
4566 return nColumnWidth;
4569 /***
4570 * DESCRIPTION:
4571 * In list or report display mode, retrieves the number of items that can fit
4572 * vertically in the visible area. In icon or small icon display mode,
4573 * retrieves the total number of visible items.
4575 * PARAMETER(S):
4576 * [I] infoPtr : valid pointer to the listview structure
4578 * RETURN:
4579 * Number of fully visible items.
4581 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4583 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4584 INT nItemCount = 0;
4586 if (uView == LVS_LIST)
4588 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4590 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4591 LISTVIEW_GetCountPerColumn(infoPtr);
4594 else if (uView == LVS_REPORT)
4596 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4598 else
4600 nItemCount = infoPtr->nItemCount;
4603 return nItemCount;
4607 /***
4608 * DESCRIPTION:
4609 * Retrieves an image list handle.
4611 * PARAMETER(S):
4612 * [I] infoPtr : valid pointer to the listview structure
4613 * [I] nImageList : image list identifier
4615 * RETURN:
4616 * SUCCESS : image list handle
4617 * FAILURE : NULL
4619 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4621 HIMAGELIST himl = NULL;
4623 switch (nImageList)
4625 case LVSIL_NORMAL:
4626 himl = infoPtr->himlNormal;
4627 break;
4628 case LVSIL_SMALL:
4629 himl = infoPtr->himlSmall;
4630 break;
4631 case LVSIL_STATE:
4632 himl = infoPtr->himlState;
4633 break;
4636 return (LRESULT)himl;
4639 /* LISTVIEW_GetISearchString */
4641 /***
4642 * DESCRIPTION:
4643 * Retrieves item attributes.
4645 * PARAMETER(S):
4646 * [I] hwnd : window handle
4647 * [IO] lpLVItem : item info
4648 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4649 * if FALSE, the lpLVItem is a LPLVITEMA.
4651 * NOTE:
4652 * This is the internal 'GetItem' interface -- it tries to
4653 * be smart, and avoids text copies, if possible, by modifing
4654 * lpLVItem->pszText to point to the text string. Please note
4655 * that this is not always possible (e.g. OWNERDATA), so on
4656 * entry you *must* supply valid values for pszText, and cchTextMax.
4657 * The only difference to the documented interface is that upon
4658 * return, you should use *only* the lpLVItem->pszText, rather than
4659 * the buffer pointer you provided on input. Most code already does
4660 * that, so it's not a problem.
4661 * For the two cases when the text must be copied (that is,
4662 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4664 * RETURN:
4665 * SUCCESS : TRUE
4666 * FAILURE : FALSE
4668 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4670 NMLVDISPINFOW dispInfo;
4671 LISTVIEW_ITEM *lpItem;
4672 ITEMHDR* pItemHdr;
4673 HDPA hdpaSubItems;
4675 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4677 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4678 return FALSE;
4680 if (lpLVItem->mask == 0) return TRUE;
4682 /* a quick optimization if all we're asked is the focus state
4683 * these queries are worth optimising since they are common,
4684 * and can be answered in constant time, without the heavy accesses */
4685 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
4686 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4688 lpLVItem->state = 0;
4689 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4690 lpLVItem->state |= LVIS_FOCUSED;
4691 return TRUE;
4694 ZeroMemory(&dispInfo, sizeof(dispInfo));
4696 /* if the app stores all the data, handle it separately */
4697 if (infoPtr->dwStyle & LVS_OWNERDATA)
4699 dispInfo.item.state = 0;
4701 /* if we need to callback, do it now */
4702 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4704 /* NOTE: copy only fields which we _know_ are initialized, some apps
4705 * depend on the uninitialized fields being 0 */
4706 dispInfo.item.mask = lpLVItem->mask;
4707 dispInfo.item.iItem = lpLVItem->iItem;
4708 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4709 if (lpLVItem->mask & LVIF_TEXT)
4711 dispInfo.item.pszText = lpLVItem->pszText;
4712 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4714 if (lpLVItem->mask & LVIF_STATE)
4715 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4716 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4717 dispInfo.item.stateMask = lpLVItem->stateMask;
4718 *lpLVItem = dispInfo.item;
4719 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4722 /* we store only a little state, so if we're not asked, we're done */
4723 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4725 /* if focus is handled by us, report it */
4726 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4728 lpLVItem->state &= ~LVIS_FOCUSED;
4729 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4730 lpLVItem->state |= LVIS_FOCUSED;
4733 /* and do the same for selection, if we handle it */
4734 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4736 lpLVItem->state &= ~LVIS_SELECTED;
4737 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4738 lpLVItem->state |= LVIS_SELECTED;
4741 return TRUE;
4744 /* find the item and subitem structures before we proceed */
4745 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4746 if (hdpaSubItems == NULL) return FALSE;
4748 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4749 return FALSE;
4751 if (lpLVItem->iSubItem)
4753 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4754 if(!lpSubItem) return FALSE;
4755 pItemHdr = &lpSubItem->hdr;
4757 else
4758 pItemHdr = &lpItem->hdr;
4760 /* Do we need to query the state from the app? */
4761 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4763 dispInfo.item.mask |= LVIF_STATE;
4764 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4767 /* Do we need to enquire about the image? */
4768 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK)
4769 dispInfo.item.mask |= LVIF_IMAGE;
4771 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
4772 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4774 dispInfo.item.mask |= LVIF_TEXT;
4775 dispInfo.item.pszText = lpLVItem->pszText;
4776 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4777 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4778 *dispInfo.item.pszText = '\0';
4781 /* If we don't have all the requested info, query the application */
4782 if (dispInfo.item.mask != 0)
4784 dispInfo.item.iItem = lpLVItem->iItem;
4785 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4786 dispInfo.item.lParam = lpItem->lParam;
4787 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4788 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4791 /* Now, handle the iImage field */
4792 if (dispInfo.item.mask & LVIF_IMAGE)
4794 lpLVItem->iImage = dispInfo.item.iImage;
4795 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
4796 pItemHdr->iImage = dispInfo.item.iImage;
4798 else if (lpLVItem->mask & LVIF_IMAGE)
4799 lpLVItem->iImage = pItemHdr->iImage;
4801 /* The pszText field */
4802 if (dispInfo.item.mask & LVIF_TEXT)
4804 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4805 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4807 lpLVItem->pszText = dispInfo.item.pszText;
4809 else if (lpLVItem->mask & LVIF_TEXT)
4811 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4812 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4815 /* if this is a subitem, we're done */
4816 if (lpLVItem->iSubItem) return TRUE;
4818 /* Next is the lParam field */
4819 if (dispInfo.item.mask & LVIF_PARAM)
4821 lpLVItem->lParam = dispInfo.item.lParam;
4822 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4823 lpItem->lParam = dispInfo.item.lParam;
4825 else if (lpLVItem->mask & LVIF_PARAM)
4826 lpLVItem->lParam = lpItem->lParam;
4828 /* ... the state field (this one is different due to uCallbackmask) */
4829 if (lpLVItem->mask & LVIF_STATE)
4831 lpLVItem->state = lpItem->state;
4832 if (dispInfo.item.mask & LVIF_STATE)
4834 lpLVItem->state &= ~dispInfo.item.stateMask;
4835 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4837 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4839 lpLVItem->state &= ~LVIS_FOCUSED;
4840 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4841 lpLVItem->state |= LVIS_FOCUSED;
4843 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4845 lpLVItem->state &= ~LVIS_SELECTED;
4846 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4847 lpLVItem->state |= LVIS_SELECTED;
4851 /* and last, but not least, the indent field */
4852 if (lpLVItem->mask & LVIF_INDENT)
4853 lpLVItem->iIndent = lpItem->iIndent;
4855 return TRUE;
4858 /***
4859 * DESCRIPTION:
4860 * Retrieves item attributes.
4862 * PARAMETER(S):
4863 * [I] hwnd : window handle
4864 * [IO] lpLVItem : item info
4865 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4866 * if FALSE, the lpLVItem is a LPLVITEMA.
4868 * NOTE:
4869 * This is the external 'GetItem' interface -- it properly copies
4870 * the text in the provided buffer.
4872 * RETURN:
4873 * SUCCESS : TRUE
4874 * FAILURE : FALSE
4876 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4878 LPWSTR pszText;
4879 BOOL bResult;
4881 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4882 return FALSE;
4884 pszText = lpLVItem->pszText;
4885 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4886 if (bResult && lpLVItem->pszText != pszText)
4887 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4888 lpLVItem->pszText = pszText;
4890 return bResult;
4894 /***
4895 * DESCRIPTION:
4896 * Retrieves the position (upper-left) of the listview control item.
4897 * Note that for LVS_ICON style, the upper-left is that of the icon
4898 * and not the bounding box.
4900 * PARAMETER(S):
4901 * [I] infoPtr : valid pointer to the listview structure
4902 * [I] nItem : item index
4903 * [O] lpptPosition : coordinate information
4905 * RETURN:
4906 * SUCCESS : TRUE
4907 * FAILURE : FALSE
4909 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4911 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4912 POINT Origin;
4914 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4916 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4917 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition)) return FALSE;
4918 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
4920 if (uView == LVS_ICON)
4922 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
4923 lpptPosition->y += ICON_TOP_PADDING;
4925 lpptPosition->x += Origin.x;
4926 lpptPosition->y += Origin.y;
4928 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
4929 return TRUE;
4933 /***
4934 * DESCRIPTION:
4935 * Retrieves the bounding rectangle for a listview control item.
4937 * PARAMETER(S):
4938 * [I] infoPtr : valid pointer to the listview structure
4939 * [I] nItem : item index
4940 * [IO] lprc : bounding rectangle coordinates
4941 * lprc->left specifies the portion of the item for which the bounding
4942 * rectangle will be retrieved.
4944 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
4945 * including the icon and label.
4947 * * For LVS_ICON
4948 * * Experiment shows that native control returns:
4949 * * width = min (48, length of text line)
4950 * * .left = position.x - (width - iconsize.cx)/2
4951 * * .right = .left + width
4952 * * height = #lines of text * ntmHeight + icon height + 8
4953 * * .top = position.y - 2
4954 * * .bottom = .top + height
4955 * * separation between items .y = itemSpacing.cy - height
4956 * * .x = itemSpacing.cx - width
4957 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
4959 * * For LVS_ICON
4960 * * Experiment shows that native control returns:
4961 * * width = iconSize.cx + 16
4962 * * .left = position.x - (width - iconsize.cx)/2
4963 * * .right = .left + width
4964 * * height = iconSize.cy + 4
4965 * * .top = position.y - 2
4966 * * .bottom = .top + height
4967 * * separation between items .y = itemSpacing.cy - height
4968 * * .x = itemSpacing.cx - width
4969 * LVIR_LABEL Returns the bounding rectangle of the item text.
4971 * * For LVS_ICON
4972 * * Experiment shows that native control returns:
4973 * * width = text length
4974 * * .left = position.x - width/2
4975 * * .right = .left + width
4976 * * height = ntmH * linecount + 2
4977 * * .top = position.y + iconSize.cy + 6
4978 * * .bottom = .top + height
4979 * * separation between items .y = itemSpacing.cy - height
4980 * * .x = itemSpacing.cx - width
4981 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
4982 * rectangles, but excludes columns in report view.
4984 * RETURN:
4985 * SUCCESS : TRUE
4986 * FAILURE : FALSE
4988 * NOTES
4989 * Note that the bounding rectangle of the label in the LVS_ICON view depends
4990 * upon whether the window has the focus currently and on whether the item
4991 * is the one with the focus. Ensure that the control's record of which
4992 * item has the focus agrees with the items' records.
4994 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
4996 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4997 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4998 BOOL doLabel = TRUE, oversizedBox = FALSE;
4999 POINT Position, Origin;
5000 LVITEMW lvItem;
5001 RECT label_rect;
5003 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5005 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5006 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
5007 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) return FALSE;
5009 /* Be smart and try to figure out the minimum we have to do */
5010 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5011 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5012 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5013 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5014 oversizedBox = TRUE;
5016 /* get what we need from the item before hand, so we make
5017 * only one request. This can speed up things, if data
5018 * is stored on the app side */
5019 lvItem.mask = 0;
5020 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5021 if (doLabel) lvItem.mask |= LVIF_TEXT;
5022 lvItem.iItem = nItem;
5023 lvItem.iSubItem = 0;
5024 lvItem.pszText = szDispText;
5025 lvItem.cchTextMax = DISP_TEXT_SIZE;
5026 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5027 /* we got the state already up, simulate it here, to avoid a reget */
5028 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5030 lvItem.mask |= LVIF_STATE;
5031 lvItem.stateMask = LVIS_FOCUSED;
5032 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5035 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5036 lprc->left = LVIR_BOUNDS;
5037 switch(lprc->left)
5039 case LVIR_ICON:
5040 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL)) return FALSE;
5041 break;
5043 case LVIR_LABEL:
5044 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc)) return FALSE;
5045 break;
5047 case LVIR_BOUNDS:
5048 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL)) return FALSE;
5049 break;
5051 case LVIR_SELECTBOUNDS:
5052 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect)) return FALSE;
5053 UnionRect(lprc, lprc, &label_rect);
5054 break;
5056 default:
5057 WARN("Unknown value: %d\n", lprc->left);
5058 return FALSE;
5061 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5063 TRACE(" rect=%s\n", debugrect(lprc));
5065 return TRUE;
5068 /***
5069 * DESCRIPTION:
5070 * Retrieves the spacing between listview control items.
5072 * PARAMETER(S):
5073 * [I] infoPtr : valid pointer to the listview structure
5074 * [IO] lprc : rectangle to receive the output
5075 * on input, lprc->top = nSubItem
5076 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5078 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5079 * not only those of the first column.
5080 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5082 * RETURN:
5083 * TRUE: success
5084 * FALSE: failure
5086 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5088 POINT Position, Origin;
5089 LVITEMW lvItem;
5091 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5093 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5095 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
5096 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5098 lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5099 lvItem.iItem = nItem;
5100 lvItem.iSubItem = lprc->top;
5102 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5103 switch(lprc->left)
5105 case LVIR_ICON:
5106 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL)) return FALSE;
5107 break;
5109 case LVIR_LABEL:
5110 case LVIR_BOUNDS:
5111 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL)) return FALSE;
5112 break;
5114 default:
5115 ERR("Unknown bounds=%d\n", lprc->left);
5116 return FALSE;
5119 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5120 return TRUE;
5124 /***
5125 * DESCRIPTION:
5126 * Retrieves the width of a label.
5128 * PARAMETER(S):
5129 * [I] infoPtr : valid pointer to the listview structure
5131 * RETURN:
5132 * SUCCESS : string width (in pixels)
5133 * FAILURE : zero
5135 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5137 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5138 LVITEMW lvItem;
5140 TRACE("(nItem=%d)\n", nItem);
5142 lvItem.mask = LVIF_TEXT;
5143 lvItem.iItem = nItem;
5144 lvItem.iSubItem = 0;
5145 lvItem.pszText = szDispText;
5146 lvItem.cchTextMax = DISP_TEXT_SIZE;
5147 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5149 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5152 /***
5153 * DESCRIPTION:
5154 * Retrieves the spacing between listview control items.
5156 * PARAMETER(S):
5157 * [I] infoPtr : valid pointer to the listview structure
5158 * [I] BOOL : flag for small or large icon
5160 * RETURN:
5161 * Horizontal + vertical spacing
5163 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5165 LONG lResult;
5167 if (!bSmall)
5169 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5171 else
5173 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5174 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5175 else
5176 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5178 return lResult;
5181 /***
5182 * DESCRIPTION:
5183 * Retrieves the state of a listview control item.
5185 * PARAMETER(S):
5186 * [I] infoPtr : valid pointer to the listview structure
5187 * [I] nItem : item index
5188 * [I] uMask : state mask
5190 * RETURN:
5191 * State specified by the mask.
5193 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5195 LVITEMW lvItem;
5197 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5199 lvItem.iItem = nItem;
5200 lvItem.iSubItem = 0;
5201 lvItem.mask = LVIF_STATE;
5202 lvItem.stateMask = uMask;
5203 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5205 return lvItem.state & uMask;
5208 /***
5209 * DESCRIPTION:
5210 * Retrieves the text of a listview control item or subitem.
5212 * PARAMETER(S):
5213 * [I] hwnd : window handle
5214 * [I] nItem : item index
5215 * [IO] lpLVItem : item information
5216 * [I] isW : TRUE if lpLVItem is Unicode
5218 * RETURN:
5219 * SUCCESS : string length
5220 * FAILURE : 0
5222 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5224 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5226 lpLVItem->mask = LVIF_TEXT;
5227 lpLVItem->iItem = nItem;
5228 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5230 return textlenT(lpLVItem->pszText, isW);
5233 /***
5234 * DESCRIPTION:
5235 * Searches for an item based on properties + relationships.
5237 * PARAMETER(S):
5238 * [I] infoPtr : valid pointer to the listview structure
5239 * [I] nItem : item index
5240 * [I] uFlags : relationship flag
5242 * FIXME:
5243 * This function is very, very inefficient! Needs work.
5245 * RETURN:
5246 * SUCCESS : item index
5247 * FAILURE : -1
5249 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5251 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5252 UINT uMask = 0;
5253 LVFINDINFOW lvFindInfo;
5254 INT nCountPerColumn;
5255 INT i;
5257 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5258 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5260 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5262 if (uFlags & LVNI_CUT)
5263 uMask |= LVIS_CUT;
5265 if (uFlags & LVNI_DROPHILITED)
5266 uMask |= LVIS_DROPHILITED;
5268 if (uFlags & LVNI_FOCUSED)
5269 uMask |= LVIS_FOCUSED;
5271 if (uFlags & LVNI_SELECTED)
5272 uMask |= LVIS_SELECTED;
5274 /* if we're asked for the focused item, that's only one,
5275 * so it's worth optimizing */
5276 if (uFlags & LVNI_FOCUSED)
5278 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5279 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5282 if (uFlags & LVNI_ABOVE)
5284 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5286 while (nItem >= 0)
5288 nItem--;
5289 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5290 return nItem;
5293 else
5295 lvFindInfo.flags = LVFI_NEARESTXY;
5296 lvFindInfo.vkDirection = VK_UP;
5297 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5298 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5300 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5301 return nItem;
5305 else if (uFlags & LVNI_BELOW)
5307 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5309 while (nItem < infoPtr->nItemCount)
5311 nItem++;
5312 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5313 return nItem;
5316 else
5318 lvFindInfo.flags = LVFI_NEARESTXY;
5319 lvFindInfo.vkDirection = VK_DOWN;
5320 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5321 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5323 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5324 return nItem;
5328 else if (uFlags & LVNI_TOLEFT)
5330 if (uView == LVS_LIST)
5332 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5333 while (nItem - nCountPerColumn >= 0)
5335 nItem -= nCountPerColumn;
5336 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5337 return nItem;
5340 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5342 lvFindInfo.flags = LVFI_NEARESTXY;
5343 lvFindInfo.vkDirection = VK_LEFT;
5344 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5345 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5347 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5348 return nItem;
5352 else if (uFlags & LVNI_TORIGHT)
5354 if (uView == LVS_LIST)
5356 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5357 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5359 nItem += nCountPerColumn;
5360 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5361 return nItem;
5364 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5366 lvFindInfo.flags = LVFI_NEARESTXY;
5367 lvFindInfo.vkDirection = VK_RIGHT;
5368 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5369 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5371 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5372 return nItem;
5376 else
5378 nItem++;
5380 /* search by index */
5381 for (i = nItem; i < infoPtr->nItemCount; i++)
5383 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5384 return i;
5388 return -1;
5391 /* LISTVIEW_GetNumberOfWorkAreas */
5393 /***
5394 * DESCRIPTION:
5395 * Retrieves the origin coordinates when in icon or small icon display mode.
5397 * PARAMETER(S):
5398 * [I] infoPtr : valid pointer to the listview structure
5399 * [O] lpptOrigin : coordinate information
5401 * RETURN:
5402 * SUCCESS : TRUE
5403 * FAILURE : FALSE
5405 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5407 DWORD lStyle = infoPtr->dwStyle;
5408 UINT uView = lStyle & LVS_TYPEMASK;
5409 INT nHorzPos = 0, nVertPos = 0;
5410 SCROLLINFO scrollInfo;
5412 if (!lpptOrigin) return FALSE;
5414 scrollInfo.cbSize = sizeof(SCROLLINFO);
5415 scrollInfo.fMask = SIF_POS;
5417 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5418 nHorzPos = scrollInfo.nPos;
5419 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5420 nVertPos = scrollInfo.nPos;
5422 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5424 lpptOrigin->x = infoPtr->rcList.left;
5425 lpptOrigin->y = infoPtr->rcList.top;
5426 if (uView == LVS_LIST)
5427 nHorzPos *= infoPtr->nItemWidth;
5428 else if (uView == LVS_REPORT)
5429 nVertPos *= infoPtr->nItemHeight;
5431 lpptOrigin->x -= nHorzPos;
5432 lpptOrigin->y -= nVertPos;
5434 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5436 return TRUE;
5439 /***
5440 * DESCRIPTION:
5441 * Retrieves the width of a string.
5443 * PARAMETER(S):
5444 * [I] hwnd : window handle
5445 * [I] lpszText : text string to process
5446 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5448 * RETURN:
5449 * SUCCESS : string width (in pixels)
5450 * FAILURE : zero
5452 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5454 SIZE stringSize;
5456 stringSize.cx = 0;
5457 if (is_textT(lpszText, isW))
5459 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5460 HDC hdc = GetDC(infoPtr->hwndSelf);
5461 HFONT hOldFont = SelectObject(hdc, hFont);
5463 if (isW)
5464 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5465 else
5466 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5467 SelectObject(hdc, hOldFont);
5468 ReleaseDC(infoPtr->hwndSelf, hdc);
5470 return stringSize.cx;
5473 /***
5474 * DESCRIPTION:
5475 * Determines which listview item is located at the specified position.
5477 * PARAMETER(S):
5478 * [I] infoPtr : valid pointer to the listview structure
5479 * [IO] lpht : hit test information
5480 * [I] subitem : fill out iSubItem.
5481 * [I] select : return the index only if the hit selects the item
5483 * NOTE:
5484 * (mm 20001022): We must not allow iSubItem to be touched, for
5485 * an app might pass only a structure with space up to iItem!
5486 * (MS Office 97 does that for instance in the file open dialog)
5488 * RETURN:
5489 * SUCCESS : item index
5490 * FAILURE : -1
5492 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5494 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5495 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5496 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5497 POINT Origin, Position, opt;
5498 LVITEMW lvItem;
5499 ITERATOR i;
5501 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5503 lpht->flags = 0;
5504 lpht->iItem = -1;
5505 if (subitem) lpht->iSubItem = 0;
5507 if (infoPtr->rcList.left > lpht->pt.x)
5508 lpht->flags |= LVHT_TOLEFT;
5509 else if (infoPtr->rcList.right < lpht->pt.x)
5510 lpht->flags |= LVHT_TORIGHT;
5512 if (infoPtr->rcList.top > lpht->pt.y)
5513 lpht->flags |= LVHT_ABOVE;
5514 else if (infoPtr->rcList.bottom < lpht->pt.y)
5515 lpht->flags |= LVHT_BELOW;
5517 TRACE("lpht->flags=0x%x\n", lpht->flags);
5518 if (lpht->flags) return -1;
5520 lpht->flags |= LVHT_NOWHERE;
5522 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
5524 /* first deal with the large items */
5525 rcSearch.left = lpht->pt.x;
5526 rcSearch.top = lpht->pt.y;
5527 rcSearch.right = rcSearch.left + 1;
5528 rcSearch.bottom = rcSearch.top + 1;
5530 iterator_frameditems(&i, infoPtr, &rcSearch);
5531 iterator_next(&i); /* go to first item in the sequence */
5532 lpht->iItem = i.nItem;
5533 iterator_destroy(&i);
5535 TRACE("lpht->iItem=%d\n", lpht->iItem);
5536 if (lpht->iItem == -1) return -1;
5538 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5539 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5540 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5541 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5542 lvItem.iItem = lpht->iItem;
5543 lvItem.iSubItem = 0;
5544 lvItem.pszText = szDispText;
5545 lvItem.cchTextMax = DISP_TEXT_SIZE;
5546 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5547 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5549 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel)) return -1;
5550 if (!LISTVIEW_GetItemOrigin(infoPtr, lpht->iItem, &Position)) return -1;
5551 opt.x = lpht->pt.x - Position.x - Origin.x;
5552 opt.y = lpht->pt.y - Position.y - Origin.y;
5554 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
5555 rcBounds = rcBox;
5556 else
5557 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5558 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5559 if (!PtInRect(&rcBounds, opt)) return -1;
5561 if (PtInRect(&rcIcon, opt))
5562 lpht->flags |= LVHT_ONITEMICON;
5563 else if (PtInRect(&rcLabel, opt))
5564 lpht->flags |= LVHT_ONITEMLABEL;
5565 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5566 lpht->flags |= LVHT_ONITEMSTATEICON;
5567 if (lpht->flags & LVHT_ONITEM)
5568 lpht->flags &= ~LVHT_NOWHERE;
5570 TRACE("lpht->flags=0x%x\n", lpht->flags);
5571 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5573 INT j;
5575 rcBounds.right = rcBounds.left;
5576 for (j = 0; j < infoPtr->hdpaColumns->nItemCount; j++)
5578 rcBounds.left = rcBounds.right;
5579 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5580 if (PtInRect(&rcBounds, opt))
5582 lpht->iSubItem = j;
5583 break;
5588 if (!select || lpht->iItem == -1) return lpht->iItem;
5590 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5592 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5593 return PtInRect(&rcBounds, opt) ? lpht->iItem : -1;
5597 /***
5598 * DESCRIPTION:
5599 * Inserts a new column.
5601 * PARAMETER(S):
5602 * [I] infoPtr : valid pointer to the listview structure
5603 * [I] INT : column index
5604 * [I] LPLVCOLUMNW : column information
5606 * RETURN:
5607 * SUCCESS : new column index
5608 * FAILURE : -1
5610 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5611 LPLVCOLUMNW lpColumn, BOOL isW)
5613 COLUMN_INFO *lpColumnInfo = NULL;
5614 RECT rcCol;
5615 INT nNewColumn;
5616 HDITEMW hdi;
5618 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5620 if (!lpColumn) return -1;
5622 hdi.mask = hdi.fmt = 0;
5623 if (lpColumn->mask & LVCF_FMT)
5625 /* format member is valid */
5626 hdi.mask |= HDI_FORMAT;
5628 /* set text alignment (leftmost column must be left-aligned) */
5629 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5630 hdi.fmt |= HDF_LEFT;
5631 else if (lpColumn->fmt & LVCFMT_RIGHT)
5632 hdi.fmt |= HDF_RIGHT;
5633 else if (lpColumn->fmt & LVCFMT_CENTER)
5634 hdi.fmt |= HDF_CENTER;
5636 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5637 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5639 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5641 hdi.fmt |= HDF_IMAGE;
5642 hdi.iImage = I_IMAGECALLBACK;
5646 if (lpColumn->mask & LVCF_WIDTH)
5648 hdi.mask |= HDI_WIDTH;
5649 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5651 /* make it fill the remainder of the controls width */
5652 RECT rcHeader;
5653 INT item_index;
5655 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5656 if (LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader))
5657 hdi.cxy += rcHeader.right - rcHeader.left;
5659 /* retrieve the layout of the header */
5660 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5661 TRACE("start cxy=%d rcHeader=%s\n", hdi.cxy, debugrect(&rcHeader));
5663 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
5665 else
5666 hdi.cxy = lpColumn->cx;
5669 if (lpColumn->mask & LVCF_TEXT)
5671 hdi.mask |= HDI_TEXT | HDI_FORMAT;
5672 hdi.fmt |= HDF_STRING;
5673 hdi.pszText = lpColumn->pszText;
5674 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
5677 if (lpColumn->mask & LVCF_IMAGE)
5679 hdi.mask |= HDI_IMAGE;
5680 hdi.iImage = lpColumn->iImage;
5683 if (lpColumn->mask & LVCF_ORDER)
5685 hdi.mask |= HDI_ORDER;
5686 hdi.iOrder = lpColumn->iOrder;
5689 /* insert item in header control */
5690 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5691 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5692 (WPARAM)nColumn, (LPARAM)&hdi);
5693 if (nNewColumn == -1) return -1;
5695 /* create our own column info */
5696 if (!(lpColumnInfo = COMCTL32_Alloc(sizeof(COLUMN_INFO)))) goto fail;
5697 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
5698 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &rcCol)) goto fail;
5699 lpColumnInfo->rcHeader = rcCol;
5700 if (lpColumn->mask & LVCF_FMT)
5702 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT) lpColumnInfo->align = DT_LEFT;
5703 else if (lpColumn->fmt & LVCFMT_RIGHT) lpColumnInfo->align = DT_RIGHT;
5704 else if (lpColumn->fmt & LVCFMT_CENTER) lpColumnInfo->align = DT_CENTER;
5706 if (lpColumn->fmt & LVCFMT_IMAGE) lpColumnInfo->hasImage = TRUE;
5708 else
5710 lpColumnInfo->align = DT_LEFT;
5711 lpColumnInfo->hasImage = (nColumn == 0);
5714 /* now we have to actually adjust the data */
5715 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
5717 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
5718 HDPA hdpaSubItems;
5719 INT nItem, i;
5721 /* preallocate memory, so we can fail gracefully */
5722 if (nNewColumn == 0)
5724 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->nItemCount);
5725 if (!lpNewItems) goto fail;
5726 for (i = 0; i < infoPtr->nItemCount; i++)
5727 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
5728 if (i != infoPtr->nItemCount)
5730 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5731 COMCTL32_Free(lpNewItems);
5732 goto fail;
5736 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5738 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5739 if (!hdpaSubItems) continue;
5740 for (i = 1; i < hdpaSubItems->nItemCount; i++)
5742 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
5743 if (!lpSubItem) break;
5744 if (lpSubItem->iSubItem >= nNewColumn)
5745 lpSubItem->iSubItem++;
5748 /* if we found our subitem, zapp it */
5749 if (nNewColumn == 0)
5751 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
5752 lpSubItem = lpNewItems[nItem];
5753 lpSubItem->hdr = lpMainItem->hdr;
5754 lpSubItem->iSubItem = 1;
5755 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
5756 lpMainItem->iSubItem = 0;
5757 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
5761 COMCTL32_Free(lpNewItems);
5764 /* make space for the new column */
5765 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, rcCol.right - rcCol.left);
5767 return nNewColumn;
5769 fail:
5770 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
5771 if (lpColumnInfo)
5773 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
5774 COMCTL32_Free(lpColumnInfo);
5776 return -1;
5779 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5780 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5781 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5782 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5783 their own sort proc. when sending LVM_SORTITEMS.
5785 /* Platform SDK:
5786 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5788 LVS_SORTXXX must be specified,
5789 LVS_OWNERDRAW is not set,
5790 <item>.pszText is not LPSTR_TEXTCALLBACK.
5792 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5793 are sorted based on item text..."
5795 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5797 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5798 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5799 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5801 /* if we're sorting descending, negate the return value */
5802 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5805 /***
5806 * nESCRIPTION:
5807 * Inserts a new item in the listview control.
5809 * PARAMETER(S):
5810 * [I] infoPtr : valid pointer to the listview structure
5811 * [I] lpLVItem : item information
5812 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5814 * RETURN:
5815 * SUCCESS : new item index
5816 * FAILURE : -1
5818 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5820 LONG lStyle = infoPtr->dwStyle;
5821 UINT uView = lStyle & LVS_TYPEMASK;
5822 INT nItem = -1;
5823 HDPA hdpaSubItems;
5824 NMLISTVIEW nmlv;
5825 LISTVIEW_ITEM *lpItem;
5826 BOOL is_sorted, has_changed;
5828 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5830 if (lStyle & LVS_OWNERDATA)
5832 nItem = infoPtr->nItemCount;
5833 infoPtr->nItemCount++;
5834 return nItem;
5837 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5838 if (!lpLVItem || lpLVItem->iSubItem) return -1;
5840 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5842 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5843 return -1;
5845 /* insert item in listview control data structure */
5846 if ( (hdpaSubItems = DPA_Create(8)) )
5847 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5848 if (nItem == -1) goto fail;
5850 /* FIXME: is the handling of this LVS_OWNERDRAWFIXED correct? */
5851 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5852 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5854 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
5855 is_sorted ? infoPtr->nItemCount + 1 : lpLVItem->iItem,
5856 hdpaSubItems );
5857 if (nItem == -1) goto fail;
5858 /* the array may be sparsly populated, we can't just increment the count here */
5859 infoPtr->nItemCount = infoPtr->hdpaItems->nItemCount;
5861 /* set the item attributes */
5862 if (!set_main_item(infoPtr, lpLVItem, TRUE, isW, &has_changed)) goto undo;
5864 /* if we're sorted, sort the list, and update the index */
5865 if (is_sorted)
5867 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5868 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5869 assert(nItem != -1);
5872 /* make room for the position, if we are in the right mode */
5873 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5875 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5876 goto undo;
5877 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5879 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5880 goto undo;
5884 /* Add the subitem list to the items array. Do this last in case we go to
5885 * fail during the above.
5887 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5889 /* send LVN_INSERTITEM notification */
5890 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5891 nmlv.iItem = nItem;
5892 nmlv.lParam = lpItem->lParam;
5893 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5895 /* align items (set position of each item) */
5896 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5898 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
5899 else LISTVIEW_AlignTop(infoPtr);
5902 LISTVIEW_UpdateScroll(infoPtr);
5904 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
5906 TRACE(" <- %d\n", nItem);
5907 return nItem;
5909 undo:
5910 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5911 infoPtr->nItemCount--;
5912 fail:
5913 DPA_DeletePtr(hdpaSubItems, 0);
5914 DPA_Destroy (hdpaSubItems);
5915 COMCTL32_Free (lpItem);
5916 return -1;
5919 /***
5920 * DESCRIPTION:
5921 * Redraws a range of items.
5923 * PARAMETER(S):
5924 * [I] infoPtr : valid pointer to the listview structure
5925 * [I] INT : first item
5926 * [I] INT : last item
5928 * RETURN:
5929 * SUCCESS : TRUE
5930 * FAILURE : FALSE
5932 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
5934 INT i;
5936 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
5937 max(nFirst, nLast) >= infoPtr->nItemCount)
5938 return FALSE;
5940 for (i = nFirst; i <= nLast; i++)
5941 LISTVIEW_InvalidateItem(infoPtr, i);
5943 return TRUE;
5946 /***
5947 * DESCRIPTION:
5948 * Scroll the content of a listview.
5950 * PARAMETER(S):
5951 * [I] infoPtr : valid pointer to the listview structure
5952 * [I] INT : horizontal scroll amount in pixels
5953 * [I] INT : vertical scroll amount in pixels
5955 * RETURN:
5956 * SUCCESS : TRUE
5957 * FAILURE : FALSE
5959 * COMMENTS:
5960 * If the control is in report mode (LVS_REPORT) the control can
5961 * be scrolled only in line increments. "dy" will be rounded to the
5962 * nearest number of pixels that are a whole line. Ex: if line height
5963 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
5964 * is passed the the scroll will be 0. (per MSDN 7/2002)
5966 * For: (per experimentaion with native control and CSpy ListView)
5967 * LVS_ICON dy=1 = 1 pixel (vertical only)
5968 * dx ignored
5969 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
5970 * dx ignored
5971 * LVS_LIST dx=1 = 1 column (horizontal only)
5972 * but will only scroll 1 column per message
5973 * no matter what the value.
5974 * dy must be 0 or FALSE returned.
5975 * LVS_REPORT dx=1 = 1 pixel
5976 * dy= see above
5979 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
5981 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
5982 case LVS_REPORT:
5983 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
5984 dy /= infoPtr->nItemHeight;
5985 break;
5986 case LVS_LIST:
5987 if (dy != 0) return FALSE;
5988 break;
5989 default: /* icon */
5990 dx = 0;
5991 break;
5994 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
5995 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
5997 return TRUE;
6000 /***
6001 * DESCRIPTION:
6002 * Sets the background color.
6004 * PARAMETER(S):
6005 * [I] infoPtr : valid pointer to the listview structure
6006 * [I] COLORREF : background color
6008 * RETURN:
6009 * SUCCESS : TRUE
6010 * FAILURE : FALSE
6012 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6014 TRACE("(clrBk=%lx)\n", clrBk);
6016 if(infoPtr->clrBk != clrBk) {
6017 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6018 infoPtr->clrBk = clrBk;
6019 if (clrBk == CLR_NONE)
6020 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6021 else
6022 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6023 LISTVIEW_InvalidateList(infoPtr);
6026 return TRUE;
6029 /* LISTVIEW_SetBkImage */
6031 /***
6032 * DESCRIPTION:
6033 * Sets the attributes of a header item.
6035 * PARAMETER(S):
6036 * [I] infoPtr : valid pointer to the listview structure
6037 * [I] INT : column index
6038 * [I] LPLVCOLUMNW : column attributes
6039 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6040 * otherwise it is in fact a LPLVCOLUMNA
6042 * RETURN:
6043 * SUCCESS : TRUE
6044 * FAILURE : FALSE
6046 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6047 LPLVCOLUMNW lpColumn, BOOL isW)
6049 BOOL bResult = FALSE;
6050 HDITEMW hdi, hdiget;
6052 if ((lpColumn != NULL) && (nColumn >= 0) && (nColumn < infoPtr->hdpaColumns->nItemCount))
6054 /* initialize memory */
6055 ZeroMemory(&hdi, sizeof(hdi));
6057 if (lpColumn->mask & LVCF_FMT)
6059 /* format member is valid */
6060 hdi.mask |= HDI_FORMAT;
6062 /* get current format first */
6063 hdiget.mask = HDI_FORMAT;
6064 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6065 /* preserve HDF_STRING if present */
6066 hdi.fmt = hdiget.fmt & HDF_STRING;
6068 /* set text alignment (leftmost column must be left-aligned) */
6069 if (nColumn == 0)
6071 hdi.fmt |= HDF_LEFT;
6073 else
6075 if (lpColumn->fmt & LVCFMT_LEFT)
6076 hdi.fmt |= HDF_LEFT;
6077 else if (lpColumn->fmt & LVCFMT_RIGHT)
6078 hdi.fmt |= HDF_RIGHT;
6079 else if (lpColumn->fmt & LVCFMT_CENTER)
6080 hdi.fmt |= HDF_CENTER;
6083 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6084 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6086 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6087 hdi.fmt |= HDF_IMAGE;
6089 if (lpColumn->fmt & LVCFMT_IMAGE)
6091 hdi.fmt |= HDF_IMAGE;
6092 hdi.iImage = I_IMAGECALLBACK;
6096 if (lpColumn->mask & LVCF_WIDTH)
6098 hdi.mask |= HDI_WIDTH;
6099 hdi.cxy = lpColumn->cx;
6102 if (lpColumn->mask & LVCF_TEXT)
6104 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6105 hdi.pszText = lpColumn->pszText;
6106 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6107 hdi.fmt |= HDF_STRING;
6110 if (lpColumn->mask & LVCF_IMAGE)
6112 hdi.mask |= HDI_IMAGE;
6113 hdi.iImage = lpColumn->iImage;
6116 if (lpColumn->mask & LVCF_ORDER)
6118 hdi.mask |= HDI_ORDER;
6119 hdi.iOrder = lpColumn->iOrder;
6122 /* set header item attributes */
6123 if (isW)
6124 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6125 else
6126 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6129 return bResult;
6132 /***
6133 * DESCRIPTION:
6134 * Sets the column order array
6136 * PARAMETERS:
6137 * [I] infoPtr : valid pointer to the listview structure
6138 * [I] INT : number of elements in column order array
6139 * [I] INT : pointer to column order array
6141 * RETURN:
6142 * SUCCESS : TRUE
6143 * FAILURE : FALSE
6145 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6147 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6149 if (!lpiArray)
6150 return FALSE;
6152 return TRUE;
6156 /***
6157 * DESCRIPTION:
6158 * Sets the width of a column
6160 * PARAMETERS:
6161 * [I] infoPtr : valid pointer to the listview structure
6162 * [I] INT : column index
6163 * [I] INT : column width
6165 * RETURN:
6166 * SUCCESS : TRUE
6167 * FAILURE : FALSE
6169 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6171 HDITEMW hdi;
6172 LRESULT lret;
6173 LONG lStyle = infoPtr->dwStyle;
6174 UINT uView = lStyle & LVS_TYPEMASK;
6175 HDC hdc;
6176 HFONT header_font;
6177 HFONT old_font;
6178 SIZE size;
6179 WCHAR text_buffer[DISP_TEXT_SIZE];
6180 INT header_item_count;
6181 INT item_index;
6182 INT nLabelWidth;
6183 RECT rcHeader;
6184 LVITEMW lvItem;
6185 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6187 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6189 /* set column width only if in report or list mode */
6190 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6192 /* take care of invalid cx values */
6193 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6194 else if (uView == LVS_LIST && cx < 1) return FALSE;
6196 /* resize all columns if in LVS_LIST mode */
6197 if(uView == LVS_LIST)
6199 infoPtr->nItemWidth = cx;
6200 LISTVIEW_InvalidateList(infoPtr);
6201 return TRUE;
6204 /* autosize based on listview items width */
6205 if(cx == LVSCW_AUTOSIZE)
6207 /* set the width of the column to the width of the widest item */
6208 if (iCol == 0 || uView == LVS_LIST)
6210 cx = 0;
6211 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6213 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6214 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6216 if (infoPtr->himlSmall)
6217 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6219 else
6221 lvItem.iSubItem = iCol;
6222 lvItem.mask = LVIF_TEXT;
6223 lvItem.pszText = szDispText;
6224 lvItem.cchTextMax = DISP_TEXT_SIZE;
6225 cx = 0;
6226 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6228 lvItem.iItem = item_index;
6229 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6230 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6231 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6234 cx += TRAILING_PADDING;
6235 } /* autosize based on listview header width */
6236 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6238 header_item_count = infoPtr->hdpaColumns->nItemCount;
6240 /* if iCol is the last column make it fill the remainder of the controls width */
6241 if(iCol == (header_item_count - 1)) {
6242 cx = 0;
6244 for(item_index = 0; item_index < (header_item_count - 1); item_index++)
6246 if (LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader))
6247 cx += rcHeader.right - rcHeader.left;
6250 /* retrieve the layout of the header */
6251 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6253 cx = (rcHeader.right - rcHeader.left) - cx;
6255 else
6257 /* Despite what the MS docs say, if this is not the last
6258 column, then MS resizes the column to the width of the
6259 largest text string in the column, including headers
6260 and items. This is different from LVSCW_AUTOSIZE in that
6261 LVSCW_AUTOSIZE ignores the header string length.
6264 /* retrieve header font */
6265 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6267 /* retrieve header text */
6268 hdi.mask = HDI_TEXT;
6269 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6270 hdi.pszText = text_buffer;
6272 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6274 /* determine the width of the text in the header */
6275 hdc = GetDC(infoPtr->hwndSelf);
6276 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6278 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6280 SelectObject(hdc, old_font); /* restore the old font */
6281 ReleaseDC(infoPtr->hwndSelf, hdc);
6283 lvItem.iSubItem = iCol;
6284 lvItem.mask = LVIF_TEXT;
6285 lvItem.pszText = szDispText;
6286 lvItem.cchTextMax = DISP_TEXT_SIZE;
6287 cx = size.cx;
6288 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6290 lvItem.iItem = item_index;
6291 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6292 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6293 nLabelWidth += TRAILING_PADDING;
6294 /* While it is possible for subitems to have icons, even MS messes
6295 up the positioning, so I suspect no applications actually use
6296 them. */
6297 if (item_index == 0 && infoPtr->himlSmall)
6298 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6299 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6304 /* call header to update the column change */
6305 hdi.mask = HDI_WIDTH;
6307 hdi.cxy = cx;
6308 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6310 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6312 return lret;
6315 /***
6316 * DESCRIPTION:
6317 * Sets the extended listview style.
6319 * PARAMETERS:
6320 * [I] infoPtr : valid pointer to the listview structure
6321 * [I] DWORD : mask
6322 * [I] DWORD : style
6324 * RETURN:
6325 * SUCCESS : previous style
6326 * FAILURE : 0
6328 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6330 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6332 /* set new style */
6333 if (dwMask)
6334 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6335 else
6336 infoPtr->dwLvExStyle = dwStyle;
6338 return dwOldStyle;
6341 /***
6342 * DESCRIPTION:
6343 * Sets the new hot cursor used during hot tracking and hover selection.
6345 * PARAMETER(S):
6346 * [I] infoPtr : valid pointer to the listview structure
6347 * [I} hCurosr : the new hot cursor handle
6349 * RETURN:
6350 * Returns the previous hot cursor
6352 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6354 HCURSOR oldCursor = infoPtr->hHotCursor;
6355 infoPtr->hHotCursor = hCursor;
6356 return oldCursor;
6360 /***
6361 * DESCRIPTION:
6362 * Sets the hot item index.
6364 * PARAMETERS:
6365 * [I] infoPtr : valid pointer to the listview structure
6366 * [I] INT : index
6368 * RETURN:
6369 * SUCCESS : previous hot item index
6370 * FAILURE : -1 (no hot item)
6372 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6374 INT iOldIndex = infoPtr->nHotItem;
6375 infoPtr->nHotItem = iIndex;
6376 return iOldIndex;
6380 /***
6381 * DESCRIPTION:
6382 * Sets the amount of time the cursor must hover over an item before it is selected.
6384 * PARAMETER(S):
6385 * [I] infoPtr : valid pointer to the listview structure
6386 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6388 * RETURN:
6389 * Returns the previous hover time
6391 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6393 DWORD oldHoverTime = infoPtr->dwHoverTime;
6394 infoPtr->dwHoverTime = dwHoverTime;
6395 return oldHoverTime;
6398 /***
6399 * DESCRIPTION:
6400 * Sets spacing for icons of LVS_ICON style.
6402 * PARAMETER(S):
6403 * [I] infoPtr : valid pointer to the listview structure
6404 * [I] DWORD : MAKELONG(cx, cy)
6406 * RETURN:
6407 * MAKELONG(oldcx, oldcy)
6409 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6411 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6412 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6413 LONG lStyle = infoPtr->dwStyle;
6414 UINT uView = lStyle & LVS_TYPEMASK;
6416 TRACE("requested=(%d,%d)\n", cx, cy);
6418 /* this is supported only for LVS_ICON style */
6419 if (uView != LVS_ICON) return oldspacing;
6421 /* set to defaults, if instructed to */
6422 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6423 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6425 /* if 0 then compute width
6426 * FIXME: Should scan each item and determine max width of
6427 * icon or label, then make that the width */
6428 if (cx == 0)
6429 cx = infoPtr->iconSpacing.cx;
6431 /* if 0 then compute height */
6432 if (cy == 0)
6433 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6434 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6437 infoPtr->iconSpacing.cx = cx;
6438 infoPtr->iconSpacing.cy = cy;
6440 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6441 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6442 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6443 infoPtr->ntmHeight);
6445 /* these depend on the iconSpacing */
6446 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6447 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6449 return oldspacing;
6452 inline void update_icon_size(HIMAGELIST himl, SIZE *size)
6454 INT cx, cy;
6456 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6458 size->cx = cx;
6459 size->cy = cy;
6461 else
6462 size->cx = size->cy = 0;
6465 /***
6466 * DESCRIPTION:
6467 * Sets image lists.
6469 * PARAMETER(S):
6470 * [I] infoPtr : valid pointer to the listview structure
6471 * [I] INT : image list type
6472 * [I] HIMAGELIST : image list handle
6474 * RETURN:
6475 * SUCCESS : old image list
6476 * FAILURE : NULL
6478 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6480 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6481 INT oldHeight = infoPtr->nItemHeight;
6482 HIMAGELIST himlOld = 0;
6484 switch (nType)
6486 case LVSIL_NORMAL:
6487 himlOld = infoPtr->himlNormal;
6488 infoPtr->himlNormal = himl;
6489 if (uView == LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6490 LISTVIEW_SetIconSpacing(infoPtr, 0);
6491 break;
6493 case LVSIL_SMALL:
6494 himlOld = infoPtr->himlSmall;
6495 infoPtr->himlSmall = himl;
6496 if (uView != LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6497 break;
6499 case LVSIL_STATE:
6500 himlOld = infoPtr->himlState;
6501 infoPtr->himlState = himl;
6502 update_icon_size(himl, &infoPtr->iconStateSize);
6503 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6504 break;
6506 default:
6507 ERR("Unknown icon type=%d\n", nType);
6508 return NULL;
6511 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6512 if (infoPtr->nItemHeight != oldHeight)
6513 LISTVIEW_UpdateScroll(infoPtr);
6515 return himlOld;
6518 /***
6519 * DESCRIPTION:
6520 * Preallocates memory (does *not* set the actual count of items !)
6522 * PARAMETER(S):
6523 * [I] infoPtr : valid pointer to the listview structure
6524 * [I] INT : item count (projected number of items to allocate)
6525 * [I] DWORD : update flags
6527 * RETURN:
6528 * SUCCESS : TRUE
6529 * FAILURE : FALSE
6531 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6533 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6535 if (infoPtr->dwStyle & LVS_OWNERDATA)
6537 int precount,topvisible;
6539 TRACE("LVS_OWNERDATA is set!\n");
6540 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6541 FIXME("flags %s %s not implemented\n",
6542 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6543 : "",
6544 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6546 LISTVIEW_DeselectAll(infoPtr);
6548 precount = infoPtr->nItemCount;
6549 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6550 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6552 infoPtr->nItemCount = nItems;
6553 infoPtr->nItemWidth = max(LISTVIEW_CalculateMaxWidth(infoPtr),
6554 DEFAULT_COLUMN_WIDTH);
6556 LISTVIEW_UpdateSize(infoPtr);
6557 LISTVIEW_UpdateScroll(infoPtr);
6559 if (min(precount,infoPtr->nItemCount) < topvisible)
6560 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6562 else
6564 /* According to MSDN for non-LVS_OWNERDATA this is just
6565 * a performance issue. The control allocates its internal
6566 * data structures for the number of items specified. It
6567 * cuts down on the number of memory allocations. Therefore
6568 * we will just issue a WARN here
6570 WARN("for non-ownerdata performance option not implemented.\n");
6573 return TRUE;
6576 /***
6577 * DESCRIPTION:
6578 * Sets the position of an item.
6580 * PARAMETER(S):
6581 * [I] infoPtr : valid pointer to the listview structure
6582 * [I] nItem : item index
6583 * [I] pt : coordinate
6585 * RETURN:
6586 * SUCCESS : TRUE
6587 * FAILURE : FALSE
6589 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6591 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6592 POINT old;
6594 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6596 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6597 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6599 /* This point value seems to be an undocumented feature.
6600 * The best guess is that it means either at the origin,
6601 * or at true beginning of the list. I will assume the origin. */
6602 if ((pt.x == -1) && (pt.y == -1))
6603 LISTVIEW_GetOrigin(infoPtr, &pt);
6604 else if (uView == LVS_ICON)
6606 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6607 pt.y -= ICON_TOP_PADDING;
6610 /* save the old position */
6611 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
6612 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
6614 /* Is the position changing? */
6615 if (pt.x == old.x && pt.y == old.y) return TRUE;
6617 /* FIXME: shouldn't we invalidate, as the item moved? */
6619 /* Allocating a POINTER for every item is too resource intensive,
6620 * so we'll keep the (x,y) in different arrays */
6621 if (DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)pt.x) &&
6622 DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)pt.y) )
6623 return TRUE;
6625 ERR("We should never fail here (nItem=%d, pt=%s), please report.\n",
6626 nItem, debugpoint(&pt));
6627 return FALSE;
6630 /***
6631 * DESCRIPTION:
6632 * Sets the state of one or many items.
6634 * PARAMETER(S):
6635 * [I] infoPtr : valid pointer to the listview structure
6636 * [I]INT : item index
6637 * [I] LPLVITEM : item or subitem info
6639 * RETURN:
6640 * SUCCESS : TRUE
6641 * FAILURE : FALSE
6643 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6645 BOOL bResult = TRUE;
6646 LVITEMW lvItem;
6648 lvItem.iItem = nItem;
6649 lvItem.iSubItem = 0;
6650 lvItem.mask = LVIF_STATE;
6651 lvItem.state = lpLVItem->state;
6652 lvItem.stateMask = lpLVItem->stateMask;
6653 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6655 if (nItem == -1)
6657 /* apply to all items */
6658 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6659 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6661 else
6662 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6664 return bResult;
6667 /***
6668 * DESCRIPTION:
6669 * Sets the text of an item or subitem.
6671 * PARAMETER(S):
6672 * [I] hwnd : window handle
6673 * [I] nItem : item index
6674 * [I] lpLVItem : item or subitem info
6675 * [I] isW : TRUE if input is Unicode
6677 * RETURN:
6678 * SUCCESS : TRUE
6679 * FAILURE : FALSE
6681 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6683 LVITEMW lvItem;
6685 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6687 lvItem.iItem = nItem;
6688 lvItem.iSubItem = lpLVItem->iSubItem;
6689 lvItem.mask = LVIF_TEXT;
6690 lvItem.pszText = lpLVItem->pszText;
6691 lvItem.cchTextMax = lpLVItem->cchTextMax;
6693 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6695 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6698 /***
6699 * DESCRIPTION:
6700 * Set item index that marks the start of a multiple selection.
6702 * PARAMETER(S):
6703 * [I] infoPtr : valid pointer to the listview structure
6704 * [I] INT : index
6706 * RETURN:
6707 * Index number or -1 if there is no selection mark.
6709 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6711 INT nOldIndex = infoPtr->nSelectionMark;
6713 TRACE("(nIndex=%d)\n", nIndex);
6715 infoPtr->nSelectionMark = nIndex;
6717 return nOldIndex;
6720 /***
6721 * DESCRIPTION:
6722 * Sets the text background color.
6724 * PARAMETER(S):
6725 * [I] infoPtr : valid pointer to the listview structure
6726 * [I] COLORREF : text background color
6728 * RETURN:
6729 * SUCCESS : TRUE
6730 * FAILURE : FALSE
6732 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6734 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6736 if (infoPtr->clrTextBk != clrTextBk)
6738 infoPtr->clrTextBk = clrTextBk;
6739 LISTVIEW_InvalidateList(infoPtr);
6742 return TRUE;
6745 /***
6746 * DESCRIPTION:
6747 * Sets the text foreground color.
6749 * PARAMETER(S):
6750 * [I] infoPtr : valid pointer to the listview structure
6751 * [I] COLORREF : text color
6753 * RETURN:
6754 * SUCCESS : TRUE
6755 * FAILURE : FALSE
6757 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6759 TRACE("(clrText=%lx)\n", clrText);
6761 if (infoPtr->clrText != clrText)
6763 infoPtr->clrText = clrText;
6764 LISTVIEW_InvalidateList(infoPtr);
6767 return TRUE;
6770 /* LISTVIEW_SetToolTips */
6771 /* LISTVIEW_SetUnicodeFormat */
6772 /* LISTVIEW_SetWorkAreas */
6774 /***
6775 * DESCRIPTION:
6776 * Callback internally used by LISTVIEW_SortItems()
6778 * PARAMETER(S):
6779 * [I] LPVOID : first LISTVIEW_ITEM to compare
6780 * [I] LPVOID : second LISTVIEW_ITEM to compare
6781 * [I] LPARAM : HWND of control
6783 * RETURN:
6784 * if first comes before second : negative
6785 * if first comes after second : positive
6786 * if first and second are equivalent : zero
6788 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6790 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6791 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6792 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6794 /* Forward the call to the client defined callback */
6795 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6798 /***
6799 * DESCRIPTION:
6800 * Sorts the listview items.
6802 * PARAMETER(S):
6803 * [I] infoPtr : valid pointer to the listview structure
6804 * [I] WPARAM : application-defined value
6805 * [I] LPARAM : pointer to comparision callback
6807 * RETURN:
6808 * SUCCESS : TRUE
6809 * FAILURE : FALSE
6811 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6813 UINT lStyle = infoPtr->dwStyle;
6814 HDPA hdpaSubItems;
6815 LISTVIEW_ITEM *lpItem;
6816 LPVOID selectionMarkItem;
6817 LVITEMW item;
6818 int i;
6820 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6822 if (lStyle & LVS_OWNERDATA) return FALSE;
6824 if (!infoPtr->hdpaItems) return FALSE;
6826 /* if there are 0 or 1 items, there is no need to sort */
6827 if (infoPtr->nItemCount < 2) return TRUE;
6829 if (infoPtr->nFocusedItem >= 0)
6831 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6832 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6833 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6835 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
6836 /* clear the lpItem->state for non-selected ones */
6837 /* remove the selection ranges */
6839 infoPtr->pfnCompare = pfnCompare;
6840 infoPtr->lParamSort = lParamSort;
6841 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6843 /* Adjust selections and indices so that they are the way they should
6844 * be after the sort (otherwise, the list items move around, but
6845 * whatever is at the item's previous original position will be
6846 * selected instead)
6848 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6849 for (i=0; i < infoPtr->nItemCount; i++)
6851 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6852 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6854 if (lpItem->state & LVIS_SELECTED)
6856 item.state = LVIS_SELECTED;
6857 item.stateMask = LVIS_SELECTED;
6858 LISTVIEW_SetItemState(infoPtr, i, &item);
6860 if (lpItem->state & LVIS_FOCUSED)
6862 infoPtr->nFocusedItem = i;
6863 lpItem->state &= ~LVIS_FOCUSED;
6866 if (selectionMarkItem != NULL)
6867 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6868 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6870 /* align the items */
6871 LISTVIEW_AlignTop(infoPtr);
6873 /* refresh the display */
6874 LISTVIEW_InvalidateList(infoPtr); /* FIXME: display should not change for [SMALL]ICON view */
6876 return TRUE;
6879 /***
6880 * DESCRIPTION:
6881 * Updates an items or rearranges the listview control.
6883 * PARAMETER(S):
6884 * [I] infoPtr : valid pointer to the listview structure
6885 * [I] INT : item index
6887 * RETURN:
6888 * SUCCESS : TRUE
6889 * FAILURE : FALSE
6891 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
6893 LONG lStyle = infoPtr->dwStyle;
6894 UINT uView = lStyle & LVS_TYPEMASK;
6896 TRACE("(nItem=%d)\n", nItem);
6898 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6900 /* rearrange with default alignment style */
6901 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
6902 LISTVIEW_Arrange(infoPtr, 0);
6903 else
6904 LISTVIEW_InvalidateItem(infoPtr, nItem);
6906 return TRUE;
6910 /***
6911 * DESCRIPTION:
6912 * Creates the listview control.
6914 * PARAMETER(S):
6915 * [I] hwnd : window handle
6916 * [I] lpcs : the create parameters
6918 * RETURN:
6919 * Success: 0
6920 * Failure: -1
6922 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
6924 LISTVIEW_INFO *infoPtr;
6925 UINT uView = lpcs->style & LVS_TYPEMASK;
6926 LOGFONTW logFont;
6928 TRACE("(lpcs=%p)\n", lpcs);
6930 /* initialize info pointer */
6931 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
6932 if (!infoPtr) return -1;
6934 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
6936 infoPtr->hwndSelf = hwnd;
6937 infoPtr->dwStyle = lpcs->style;
6938 /* determine the type of structures to use */
6939 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
6940 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
6942 /* initialize color information */
6943 infoPtr->clrBk = CLR_NONE;
6944 infoPtr->clrText = comctl32_color.clrWindowText;
6945 infoPtr->clrTextBk = CLR_DEFAULT;
6946 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
6948 /* set default values */
6949 infoPtr->nFocusedItem = -1;
6950 infoPtr->nSelectionMark = -1;
6951 infoPtr->nHotItem = -1;
6952 infoPtr->bRedraw = TRUE;
6953 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
6954 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
6955 infoPtr->nEditLabelItem = -1;
6957 /* get default font (icon title) */
6958 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
6959 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
6960 infoPtr->hFont = infoPtr->hDefaultFont;
6961 LISTVIEW_SaveTextMetrics(infoPtr);
6963 /* create header */
6964 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
6965 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
6966 0, 0, 0, 0, hwnd, (HMENU)0,
6967 lpcs->hInstance, NULL);
6969 /* set header unicode format */
6970 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
6972 /* set header font */
6973 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
6975 infoPtr->hdpaColumns = DPA_Create(10);
6977 if (uView == LVS_ICON)
6979 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
6980 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
6982 else if (uView == LVS_REPORT)
6984 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
6986 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6988 else
6990 /* set HDS_HIDDEN flag to hide the header bar */
6991 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
6992 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
6996 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6997 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6999 else
7001 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7002 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7005 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
7006 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
7008 /* display unsupported listview window styles */
7009 LISTVIEW_UnsupportedStyles(lpcs->style);
7011 /* allocate memory for the data structure */
7012 infoPtr->hdpaItems = DPA_Create(10);
7013 infoPtr->hdpaPosX = DPA_Create(10);
7014 infoPtr->hdpaPosY = DPA_Create(10);
7016 /* allocate memory for the selection ranges */
7017 infoPtr->selectionRanges = ranges_create(10);
7019 /* initialize size of items */
7020 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7021 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
7023 /* initialize the hover time to -1(indicating the default system hover time) */
7024 infoPtr->dwHoverTime = -1;
7026 return 0;
7029 /***
7030 * DESCRIPTION:
7031 * Erases the background of the listview control.
7033 * PARAMETER(S):
7034 * [I] infoPtr : valid pointer to the listview structure
7035 * [I] hdc : device context handle
7037 * RETURN:
7038 * SUCCESS : TRUE
7039 * FAILURE : FALSE
7041 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7043 RECT rc;
7045 TRACE("(hdc=%x)\n", hdc);
7047 if (!GetClipBox(hdc, &rc)) return FALSE;
7049 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7053 /***
7054 * DESCRIPTION:
7055 * Helper function for LISTVIEW_[HV]Scroll *only*.
7056 * Performs vertical/horizontal scrolling by a give amount.
7058 * PARAMETER(S):
7059 * [I] infoPtr : valid pointer to the listview structure
7060 * [I] dx : amount of horizontal scroll
7061 * [I] dy : amount of vertical scroll
7063 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7065 /* now we can scroll the list */
7066 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7067 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7068 /* if we have focus, adjust rect */
7069 OffsetRect(&infoPtr->rcFocus, dx, dy);
7070 UpdateWindow(infoPtr->hwndSelf);
7073 /***
7074 * DESCRIPTION:
7075 * Performs vertical scrolling.
7077 * PARAMETER(S):
7078 * [I] infoPtr : valid pointer to the listview structure
7079 * [I] nScrollCode : scroll code
7080 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7081 * [I] hScrollWnd : scrollbar control window handle
7083 * RETURN:
7084 * Zero
7086 * NOTES:
7087 * SB_LINEUP/SB_LINEDOWN:
7088 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7089 * for LVS_REPORT is 1 line
7090 * for LVS_LIST cannot occur
7093 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7094 INT nScrollDiff, HWND hScrollWnd)
7096 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7097 INT nOldScrollPos, nNewScrollPos;
7098 SCROLLINFO scrollInfo;
7099 BOOL is_an_icon;
7101 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7103 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7105 scrollInfo.cbSize = sizeof(SCROLLINFO);
7106 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7108 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7110 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7112 nOldScrollPos = scrollInfo.nPos;
7113 switch (nScrollCode)
7115 case SB_INTERNAL:
7116 break;
7118 case SB_LINEUP:
7119 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7120 break;
7122 case SB_LINEDOWN:
7123 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7124 break;
7126 case SB_PAGEUP:
7127 nScrollDiff = -scrollInfo.nPage;
7128 break;
7130 case SB_PAGEDOWN:
7131 nScrollDiff = scrollInfo.nPage;
7132 break;
7134 case SB_THUMBPOSITION:
7135 case SB_THUMBTRACK:
7136 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7137 break;
7139 default:
7140 nScrollDiff = 0;
7143 /* quit right away if pos isn't changing */
7144 if (nScrollDiff == 0) return 0;
7146 /* calculate new position, and handle overflows */
7147 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7148 if (nScrollDiff > 0) {
7149 if (nNewScrollPos < nOldScrollPos ||
7150 nNewScrollPos > scrollInfo.nMax)
7151 nNewScrollPos = scrollInfo.nMax;
7152 } else {
7153 if (nNewScrollPos > nOldScrollPos ||
7154 nNewScrollPos < scrollInfo.nMin)
7155 nNewScrollPos = scrollInfo.nMin;
7158 /* set the new position, and reread in case it changed */
7159 scrollInfo.fMask = SIF_POS;
7160 scrollInfo.nPos = nNewScrollPos;
7161 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7163 /* carry on only if it really changed */
7164 if (nNewScrollPos == nOldScrollPos) return 0;
7166 /* now adjust to client coordinates */
7167 nScrollDiff = nOldScrollPos - nNewScrollPos;
7168 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7170 /* and scroll the window */
7171 scroll_list(infoPtr, 0, nScrollDiff);
7173 return 0;
7176 /***
7177 * DESCRIPTION:
7178 * Performs horizontal scrolling.
7180 * PARAMETER(S):
7181 * [I] infoPtr : valid pointer to the listview structure
7182 * [I] nScrollCode : scroll code
7183 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7184 * [I] hScrollWnd : scrollbar control window handle
7186 * RETURN:
7187 * Zero
7189 * NOTES:
7190 * SB_LINELEFT/SB_LINERIGHT:
7191 * for LVS_ICON, LVS_SMALLICON 1 pixel
7192 * for LVS_REPORT is 1 pixel
7193 * for LVS_LIST is 1 column --> which is a 1 because the
7194 * scroll is based on columns not pixels
7197 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7198 INT nScrollDiff, HWND hScrollWnd)
7200 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7201 INT nOldScrollPos, nNewScrollPos;
7202 SCROLLINFO scrollInfo;
7204 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7206 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7208 scrollInfo.cbSize = sizeof(SCROLLINFO);
7209 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7211 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7213 nOldScrollPos = scrollInfo.nPos;
7215 switch (nScrollCode)
7217 case SB_INTERNAL:
7218 break;
7220 case SB_LINELEFT:
7221 nScrollDiff = -1;
7222 break;
7224 case SB_LINERIGHT:
7225 nScrollDiff = 1;
7226 break;
7228 case SB_PAGELEFT:
7229 nScrollDiff = -scrollInfo.nPage;
7230 break;
7232 case SB_PAGERIGHT:
7233 nScrollDiff = scrollInfo.nPage;
7234 break;
7236 case SB_THUMBPOSITION:
7237 case SB_THUMBTRACK:
7238 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7239 break;
7241 default:
7242 nScrollDiff = 0;
7245 /* quit right away if pos isn't changing */
7246 if (nScrollDiff == 0) return 0;
7248 /* calculate new position, and handle overflows */
7249 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7250 if (nScrollDiff > 0) {
7251 if (nNewScrollPos < nOldScrollPos ||
7252 nNewScrollPos > scrollInfo.nMax)
7253 nNewScrollPos = scrollInfo.nMax;
7254 } else {
7255 if (nNewScrollPos > nOldScrollPos ||
7256 nNewScrollPos < scrollInfo.nMin)
7257 nNewScrollPos = scrollInfo.nMin;
7260 /* set the new position, and reread in case it changed */
7261 scrollInfo.fMask = SIF_POS;
7262 scrollInfo.nPos = nNewScrollPos;
7263 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7265 /* carry on only if it really changed */
7266 if (nNewScrollPos == nOldScrollPos) return 0;
7268 if(uView == LVS_REPORT)
7269 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7271 /* now adjust to client coordinates */
7272 nScrollDiff = nOldScrollPos - nNewScrollPos;
7273 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7275 /* and scroll the window */
7276 scroll_list(infoPtr, nScrollDiff, 0);
7278 return 0;
7281 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7283 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7284 INT gcWheelDelta = 0;
7285 UINT pulScrollLines = 3;
7286 SCROLLINFO scrollInfo;
7288 TRACE("(wheelDelta=%d)\n", wheelDelta);
7290 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7291 gcWheelDelta -= wheelDelta;
7293 scrollInfo.cbSize = sizeof(SCROLLINFO);
7294 scrollInfo.fMask = SIF_POS;
7296 switch(uView)
7298 case LVS_ICON:
7299 case LVS_SMALLICON:
7301 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7302 * should be fixed in the future.
7304 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7305 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7306 scrollInfo.nPos + (gcWheelDelta < 0) ?
7307 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7308 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7309 break;
7311 case LVS_REPORT:
7312 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7314 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7316 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7317 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7318 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7321 break;
7323 case LVS_LIST:
7324 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7325 break;
7327 return 0;
7330 /***
7331 * DESCRIPTION:
7332 * ???
7334 * PARAMETER(S):
7335 * [I] infoPtr : valid pointer to the listview structure
7336 * [I] INT : virtual key
7337 * [I] LONG : key data
7339 * RETURN:
7340 * Zero
7342 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7344 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7345 INT nItem = -1;
7346 NMLVKEYDOWN nmKeyDown;
7348 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7350 /* send LVN_KEYDOWN notification */
7351 nmKeyDown.wVKey = nVirtualKey;
7352 nmKeyDown.flags = 0;
7353 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7355 switch (nVirtualKey)
7357 case VK_RETURN:
7358 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7360 notify(infoPtr, NM_RETURN);
7361 notify(infoPtr, LVN_ITEMACTIVATE);
7363 break;
7365 case VK_HOME:
7366 if (infoPtr->nItemCount > 0)
7367 nItem = 0;
7368 break;
7370 case VK_END:
7371 if (infoPtr->nItemCount > 0)
7372 nItem = infoPtr->nItemCount - 1;
7373 break;
7375 case VK_LEFT:
7376 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7377 break;
7379 case VK_UP:
7380 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7381 break;
7383 case VK_RIGHT:
7384 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7385 break;
7387 case VK_DOWN:
7388 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7389 break;
7391 case VK_PRIOR:
7392 if (uView == LVS_REPORT)
7393 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7394 else
7395 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7396 * LISTVIEW_GetCountPerRow(infoPtr);
7397 if(nItem < 0) nItem = 0;
7398 break;
7400 case VK_NEXT:
7401 if (uView == LVS_REPORT)
7402 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7403 else
7404 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7405 * LISTVIEW_GetCountPerRow(infoPtr);
7406 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7407 break;
7410 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7411 LISTVIEW_KeySelection(infoPtr, nItem);
7413 return 0;
7416 /***
7417 * DESCRIPTION:
7418 * Kills the focus.
7420 * PARAMETER(S):
7421 * [I] infoPtr : valid pointer to the listview structure
7423 * RETURN:
7424 * Zero
7426 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7428 TRACE("()\n");
7430 /* if we did not have the focus, there's nothing to do */
7431 if (!infoPtr->bFocus) return 0;
7433 /* send NM_KILLFOCUS notification */
7434 notify(infoPtr, NM_KILLFOCUS);
7436 /* if we have a focus rectagle, get rid of it */
7437 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7439 /* set window focus flag */
7440 infoPtr->bFocus = FALSE;
7442 /* invalidate the selected items before reseting focus flag */
7443 LISTVIEW_InvalidateSelectedItems(infoPtr);
7445 return 0;
7448 /***
7449 * DESCRIPTION:
7450 * Processes double click messages (left mouse button).
7452 * PARAMETER(S):
7453 * [I] infoPtr : valid pointer to the listview structure
7454 * [I] wKey : key flag
7455 * [I] pts : mouse coordinate
7457 * RETURN:
7458 * Zero
7460 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7462 LVHITTESTINFO htInfo;
7464 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7466 /* send NM_RELEASEDCAPTURE notification */
7467 notify(infoPtr, NM_RELEASEDCAPTURE);
7469 htInfo.pt.x = pts.x;
7470 htInfo.pt.y = pts.y;
7472 /* send NM_DBLCLK notification */
7473 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7474 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7476 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7477 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7479 return 0;
7482 /***
7483 * DESCRIPTION:
7484 * Processes mouse down 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_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7496 LVHITTESTINFO lvHitTestInfo;
7497 LONG lStyle = infoPtr->dwStyle;
7498 static BOOL bGroupSelect = TRUE;
7499 POINT pt = { pts.x, pts.y };
7500 INT nItem;
7502 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7504 /* send NM_RELEASEDCAPTURE notification */
7505 notify(infoPtr, NM_RELEASEDCAPTURE);
7507 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7509 /* set left button down flag */
7510 infoPtr->bLButtonDown = TRUE;
7512 lvHitTestInfo.pt.x = pts.x;
7513 lvHitTestInfo.pt.y = pts.y;
7515 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7516 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7517 infoPtr->nEditLabelItem = -1;
7518 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7520 if (lStyle & LVS_SINGLESEL)
7522 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7523 infoPtr->nEditLabelItem = nItem;
7524 else
7525 LISTVIEW_SetSelection(infoPtr, nItem);
7527 else
7529 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7531 if (bGroupSelect)
7532 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7533 else
7535 LVITEMW item;
7537 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7538 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7540 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7541 infoPtr->nSelectionMark = nItem;
7544 else if (wKey & MK_CONTROL)
7546 LVITEMW item;
7548 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7550 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7551 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7552 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7553 infoPtr->nSelectionMark = nItem;
7555 else if (wKey & MK_SHIFT)
7557 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7559 else
7561 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7562 infoPtr->nEditLabelItem = nItem;
7564 /* set selection (clears other pre-existing selections) */
7565 LISTVIEW_SetSelection(infoPtr, nItem);
7569 else
7571 /* remove all selections */
7572 LISTVIEW_DeselectAll(infoPtr);
7575 return 0;
7578 /***
7579 * DESCRIPTION:
7580 * Processes mouse up messages (left mouse button).
7582 * PARAMETER(S):
7583 * [I] infoPtr : valid pointer to the listview structure
7584 * [I] wKey : key flag
7585 * [I] pts : mouse coordinate
7587 * RETURN:
7588 * Zero
7590 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7592 LVHITTESTINFO lvHitTestInfo;
7594 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7596 if (!infoPtr->bLButtonDown) return 0;
7598 lvHitTestInfo.pt.x = pts.x;
7599 lvHitTestInfo.pt.y = pts.y;
7601 /* send NM_CLICK notification */
7602 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7603 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7605 /* set left button flag */
7606 infoPtr->bLButtonDown = FALSE;
7608 /* if we clicked on a selected item, edit the label */
7609 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7610 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7612 return 0;
7615 /***
7616 * DESCRIPTION:
7617 * Destroys the listview control (called after WM_DESTROY).
7619 * PARAMETER(S):
7620 * [I] infoPtr : valid pointer to the listview structure
7622 * RETURN:
7623 * Zero
7625 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7627 LONG lStyle = infoPtr->dwStyle;
7629 TRACE("()\n");
7631 /* delete all items */
7632 LISTVIEW_DeleteAllItems(infoPtr);
7634 /* destroy data structure */
7635 DPA_Destroy(infoPtr->hdpaItems);
7636 ranges_destroy(infoPtr->selectionRanges);
7638 /* destroy image lists */
7639 if (!(lStyle & LVS_SHAREIMAGELISTS))
7641 if (infoPtr->himlNormal)
7642 ImageList_Destroy(infoPtr->himlNormal);
7643 if (infoPtr->himlSmall)
7644 ImageList_Destroy(infoPtr->himlSmall);
7645 if (infoPtr->himlState)
7646 ImageList_Destroy(infoPtr->himlState);
7649 /* destroy font, bkgnd brush */
7650 infoPtr->hFont = 0;
7651 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7652 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7654 /* free listview info pointer*/
7655 COMCTL32_Free(infoPtr);
7657 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7658 return 0;
7661 /***
7662 * DESCRIPTION:
7663 * Handles notifications from children.
7665 * PARAMETER(S):
7666 * [I] infoPtr : valid pointer to the listview structure
7667 * [I] INT : control identifier
7668 * [I] LPNMHDR : notification information
7670 * RETURN:
7671 * Zero
7673 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7675 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7677 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7679 /* handle notification from header control */
7680 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7682 LPNMHEADERW lphnm = (LPNMHEADERW)lpnmh;
7684 if (lpnmh->code == HDN_TRACKW || lpnmh->code == HDN_TRACKA)
7686 COLUMN_INFO *lpColumnInfo;
7687 RECT rcCol;
7688 INT dx;
7690 if (!(lpColumnInfo = DPA_GetPtr(infoPtr->hdpaColumns, lphnm->iItem))) return 0;
7691 if (!(lphnm->pitem->mask & HDI_WIDTH)) return 0;
7693 /* determine how much we change since the last know position */
7694 dx = lphnm->pitem->cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7696 /* ajust the column being tracked */
7697 lpColumnInfo->rcHeader.right += dx;
7699 /* compute the rectangle for the tracked column */
7700 rcCol.left = lpColumnInfo->rcHeader.left;
7701 rcCol.top = infoPtr->rcList.top;
7702 rcCol.right = lpColumnInfo->rcHeader.right;
7703 rcCol.bottom = infoPtr->rcList.bottom;
7705 LISTVIEW_ScrollColumns(infoPtr, lphnm->iItem + 1, dx);
7706 if (uView == LVS_REPORT) LISTVIEW_InvalidateRect(infoPtr, &rcCol);
7708 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7710 /* Handle sorting by Header Column */
7711 NMLISTVIEW nmlv;
7713 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7714 nmlv.iItem = -1;
7715 nmlv.iSubItem = lphnm->iItem;
7716 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7720 return 0;
7723 /***
7724 * DESCRIPTION:
7725 * Determines the type of structure to use.
7727 * PARAMETER(S):
7728 * [I] infoPtr : valid pointer to the listview structureof the sender
7729 * [I] HWND : listview window handle
7730 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
7732 * RETURN:
7733 * Zero
7735 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7737 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7739 if (nCommand == NF_REQUERY)
7740 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7741 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7742 return 0;
7745 /***
7746 * DESCRIPTION:
7747 * Paints/Repaints the listview control.
7749 * PARAMETER(S):
7750 * [I] infoPtr : valid pointer to the listview structure
7751 * [I] HDC : device context handle
7753 * RETURN:
7754 * Zero
7756 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7758 TRACE("(hdc=%x)\n", hdc);
7760 if (hdc)
7761 LISTVIEW_Refresh(infoPtr, hdc);
7762 else
7764 PAINTSTRUCT ps;
7766 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7767 if (!hdc) return 1;
7768 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7769 LISTVIEW_Refresh(infoPtr, hdc);
7770 EndPaint(infoPtr->hwndSelf, &ps);
7773 return 0;
7776 /***
7777 * DESCRIPTION:
7778 * Processes double click messages (right mouse button).
7780 * PARAMETER(S):
7781 * [I] infoPtr : valid pointer to the listview structure
7782 * [I] wKey : key flag
7783 * [I] pts : mouse coordinate
7785 * RETURN:
7786 * Zero
7788 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7790 LVHITTESTINFO lvHitTestInfo;
7792 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7794 /* send NM_RELEASEDCAPTURE notification */
7795 notify(infoPtr, NM_RELEASEDCAPTURE);
7797 /* send NM_RDBLCLK notification */
7798 lvHitTestInfo.pt.x = pts.x;
7799 lvHitTestInfo.pt.y = pts.y;
7800 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7801 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
7803 return 0;
7806 /***
7807 * DESCRIPTION:
7808 * Processes mouse down messages (right mouse button).
7810 * PARAMETER(S):
7811 * [I] infoPtr : valid pointer to the listview structure
7812 * [I] wKey : key flag
7813 * [I] pts : mouse coordinate
7815 * RETURN:
7816 * Zero
7818 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7820 LVHITTESTINFO lvHitTestInfo;
7821 INT nItem;
7823 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7825 /* send NM_RELEASEDCAPTURE notification */
7826 notify(infoPtr, NM_RELEASEDCAPTURE);
7828 /* make sure the listview control window has the focus */
7829 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7831 /* set right button down flag */
7832 infoPtr->bRButtonDown = TRUE;
7834 /* determine the index of the selected item */
7835 lvHitTestInfo.pt.x = pts.x;
7836 lvHitTestInfo.pt.y = pts.y;
7837 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7839 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7841 LISTVIEW_SetItemFocus(infoPtr, nItem);
7842 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7843 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7844 LISTVIEW_SetSelection(infoPtr, nItem);
7846 else
7848 LISTVIEW_DeselectAll(infoPtr);
7851 return 0;
7854 /***
7855 * DESCRIPTION:
7856 * Processes mouse up messages (right mouse button).
7858 * PARAMETER(S):
7859 * [I] infoPtr : valid pointer to the listview structure
7860 * [I] wKey : key flag
7861 * [I] pts : mouse coordinate
7863 * RETURN:
7864 * Zero
7866 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7868 LVHITTESTINFO lvHitTestInfo;
7869 POINT pt;
7871 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7873 if (!infoPtr->bRButtonDown) return 0;
7875 /* set button flag */
7876 infoPtr->bRButtonDown = FALSE;
7878 /* Send NM_RClICK notification */
7879 lvHitTestInfo.pt.x = pts.x;
7880 lvHitTestInfo.pt.y = pts.y;
7881 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7882 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
7884 /* Change to screen coordinate for WM_CONTEXTMENU */
7885 pt = lvHitTestInfo.pt;
7886 ClientToScreen(infoPtr->hwndSelf, &pt);
7888 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
7889 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
7890 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
7892 return 0;
7896 /***
7897 * DESCRIPTION:
7898 * Sets the cursor.
7900 * PARAMETER(S):
7901 * [I] infoPtr : valid pointer to the listview structure
7902 * [I] hwnd : window handle of window containing the cursor
7903 * [I] nHittest : hit-test code
7904 * [I] wMouseMsg : ideintifier of the mouse message
7906 * RETURN:
7907 * TRUE if cursor is set
7908 * FALSE otherwise
7910 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
7912 LVHITTESTINFO lvHitTestInfo;
7914 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
7916 if(!infoPtr->hHotCursor) return FALSE;
7918 GetCursorPos(&lvHitTestInfo.pt);
7919 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
7921 SetCursor(infoPtr->hHotCursor);
7923 return TRUE;
7926 /***
7927 * DESCRIPTION:
7928 * Sets the focus.
7930 * PARAMETER(S):
7931 * [I] infoPtr : valid pointer to the listview structure
7932 * [I] infoPtr : handle of previously focused window
7934 * RETURN:
7935 * Zero
7937 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
7939 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
7941 /* if we have the focus already, there's nothing to do */
7942 if (infoPtr->bFocus) return 0;
7944 /* send NM_SETFOCUS notification */
7945 notify(infoPtr, NM_SETFOCUS);
7947 /* set window focus flag */
7948 infoPtr->bFocus = TRUE;
7950 /* put the focus rect back on */
7951 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
7953 /* redraw all visible selected items */
7954 LISTVIEW_InvalidateSelectedItems(infoPtr);
7956 return 0;
7959 /***
7960 * DESCRIPTION:
7961 * Sets the font.
7963 * PARAMETER(S):
7964 * [I] infoPtr : valid pointer to the listview structure
7965 * [I] HFONT : font handle
7966 * [I] WORD : redraw flag
7968 * RETURN:
7969 * Zero
7971 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
7973 HFONT oldFont = infoPtr->hFont;
7975 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
7977 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
7978 if (infoPtr->hFont == oldFont) return 0;
7980 LISTVIEW_SaveTextMetrics(infoPtr);
7982 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
7983 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
7985 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
7987 return 0;
7990 /***
7991 * DESCRIPTION:
7992 * Message handling for WM_SETREDRAW.
7993 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
7995 * PARAMETER(S):
7996 * [I] infoPtr : valid pointer to the listview structure
7997 * [I] bRedraw: state of redraw flag
7999 * RETURN:
8000 * DefWinProc return value
8002 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8004 infoPtr->bRedraw = bRedraw;
8005 if(bRedraw)
8006 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
8007 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8008 return 0;
8011 /***
8012 * DESCRIPTION:
8013 * Resizes the listview control. This function processes WM_SIZE
8014 * messages. At this time, the width and height are not used.
8016 * PARAMETER(S):
8017 * [I] infoPtr : valid pointer to the listview structure
8018 * [I] WORD : new width
8019 * [I] WORD : new height
8021 * RETURN:
8022 * Zero
8024 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8026 LONG lStyle = infoPtr->dwStyle;
8027 UINT uView = lStyle & LVS_TYPEMASK;
8029 TRACE("(width=%d, height=%d)\n", Width, Height);
8031 if (LISTVIEW_UpdateSize(infoPtr))
8033 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8035 if (lStyle & LVS_ALIGNLEFT)
8036 LISTVIEW_AlignLeft(infoPtr);
8037 else
8038 LISTVIEW_AlignTop(infoPtr);
8041 LISTVIEW_UpdateScroll(infoPtr);
8043 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8046 return 0;
8049 /***
8050 * DESCRIPTION:
8051 * Sets the size information.
8053 * PARAMETER(S):
8054 * [I] infoPtr : valid pointer to the listview structure
8056 * RETURN:
8057 * Zero if no size change
8058 * 1 of size changed
8060 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8062 LONG lStyle = infoPtr->dwStyle;
8063 UINT uView = lStyle & LVS_TYPEMASK;
8064 RECT rcList;
8065 RECT rcOld;
8067 GetClientRect(infoPtr->hwndSelf, &rcList);
8068 CopyRect(&rcOld,&(infoPtr->rcList));
8069 infoPtr->rcList.left = 0;
8070 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8071 infoPtr->rcList.top = 0;
8072 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8074 if (uView == LVS_LIST)
8076 /* Apparently the "LIST" style is supposed to have the same
8077 * number of items in a column even if there is no scroll bar.
8078 * Since if a scroll bar already exists then the bottom is already
8079 * reduced, only reduce if the scroll bar does not currently exist.
8080 * The "2" is there to mimic the native control. I think it may be
8081 * related to either padding or edges. (GLA 7/2002)
8083 if (!(lStyle & WS_HSCROLL))
8085 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8086 if (infoPtr->rcList.bottom > nHScrollHeight)
8087 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8089 else
8091 if (infoPtr->rcList.bottom > 2)
8092 infoPtr->rcList.bottom -= 2;
8095 else if (uView == LVS_REPORT)
8097 HDLAYOUT hl;
8098 WINDOWPOS wp;
8100 hl.prc = &rcList;
8101 hl.pwpos = &wp;
8102 Header_Layout(infoPtr->hwndHeader, &hl);
8104 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8106 if (!(LVS_NOCOLUMNHEADER & lStyle))
8107 infoPtr->rcList.top = max(wp.cy, 0);
8109 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8112 /***
8113 * DESCRIPTION:
8114 * Processes WM_STYLECHANGED messages.
8116 * PARAMETER(S):
8117 * [I] infoPtr : valid pointer to the listview structure
8118 * [I] WPARAM : window style type (normal or extended)
8119 * [I] LPSTYLESTRUCT : window style information
8121 * RETURN:
8122 * Zero
8124 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8125 LPSTYLESTRUCT lpss)
8127 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8128 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8129 RECT rcList = infoPtr->rcList;
8131 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8132 wStyleType, lpss->styleOld, lpss->styleNew);
8134 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8136 if (wStyleType == GWL_STYLE)
8138 infoPtr->dwStyle = lpss->styleNew;
8140 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8141 ((lpss->styleNew & WS_HSCROLL) == 0))
8142 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8144 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8145 ((lpss->styleNew & WS_VSCROLL) == 0))
8146 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8148 /* If switching modes, then start with no scroll bars and then
8149 * decide.
8151 if (uNewView != uOldView)
8153 if (uOldView == LVS_REPORT)
8154 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8156 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8157 SetRectEmpty(&infoPtr->rcFocus);
8160 if (uNewView == LVS_ICON)
8162 INT oldcx, oldcy;
8164 /* First readjust the iconSize and if necessary the iconSpacing */
8165 oldcx = infoPtr->iconSize.cx;
8166 oldcy = infoPtr->iconSize.cy;
8167 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8168 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8169 if (infoPtr->himlNormal != NULL)
8171 INT cx, cy;
8172 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
8173 infoPtr->iconSize.cx = cx;
8174 infoPtr->iconSize.cy = cy;
8176 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
8178 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8179 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8180 LISTVIEW_SetIconSpacing(infoPtr,0);
8183 /* Now update the full item width and height */
8184 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8185 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8186 if (lpss->styleNew & LVS_ALIGNLEFT)
8187 LISTVIEW_AlignLeft(infoPtr);
8188 else
8189 LISTVIEW_AlignTop(infoPtr);
8191 else if (uNewView == LVS_REPORT)
8193 HDLAYOUT hl;
8194 WINDOWPOS wp;
8196 hl.prc = &rcList;
8197 hl.pwpos = &wp;
8198 Header_Layout(infoPtr->hwndHeader, &hl);
8199 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8200 wp.flags);
8201 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8202 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8204 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8205 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8206 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8207 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8209 else if (uNewView == LVS_LIST)
8211 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8212 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8213 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8214 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8216 else
8218 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8219 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8220 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8221 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8222 if (lpss->styleNew & LVS_ALIGNLEFT)
8223 LISTVIEW_AlignLeft(infoPtr);
8224 else
8225 LISTVIEW_AlignTop(infoPtr);
8228 /* update the size of the client area */
8229 LISTVIEW_UpdateSize(infoPtr);
8231 /* add scrollbars if needed */
8232 LISTVIEW_UpdateScroll(infoPtr);
8234 /* invalidate client area + erase background */
8235 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8237 /* print the list of unsupported window styles */
8238 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8241 /* If they change the view and we have an active edit control
8242 we will need to kill the control since the redraw will
8243 misplace the edit control.
8245 if (infoPtr->hwndEdit &&
8246 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8247 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8249 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8252 return 0;
8255 /***
8256 * DESCRIPTION:
8257 * Window procedure of the listview control.
8260 static LRESULT WINAPI
8261 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8263 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8265 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8267 if (!infoPtr && (uMsg != WM_CREATE))
8268 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8270 if (infoPtr)
8272 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8275 switch (uMsg)
8277 case LVM_APPROXIMATEVIEWRECT:
8278 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8279 LOWORD(lParam), HIWORD(lParam));
8280 case LVM_ARRANGE:
8281 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8283 /* case LVN_CANCELEDITLABEL */
8285 /* case LVM_CREATEDRAGIMAGE: */
8287 case LVM_DELETEALLITEMS:
8288 return LISTVIEW_DeleteAllItems(infoPtr);
8290 case LVM_DELETECOLUMN:
8291 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8293 case LVM_DELETEITEM:
8294 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8296 case LVM_EDITLABELW:
8297 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8299 case LVM_EDITLABELA:
8300 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8302 /* case LVN_ENABLEGROUPVIEW: */
8304 case LVM_ENSUREVISIBLE:
8305 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8307 case LVM_FINDITEMW:
8308 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8310 case LVM_FINDITEMA:
8311 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8313 case LVM_GETBKCOLOR:
8314 return infoPtr->clrBk;
8316 /* case LVM_GETBKIMAGE: */
8318 case LVM_GETCALLBACKMASK:
8319 return infoPtr->uCallbackMask;
8321 case LVM_GETCOLUMNA:
8322 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8324 case LVM_GETCOLUMNW:
8325 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8327 case LVM_GETCOLUMNORDERARRAY:
8328 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8330 case LVM_GETCOLUMNWIDTH:
8331 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8333 case LVM_GETCOUNTPERPAGE:
8334 return LISTVIEW_GetCountPerPage(infoPtr);
8336 case LVM_GETEDITCONTROL:
8337 return (LRESULT)infoPtr->hwndEdit;
8339 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8340 return infoPtr->dwLvExStyle;
8342 case LVM_GETHEADER:
8343 return (LRESULT)infoPtr->hwndHeader;
8345 case LVM_GETHOTCURSOR:
8346 return (LRESULT)infoPtr->hHotCursor;
8348 case LVM_GETHOTITEM:
8349 return infoPtr->nHotItem;
8351 case LVM_GETHOVERTIME:
8352 return infoPtr->dwHoverTime;
8354 case LVM_GETIMAGELIST:
8355 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8357 /* case LVN_GETINSERTMARK: */
8359 /* case LVN_GETINSERTMARKCOLOR: */
8361 /* case LVN_GETINSERTMARKRECT: */
8363 case LVM_GETISEARCHSTRINGA:
8364 case LVM_GETISEARCHSTRINGW:
8365 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8366 return FALSE;
8368 case LVM_GETITEMA:
8369 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8371 case LVM_GETITEMW:
8372 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8374 case LVM_GETITEMCOUNT:
8375 return infoPtr->nItemCount;
8377 case LVM_GETITEMPOSITION:
8378 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8380 case LVM_GETITEMRECT:
8381 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8383 case LVM_GETITEMSPACING:
8384 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8386 case LVM_GETITEMSTATE:
8387 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8389 case LVM_GETITEMTEXTA:
8390 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8392 case LVM_GETITEMTEXTW:
8393 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8395 case LVM_GETNEXTITEM:
8396 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8398 case LVM_GETNUMBEROFWORKAREAS:
8399 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8400 return 1;
8402 case LVM_GETORIGIN:
8403 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8405 /* case LVN_GETOUTLINECOLOR: */
8407 /* case LVM_GETSELECTEDCOLUMN: */
8409 case LVM_GETSELECTEDCOUNT:
8410 return LISTVIEW_GetSelectedCount(infoPtr);
8412 case LVM_GETSELECTIONMARK:
8413 return infoPtr->nSelectionMark;
8415 case LVM_GETSTRINGWIDTHA:
8416 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8418 case LVM_GETSTRINGWIDTHW:
8419 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8421 case LVM_GETSUBITEMRECT:
8422 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8424 case LVM_GETTEXTBKCOLOR:
8425 return infoPtr->clrTextBk;
8427 case LVM_GETTEXTCOLOR:
8428 return infoPtr->clrText;
8430 /* case LVN_GETTILEINFO: */
8432 /* case LVN_GETTILEVIEWINFO: */
8434 case LVM_GETTOOLTIPS:
8435 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8436 return FALSE;
8438 case LVM_GETTOPINDEX:
8439 return LISTVIEW_GetTopIndex(infoPtr);
8441 /*case LVM_GETUNICODEFORMAT:
8442 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8443 return FALSE;*/
8445 case LVM_GETVIEWRECT:
8446 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8448 case LVM_GETWORKAREAS:
8449 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8450 return FALSE;
8452 /* case LVN_HASGROUP: */
8454 case LVM_HITTEST:
8455 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8457 case LVM_INSERTCOLUMNA:
8458 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8460 case LVM_INSERTCOLUMNW:
8461 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8463 /* case LVN_INSERTGROUP: */
8465 /* case LVN_INSERTGROUPSORTED: */
8467 case LVM_INSERTITEMA:
8468 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8470 case LVM_INSERTITEMW:
8471 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8473 /* case LVN_INSERTMARKHITTEST: */
8475 /* case LVN_ISGROUPVIEWENABLED: */
8477 /* case LVN_MAPIDTOINDEX: */
8479 /* case LVN_INEDXTOID: */
8481 /* case LVN_MOVEGROUP: */
8483 /* case LVN_MOVEITEMTOGROUP: */
8485 case LVM_REDRAWITEMS:
8486 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8488 /* case LVN_REMOVEALLGROUPS: */
8490 /* case LVN_REMOVEGROUP: */
8492 case LVM_SCROLL:
8493 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8495 case LVM_SETBKCOLOR:
8496 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8498 /* case LVM_SETBKIMAGE: */
8500 case LVM_SETCALLBACKMASK:
8501 infoPtr->uCallbackMask = (UINT)wParam;
8502 return TRUE;
8504 case LVM_SETCOLUMNA:
8505 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8507 case LVM_SETCOLUMNW:
8508 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8510 case LVM_SETCOLUMNORDERARRAY:
8511 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8513 case LVM_SETCOLUMNWIDTH:
8514 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8516 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8517 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8519 /* case LVN_SETGROUPINFO: */
8521 /* case LVN_SETGROUPMETRICS: */
8523 case LVM_SETHOTCURSOR:
8524 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8526 case LVM_SETHOTITEM:
8527 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8529 case LVM_SETHOVERTIME:
8530 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8532 case LVM_SETICONSPACING:
8533 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8535 case LVM_SETIMAGELIST:
8536 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8538 /* case LVN_SETINFOTIP: */
8540 /* case LVN_SETINSERTMARK: */
8542 /* case LVN_SETINSERTMARKCOLOR: */
8544 case LVM_SETITEMA:
8545 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8547 case LVM_SETITEMW:
8548 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8550 case LVM_SETITEMCOUNT:
8551 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8553 case LVM_SETITEMPOSITION:
8555 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8556 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8559 case LVM_SETITEMPOSITION32:
8560 if (lParam == 0) return FALSE;
8561 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8563 case LVM_SETITEMSTATE:
8564 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8566 case LVM_SETITEMTEXTA:
8567 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8569 case LVM_SETITEMTEXTW:
8570 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8572 /* case LVN_SETOUTLINECOLOR: */
8574 /* case LVN_SETSELECTEDCOLUMN: */
8576 case LVM_SETSELECTIONMARK:
8577 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8579 case LVM_SETTEXTBKCOLOR:
8580 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8582 case LVM_SETTEXTCOLOR:
8583 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8585 /* case LVN_SETTILEINFO: */
8587 /* case LVN_SETTILEVIEWINFO: */
8589 /* case LVN_SETTILEWIDTH: */
8591 /* case LVM_SETTOOLTIPS: */
8593 /* case LVM_SETUNICODEFORMAT: */
8595 /* case LVN_SETVIEW: */
8597 /* case LVM_SETWORKAREAS: */
8599 /* case LVN_SORTGROUPS: */
8601 case LVM_SORTITEMS:
8602 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8604 case LVM_SUBITEMHITTEST:
8605 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8607 case LVM_UPDATE:
8608 return LISTVIEW_Update(infoPtr, (INT)wParam);
8610 case WM_CHAR:
8611 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8613 case WM_COMMAND:
8614 return LISTVIEW_Command(infoPtr, wParam, lParam);
8616 case WM_CREATE:
8617 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8619 case WM_ERASEBKGND:
8620 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8622 case WM_GETDLGCODE:
8623 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8625 case WM_GETFONT:
8626 return infoPtr->hFont;
8628 case WM_HSCROLL:
8629 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8631 case WM_KEYDOWN:
8632 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8634 case WM_KILLFOCUS:
8635 return LISTVIEW_KillFocus(infoPtr);
8637 case WM_LBUTTONDBLCLK:
8638 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8640 case WM_LBUTTONDOWN:
8641 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8643 case WM_LBUTTONUP:
8644 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8646 case WM_MOUSEMOVE:
8647 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8649 case WM_MOUSEHOVER:
8650 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8652 case WM_NCDESTROY:
8653 return LISTVIEW_NCDestroy(infoPtr);
8655 case WM_NOTIFY:
8656 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8658 case WM_NOTIFYFORMAT:
8659 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8661 case WM_PAINT:
8662 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8664 case WM_RBUTTONDBLCLK:
8665 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8667 case WM_RBUTTONDOWN:
8668 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8670 case WM_RBUTTONUP:
8671 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8673 case WM_SETCURSOR:
8674 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8675 return TRUE;
8676 goto fwd_msg;
8678 case WM_SETFOCUS:
8679 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8681 case WM_SETFONT:
8682 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8684 case WM_SETREDRAW:
8685 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8687 case WM_SIZE:
8688 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8690 case WM_STYLECHANGED:
8691 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8693 case WM_SYSCOLORCHANGE:
8694 COMCTL32_RefreshSysColors();
8695 return 0;
8697 /* case WM_TIMER: */
8699 case WM_VSCROLL:
8700 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8702 case WM_MOUSEWHEEL:
8703 if (wParam & (MK_SHIFT | MK_CONTROL))
8704 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8705 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8707 case WM_WINDOWPOSCHANGED:
8708 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8709 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8710 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8711 LISTVIEW_UpdateSize(infoPtr);
8712 LISTVIEW_UpdateScroll(infoPtr);
8714 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8716 /* case WM_WININICHANGE: */
8718 default:
8719 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8720 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8722 fwd_msg:
8723 /* call default window procedure */
8724 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8727 return 0;
8730 /***
8731 * DESCRIPTION:
8732 * Registers the window class.
8734 * PARAMETER(S):
8735 * None
8737 * RETURN:
8738 * None
8740 void LISTVIEW_Register(void)
8742 WNDCLASSW wndClass;
8744 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8745 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8746 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8747 wndClass.cbClsExtra = 0;
8748 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8749 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8750 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8751 wndClass.lpszClassName = WC_LISTVIEWW;
8752 RegisterClassW(&wndClass);
8755 /***
8756 * DESCRIPTION:
8757 * Unregisters the window class.
8759 * PARAMETER(S):
8760 * None
8762 * RETURN:
8763 * None
8765 void LISTVIEW_Unregister(void)
8767 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8770 /***
8771 * DESCRIPTION:
8772 * Handle any WM_COMMAND messages
8774 * PARAMETER(S):
8776 * RETURN:
8778 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8780 switch (HIWORD(wParam))
8782 case EN_UPDATE:
8785 * Adjust the edit window size
8787 WCHAR buffer[1024];
8788 HDC hdc = GetDC(infoPtr->hwndEdit);
8789 HFONT hFont, hOldFont = 0;
8790 RECT rect;
8791 SIZE sz;
8792 int len;
8794 if (!infoPtr->hwndEdit || !hdc) return 0;
8795 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8796 GetWindowRect(infoPtr->hwndEdit, &rect);
8798 /* Select font to get the right dimension of the string */
8799 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8800 if(hFont != 0)
8802 hOldFont = SelectObject(hdc, hFont);
8805 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8807 TEXTMETRICW textMetric;
8809 /* Add Extra spacing for the next character */
8810 GetTextMetricsW(hdc, &textMetric);
8811 sz.cx += (textMetric.tmMaxCharWidth * 2);
8813 SetWindowPos (
8814 infoPtr->hwndEdit,
8815 HWND_TOP,
8818 sz.cx,
8819 rect.bottom - rect.top,
8820 SWP_DRAWFRAME|SWP_NOMOVE);
8822 if(hFont != 0)
8823 SelectObject(hdc, hOldFont);
8825 ReleaseDC(infoPtr->hwndSelf, hdc);
8827 break;
8830 default:
8831 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8834 return 0;
8838 /***
8839 * DESCRIPTION:
8840 * Subclassed edit control windproc function
8842 * PARAMETER(S):
8844 * RETURN:
8846 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
8847 WPARAM wParam, LPARAM lParam, BOOL isW)
8849 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8850 BOOL cancel = FALSE;
8852 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8853 hwnd, uMsg, wParam, lParam, isW);
8855 switch (uMsg)
8857 case WM_GETDLGCODE:
8858 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8860 case WM_KILLFOCUS:
8861 break;
8863 case WM_DESTROY:
8865 WNDPROC editProc = infoPtr->EditWndProc;
8866 infoPtr->EditWndProc = 0;
8867 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
8868 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
8871 case WM_KEYDOWN:
8872 if (VK_ESCAPE == (INT)wParam)
8874 cancel = TRUE;
8875 break;
8877 else if (VK_RETURN == (INT)wParam)
8878 break;
8880 default:
8881 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
8884 /* kill the edit */
8885 if (infoPtr->hwndEdit)
8887 LPWSTR buffer = NULL;
8889 infoPtr->hwndEdit = 0;
8890 if (!cancel)
8892 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
8894 if (len)
8896 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
8898 if (isW) GetWindowTextW(hwnd, buffer, len+1);
8899 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
8903 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
8905 if (buffer) COMCTL32_Free(buffer);
8909 SendMessageW(hwnd, WM_CLOSE, 0, 0);
8910 return TRUE;
8913 /***
8914 * DESCRIPTION:
8915 * Subclassed edit control windproc function
8917 * PARAMETER(S):
8919 * RETURN:
8921 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8923 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
8926 /***
8927 * DESCRIPTION:
8928 * Subclassed edit control windproc function
8930 * PARAMETER(S):
8932 * RETURN:
8934 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8936 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
8939 /***
8940 * DESCRIPTION:
8941 * Creates a subclassed edit cotrol
8943 * PARAMETER(S):
8945 * RETURN:
8947 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
8948 INT x, INT y, INT width, INT height, BOOL isW)
8950 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
8951 HWND hedit;
8952 SIZE sz;
8953 HDC hdc;
8954 HDC hOldFont=0;
8955 TEXTMETRICW textMetric;
8956 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
8958 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
8960 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
8961 hdc = GetDC(infoPtr->hwndSelf);
8963 /* Select the font to get appropriate metric dimensions */
8964 if(infoPtr->hFont != 0)
8965 hOldFont = SelectObject(hdc, infoPtr->hFont);
8967 /*Get String Lenght in pixels */
8968 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
8970 /*Add Extra spacing for the next character */
8971 GetTextMetricsW(hdc, &textMetric);
8972 sz.cx += (textMetric.tmMaxCharWidth * 2);
8974 if(infoPtr->hFont != 0)
8975 SelectObject(hdc, hOldFont);
8977 ReleaseDC(infoPtr->hwndSelf, hdc);
8978 if (isW)
8979 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8980 else
8981 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8983 if (!hedit) return 0;
8985 infoPtr->EditWndProc = (WNDPROC)
8986 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
8987 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
8989 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
8991 return hedit;