Column zero has the image enabled always, irrespective of format.
[wine/multimedia.git] / dlls / comctl32 / listview.c
blobf511873f507a9711419f5bcdd72d70abf96eb4fa
1 /*
2 * Listview control
4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 Codeweavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * NOTES
25 * Listview control implementation.
27 * TODO:
28 * -- Hot item handling.
29 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
30 * -- Support CustonDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs)
32 * Notifications:
33 * LISTVIEW_Notify : most notifications from children (editbox and header)
35 * Data structure:
36 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
38 * Advanced functionality:
39 * LISTVIEW_GetNumberOfWorkAreas : not implemented
40 * LISTVIEW_GetISearchString : not implemented
41 * LISTVIEW_GetBkImage : not implemented
42 * LISTVIEW_SetBkImage : not implemented
43 * LISTVIEW_GetColumnOrderArray : simple hack only
44 * LISTVIEW_SetColumnOrderArray : simple hack only
45 * LISTVIEW_Arrange : empty stub
46 * LISTVIEW_ApproximateViewRect : incomplete
47 * LISTVIEW_Update : not completed
49 * Known differences in message stream from native control (not known if
50 * these differences cause problems):
51 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
52 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
53 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
54 * processing for "USEDOUBLECLICKTIME".
58 #include "config.h"
59 #include "wine/port.h"
61 #include <assert.h>
62 #include <ctype.h>
63 #include <string.h>
64 #include <stdlib.h>
65 #include <stdio.h>
67 #include "winbase.h"
68 #include "winnt.h"
69 #include "heap.h"
70 #include "commctrl.h"
71 #include "comctl32.h"
72 #include "wine/debug.h"
74 WINE_DEFAULT_DEBUG_CHANNEL(listview);
76 /* make sure you set this to 0 for production use! */
77 #define DEBUG_RANGES 1
79 typedef struct tagCOLUMN_INFO
81 RECT rcHeader; /* tracks the header's rectangle */
82 UINT align; /* one of DT_{LEFT,CENTER,RIGHT} */
83 BOOL hasImage; /* on/off switch for column images */
84 } COLUMN_INFO;
86 typedef struct tagITEMHDR
88 LPWSTR pszText;
89 INT iImage;
90 } ITEMHDR, *LPITEMHDR;
92 typedef struct tagLISTVIEW_SUBITEM
94 ITEMHDR hdr;
95 INT iSubItem;
96 } LISTVIEW_SUBITEM;
98 typedef struct tagLISTVIEW_ITEM
100 ITEMHDR hdr;
101 UINT state;
102 LPARAM lParam;
103 INT iIndent;
104 } LISTVIEW_ITEM;
106 typedef struct tagRANGE
108 INT lower;
109 INT upper;
110 } RANGE;
112 typedef struct tagRANGES
114 HDPA hdpa;
115 } *RANGES;
117 typedef struct tagITERATOR
119 INT nItem;
120 INT nSpecial;
121 RANGE range;
122 RANGES ranges;
123 INT index;
124 } ITERATOR;
126 typedef struct tagLISTVIEW_INFO
128 HWND hwndSelf;
129 HBRUSH hBkBrush;
130 COLORREF clrBk;
131 COLORREF clrText;
132 COLORREF clrTextBk;
133 COLORREF clrTextBkDefault;
134 HIMAGELIST himlNormal;
135 HIMAGELIST himlSmall;
136 HIMAGELIST himlState;
137 BOOL bLButtonDown;
138 BOOL bRButtonDown;
139 INT nItemHeight;
140 INT nItemWidth;
141 RANGES selectionRanges;
142 INT nSelectionMark;
143 INT nHotItem;
144 SHORT notifyFormat;
145 RECT rcList; /* This rectangle is really the window
146 * client rectangle possibly reduced by the
147 * horizontal scroll bar and/or header - see
148 * LISTVIEW_UpdateSize. This rectangle offset
149 * by the LISTVIEW_GetOrigin value is in
150 * client coordinates */
151 RECT rcView; /* This rectangle contains all items -
152 * contructed in LISTVIEW_AlignTop and
153 * LISTVIEW_AlignLeft */
154 SIZE iconSize;
155 SIZE iconSpacing;
156 SIZE iconStateSize;
157 UINT uCallbackMask;
158 HWND hwndHeader;
159 HFONT hDefaultFont;
160 HCURSOR hHotCursor;
161 HFONT hFont;
162 INT ntmHeight; /* from GetTextMetrics from above font */
163 BOOL bRedraw;
164 BOOL bFocus;
165 INT nFocusedItem;
166 RECT rcFocus;
167 DWORD dwStyle; /* the cached window GWL_STYLE */
168 DWORD dwLvExStyle; /* extended listview style */
169 INT nItemCount; /* the number of items in the list */
170 HDPA hdpaItems; /* array ITEM_INFO pointers */
171 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
172 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
173 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
174 PFNLVCOMPARE pfnCompare;
175 LPARAM lParamSort;
176 HWND hwndEdit;
177 WNDPROC EditWndProc;
178 INT nEditLabelItem;
179 DWORD dwHoverTime;
181 DWORD lastKeyPressTimestamp;
182 WPARAM charCode;
183 INT nSearchParamLength;
184 WCHAR szSearchParam[ MAX_PATH ];
185 BOOL bIsDrawing;
186 } LISTVIEW_INFO;
189 * constants
191 /* How many we debug buffer to allocate */
192 #define DEBUG_BUFFERS 20
193 /* The size of a single debug bbuffer */
194 #define DEBUG_BUFFER_SIZE 256
196 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
197 #define SB_INTERNAL -1
199 /* maximum size of a label */
200 #define DISP_TEXT_SIZE 512
202 /* padding for items in list and small icon display modes */
203 #define WIDTH_PADDING 12
205 /* padding for items in list, report and small icon display modes */
206 #define HEIGHT_PADDING 1
208 /* offset of items in report display mode */
209 #define REPORT_MARGINX 2
211 /* padding for icon in large icon display mode
212 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
213 * that HITTEST will see.
214 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
215 * ICON_TOP_PADDING - sum of the two above.
216 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
217 * LABEL_VERT_PADDING - between bottom of text and end of box
219 * ICON_LR_PADDING - additional width above icon size.
220 * ICON_LR_HALF - half of the above value
222 #define ICON_TOP_PADDING_NOTHITABLE 2
223 #define ICON_TOP_PADDING_HITABLE 2
224 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
225 #define ICON_BOTTOM_PADDING 4
226 #define LABEL_VERT_PADDING 7
227 #define ICON_LR_PADDING 16
228 #define ICON_LR_HALF (ICON_LR_PADDING/2)
230 /* default label width for items in list and small icon display modes */
231 #define DEFAULT_LABEL_WIDTH 40
233 /* default column width for items in list display mode */
234 #define DEFAULT_COLUMN_WIDTH 128
236 /* Size of "line" scroll for V & H scrolls */
237 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
239 /* Padding betwen image and label */
240 #define IMAGE_PADDING 2
242 /* Padding behind the label */
243 #define TRAILING_PADDING 5
245 /* Border for the icon caption */
246 #define CAPTION_BORDER 2
248 /* Standard DrawText flags */
249 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
250 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
251 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
253 /* The time in milisecods to reset the search in the list */
254 #define KEY_DELAY 450
256 /* Dump the LISTVIEW_INFO structure to the debug channel */
257 #define LISTVIEW_DUMP(iP) do { \
258 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
259 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
260 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
261 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
262 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
263 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
264 (iP->bFocus) ? "true" : "false"); \
265 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
266 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
267 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
268 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
269 iP->hwndSelf, \
270 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
271 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
272 } while(0)
276 * forward declarations
278 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
279 static BOOL LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
280 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
281 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
282 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
283 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *);
284 static BOOL LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
285 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
286 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
287 static INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *);
288 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
289 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *, INT);
290 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
291 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
292 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
293 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
294 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, POINT);
295 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
296 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
297 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
298 static void LISTVIEW_UnsupportedStyles(LONG);
299 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
300 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
301 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
302 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
303 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
304 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
305 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
306 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
307 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
308 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
309 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
310 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
311 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
313 /******** Text handling functions *************************************/
315 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
316 * text string. The string may be ANSI or Unicode, in which case
317 * the boolean isW tells us the type of the string.
319 * The name of the function tell what type of strings it expects:
320 * W: Unicode, T: ANSI/Unicode - function of isW
323 static inline BOOL is_textW(LPCWSTR text)
325 return text != NULL && text != LPSTR_TEXTCALLBACKW;
328 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
330 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
331 return is_textW(text);
334 static inline int textlenT(LPCWSTR text, BOOL isW)
336 return !is_textT(text, isW) ? 0 :
337 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
340 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
342 if (isDestW)
343 if (isSrcW) lstrcpynW(dest, src, max);
344 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
345 else
346 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
347 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
350 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
352 LPWSTR wstr = (LPWSTR)text;
354 if (!isW && is_textT(text, isW))
356 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
357 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
358 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
360 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
361 return wstr;
364 static inline void textfreeT(LPWSTR wstr, BOOL isW)
366 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
370 * dest is a pointer to a Unicode string
371 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
373 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
375 BOOL bResult = TRUE;
377 if (src == LPSTR_TEXTCALLBACKW)
379 if (is_textW(*dest)) COMCTL32_Free(*dest);
380 *dest = LPSTR_TEXTCALLBACKW;
382 else
384 LPWSTR pszText = textdupTtoW(src, isW);
385 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
386 bResult = Str_SetPtrW(dest, pszText);
387 textfreeT(pszText, isW);
389 return bResult;
393 * compares a Unicode to a Unicode/ANSI text string
395 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
397 if (!aw) return bt ? -1 : 0;
398 if (!bt) return aw ? 1 : 0;
399 if (aw == LPSTR_TEXTCALLBACKW)
400 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
401 if (bt != LPSTR_TEXTCALLBACKW)
403 LPWSTR bw = textdupTtoW(bt, isW);
404 int r = bw ? lstrcmpW(aw, bw) : 1;
405 textfreeT(bw, isW);
406 return r;
409 return 1;
412 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
414 int res;
416 n = min(min(n, strlenW(s1)), strlenW(s2));
417 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
418 return res ? res - sizeof(WCHAR) : res;
421 /******** Debugging functions *****************************************/
423 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
425 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
426 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
429 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
431 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
432 n = min(textlenT(text, isW), n);
433 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
436 static char* debug_getbuf()
438 static int index = 0;
439 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
440 return buffers[index++ % DEBUG_BUFFERS];
443 static inline char* debugrange(const RANGE* lprng)
445 if (lprng)
447 char* buf = debug_getbuf();
448 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
449 return buf;
450 } else return "(null)";
453 static inline char* debugpoint(const POINT* lppt)
455 if (lppt)
457 char* buf = debug_getbuf();
458 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
459 return buf;
460 } else return "(null)";
463 static inline char* debugrect(const RECT* rect)
465 if (rect)
467 char* buf = debug_getbuf();
468 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
469 rect->left, rect->top, rect->right, rect->bottom);
470 return buf;
471 } else return "(null)";
474 static char* debugnmlistview(LPNMLISTVIEW plvnm)
476 if (plvnm)
478 char* buf = debug_getbuf();
479 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
480 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
481 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
482 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
483 return buf;
484 } else return "(null)";
487 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
489 char* buf = debug_getbuf(), *text = buf;
490 int len, size = DEBUG_BUFFER_SIZE;
492 if (lpLVItem == NULL) return "(null)";
493 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
494 if (len == -1) goto end; buf += len; size -= len;
495 if (lpLVItem->mask & LVIF_STATE)
496 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
497 else len = 0;
498 if (len == -1) goto end; buf += len; size -= len;
499 if (lpLVItem->mask & LVIF_TEXT)
500 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
501 else len = 0;
502 if (len == -1) goto end; buf += len; size -= len;
503 if (lpLVItem->mask & LVIF_IMAGE)
504 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
505 else len = 0;
506 if (len == -1) goto end; buf += len; size -= len;
507 if (lpLVItem->mask & LVIF_PARAM)
508 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
509 else len = 0;
510 if (len == -1) goto end; buf += len; size -= len;
511 if (lpLVItem->mask & LVIF_INDENT)
512 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
513 else len = 0;
514 if (len == -1) goto end; buf += len; size -= len;
515 goto undo;
516 end:
517 buf = text + strlen(text);
518 undo:
519 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
520 return text;
523 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
525 char* buf = debug_getbuf(), *text = buf;
526 int len, size = DEBUG_BUFFER_SIZE;
528 if (lpColumn == NULL) return "(null)";
529 len = snprintf(buf, size, "{");
530 if (len == -1) goto end; buf += len; size -= len;
531 if (lpColumn->mask & LVCF_SUBITEM)
532 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
533 else len = 0;
534 if (len == -1) goto end; buf += len; size -= len;
535 if (lpColumn->mask & LVCF_FMT)
536 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
537 else len = 0;
538 if (len == -1) goto end; buf += len; size -= len;
539 if (lpColumn->mask & LVCF_WIDTH)
540 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
541 else len = 0;
542 if (len == -1) goto end; buf += len; size -= len;
543 if (lpColumn->mask & LVCF_TEXT)
544 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
545 else len = 0;
546 if (len == -1) goto end; buf += len; size -= len;
547 if (lpColumn->mask & LVCF_IMAGE)
548 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
549 else len = 0;
550 if (len == -1) goto end; buf += len; size -= len;
551 if (lpColumn->mask & LVCF_ORDER)
552 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
553 else len = 0;
554 if (len == -1) goto end; buf += len; size -= len;
555 goto undo;
556 end:
557 buf = text + strlen(text);
558 undo:
559 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
560 return text;
564 /******** Notification functions i************************************/
566 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
568 LRESULT result;
570 TRACE("(code=%d)\n", code);
572 pnmh->hwndFrom = infoPtr->hwndSelf;
573 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
574 pnmh->code = code;
575 result = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
576 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
578 TRACE(" <= %ld\n", result);
580 return result;
583 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
585 NMHDR nmh;
586 return notify_hdr(infoPtr, code, &nmh);
589 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
591 notify(infoPtr, LVN_ITEMACTIVATE);
594 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
596 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
597 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
600 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
602 NMLISTVIEW nmlv;
604 ZeroMemory(&nmlv, sizeof(nmlv));
605 nmlv.iItem = lvht->iItem;
606 nmlv.iSubItem = lvht->iSubItem;
607 nmlv.ptAction = lvht->pt;
608 return notify_listview(infoPtr, code, &nmlv);
611 static int get_ansi_notification(INT unicodeNotificationCode)
613 switch (unicodeNotificationCode)
615 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
616 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
617 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
618 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
619 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
620 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
622 ERR("unknown notification %x\n", unicodeNotificationCode);
623 assert(FALSE);
627 Send notification. depends on dispinfoW having same
628 structure as dispinfoA.
629 infoPtr : listview struct
630 notificationCode : *Unicode* notification code
631 pdi : dispinfo structure (can be unicode or ansi)
632 isW : TRUE if dispinfo is Unicode
634 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
636 BOOL bResult = FALSE;
637 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
638 INT realNotifCode;
639 INT cchTempBufMax = 0, savCchTextMax = 0;
640 LPWSTR pszTempBuf = NULL, savPszText = NULL;
642 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
644 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
645 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
648 if (convertToAnsi || convertToUnicode)
650 if (notificationCode != LVN_GETDISPINFOW)
652 cchTempBufMax = convertToUnicode ?
653 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
654 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
656 else
658 cchTempBufMax = pdi->item.cchTextMax;
659 *pdi->item.pszText = 0; /* make sure we don't process garbage */
662 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
663 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
664 if (!pszTempBuf) return FALSE;
665 if (convertToUnicode)
666 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
667 pszTempBuf, cchTempBufMax);
668 else
669 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
670 cchTempBufMax, NULL, NULL);
671 savCchTextMax = pdi->item.cchTextMax;
672 savPszText = pdi->item.pszText;
673 pdi->item.pszText = pszTempBuf;
674 pdi->item.cchTextMax = cchTempBufMax;
677 if (infoPtr->notifyFormat == NFR_ANSI)
678 realNotifCode = get_ansi_notification(notificationCode);
679 else
680 realNotifCode = notificationCode;
681 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
682 bResult = notify_hdr(infoPtr, realNotifCode, (LPNMHDR)pdi);
684 if (convertToUnicode || convertToAnsi)
686 if (convertToUnicode) /* note : pointer can be changed by app ! */
687 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
688 savCchTextMax, NULL, NULL);
689 else
690 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
691 savPszText, savCchTextMax);
692 pdi->item.pszText = savPszText; /* restores our buffer */
693 pdi->item.cchTextMax = savCchTextMax;
694 HeapFree(GetProcessHeap(), 0, pszTempBuf);
696 return bResult;
699 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc, LPRECT rcBounds)
701 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
702 lpnmlvcd->nmcd.hdc = hdc;
703 lpnmlvcd->nmcd.rc = *rcBounds;
704 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
705 lpnmlvcd->clrText = infoPtr->clrText;
708 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
710 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
711 return notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
714 /******** Item iterator functions **********************************/
716 static RANGES ranges_create(int count);
717 static void ranges_destroy(RANGES ranges);
718 static BOOL ranges_add(RANGES ranges, RANGE range);
719 static BOOL ranges_del(RANGES ranges, RANGE range);
720 static void ranges_dump(RANGES ranges);
722 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
724 RANGE range = { nItem, nItem + 1 };
726 return ranges_add(ranges, range);
729 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
731 RANGE range = { nItem, nItem + 1 };
733 return ranges_del(ranges, range);
736 /***
737 * ITERATOR DOCUMENTATION
739 * The iterator functions allow for easy, and convenient iteration
740 * over items of iterest in the list. Typically, you create a
741 * iterator, use it, and destroy it, as such:
742 * ITERATOR i;
744 * iterator_xxxitems(&i, ...);
745 * while (iterator_{prev,next}(&i)
747 * //code which uses i.nItem
749 * iterator_destroy(&i);
751 * where xxx is either: framed, or visible.
752 * Note that it is important that the code destroys the iterator
753 * after it's done with it, as the creation of the iterator may
754 * allocate memory, which thus needs to be freed.
756 * You can iterate both forwards, and backwards through the list,
757 * by using iterator_next or iterator_prev respectively.
759 * Lower numbered items are draw on top of higher number items in
760 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
761 * items may overlap). So, to test items, you should use
762 * iterator_next
763 * which lists the items top to bottom (in Z-order).
764 * For drawing items, you should use
765 * iterator_prev
766 * which lists the items bottom to top (in Z-order).
767 * If you keep iterating over the items after the end-of-items
768 * marker (-1) is returned, the iterator will start from the
769 * beginning. Typically, you don't need to test for -1,
770 * because iterator_{next,prev} will return TRUE if more items
771 * are to be iterated over, or FALSE otherwise.
773 * Note: the iterator is defined to be bidirectional. That is,
774 * any number of prev followed by any number of next, or
775 * five versa, should leave the iterator at the same item:
776 * prev * n, next * n = next * n, prev * n
778 * The iterator has a notion of a out-of-order, special item,
779 * which sits at the start of the list. This is used in
780 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
781 * which needs to be first, as it may overlap other items.
783 * The code is a bit messy because we have:
784 * - a special item to deal with
785 * - simple range, or composite range
786 * - empty range.
787 * If find bugs, or want to add features, please make sure you
788 * always check/modify *both* iterator_prev, and iterator_next.
791 /****
792 * This function iterates through the items in increasing order,
793 * but prefixed by the special item, then -1. That is:
794 * special, 1, 2, 3, ..., n, -1.
795 * Each item is listed only once.
797 static inline BOOL iterator_next(ITERATOR* i)
799 if (i->nItem == -1)
801 i->nItem = i->nSpecial;
802 if (i->nItem != -1) return TRUE;
804 if (i->nItem == i->nSpecial)
806 if (i->ranges) i->index = 0;
807 goto pickarange;
810 i->nItem++;
811 testitem:
812 if (i->nItem == i->nSpecial) i->nItem++;
813 if (i->nItem < i->range.upper) return TRUE;
815 pickarange:
816 if (i->ranges)
818 if (i->index < i->ranges->hdpa->nItemCount)
819 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
820 else goto end;
822 else if (i->nItem >= i->range.upper) goto end;
824 i->nItem = i->range.lower;
825 if (i->nItem >= 0) goto testitem;
826 end:
827 i->nItem = -1;
828 return FALSE;
831 /****
832 * This function iterates through the items in decreasing order,
833 * followed by the special item, then -1. That is:
834 * n, n-1, ..., 3, 2, 1, special, -1.
835 * Each item is listed only once.
837 static inline BOOL iterator_prev(ITERATOR* i)
839 BOOL start = FALSE;
841 if (i->nItem == -1)
843 start = TRUE;
844 if (i->ranges) i->index = i->ranges->hdpa->nItemCount;
845 goto pickarange;
847 if (i->nItem == i->nSpecial)
849 i->nItem = -1;
850 return FALSE;
853 testitem:
854 i->nItem--;
855 if (i->nItem == i->nSpecial) i->nItem--;
856 if (i->nItem >= i->range.lower) return TRUE;
858 pickarange:
859 if (i->ranges)
861 if (i->index > 0)
862 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
863 else goto end;
865 else if (!start && i->nItem < i->range.lower) goto end;
867 i->nItem = i->range.upper;
868 if (i->nItem > 0) goto testitem;
869 end:
870 return (i->nItem = i->nSpecial) != -1;
873 static RANGE iterator_range(ITERATOR* i)
875 RANGE range;
877 if (!i->ranges) return i->range;
879 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
880 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->ranges->hdpa->nItemCount - 1)).upper;
881 return range;
884 /***
885 * Releases resources associated with this ierator.
887 static inline void iterator_destroy(ITERATOR* i)
889 if (i->ranges) ranges_destroy(i->ranges);
892 /***
893 * Create an empty iterator.
895 static inline BOOL iterator_empty(ITERATOR* i)
897 ZeroMemory(i, sizeof(*i));
898 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
899 return TRUE;
903 /***
904 * Create an iterator over a bunch of ranges.
905 * Please note that the iterator will take ownership of the ranges,
906 * and will free them upon destruction.
908 static inline BOOL iterator_ranges(ITERATOR* i, RANGES ranges)
910 iterator_empty(i);
911 i->ranges = ranges;
912 return TRUE;
915 /***
916 * Creates an iterator over the items which intersect lprc.
918 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
920 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
921 RECT frame = *lprc, rcItem, rcTemp;
922 POINT Origin;
924 /* in case we fail, we want to return an empty iterator */
925 if (!iterator_empty(i)) return FALSE;
927 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
929 TRACE("(lprc=%s)\n", debugrect(lprc));
930 OffsetRect(&frame, -Origin.x, -Origin.y);
932 if (uView == LVS_ICON || uView == LVS_SMALLICON)
934 INT nItem;
936 if (uView == LVS_ICON)
938 if (LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem) && IntersectRect(&rcTemp, &rcItem, lprc))
939 i->nSpecial = infoPtr->nFocusedItem;
941 if (!(i->ranges = ranges_create(50))) return FALSE;
942 /* to do better here, we need to have PosX, and PosY sorted */
943 TRACE("building icon ranges:\n");
944 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
946 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
947 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
948 rcItem.right = rcItem.left + infoPtr->nItemWidth;
949 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
950 if (IntersectRect(&rcTemp, &rcItem, &frame))
951 ranges_additem(i->ranges, nItem);
953 if (TRACE_ON(listview))
955 TRACE(" icon items:\n");
956 ranges_dump(i->ranges);
958 return TRUE;
960 else if (uView == LVS_REPORT)
962 INT lower, upper;
964 if (frame.left >= infoPtr->nItemWidth) return TRUE;
965 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
967 lower = max(frame.top / infoPtr->nItemHeight, 0);
968 upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1);
969 if (upper < lower) return TRUE;
970 i->range.lower = lower;
971 i->range.upper = upper + 1;
972 TRACE(" report=%s\n", debugrange(&i->range));
974 else
976 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
977 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
978 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
979 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
980 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
981 INT lower = nFirstCol * nPerCol + nFirstRow;
982 RANGE item_range;
983 INT nCol;
985 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
986 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
988 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
990 if (!(i->ranges = ranges_create(nLastCol - nFirstCol + 1))) return FALSE;
991 TRACE("building list ranges:\n");
992 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
994 item_range.lower = nCol * nPerCol + nFirstRow;
995 if(item_range.lower >= infoPtr->nItemCount) break;
996 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
997 TRACE(" list=%s\n", debugrange(&item_range));
998 ranges_add(i->ranges, item_range);
1002 return TRUE;
1005 /***
1006 * Creates an iterator over the items which intersect the visible region of hdc.
1008 static BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
1010 POINT Origin, Position;
1011 RECT rcItem, rcClip;
1012 INT rgntype;
1014 rgntype = GetClipBox(hdc, &rcClip);
1015 if (rgntype == NULLREGION) return iterator_empty(i);
1016 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1017 if (rgntype == SIMPLEREGION) return TRUE;
1019 /* first deal with the special item */
1020 if (LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem) && !RectVisible(hdc, &rcItem))
1021 i->nSpecial = -1;
1023 /* if we can't deal with the region, we'll just go with the simple range */
1024 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return TRUE;
1025 TRACE("building visible range:\n");
1026 if (!i->ranges)
1028 if (!(i->ranges = ranges_create(50))) return TRUE;
1029 if (!ranges_add(i->ranges, i->range))
1031 ranges_destroy(i->ranges);
1032 i->ranges = 0;
1033 return TRUE;
1037 /* now delete the invisible items from the list */
1038 while(iterator_next(i))
1040 if (!LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position)) continue;
1041 rcItem.left = Position.x + Origin.x;
1042 rcItem.top = Position.y + Origin.y;
1043 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1044 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1045 if (!RectVisible(hdc, &rcItem))
1046 ranges_delitem(i->ranges, i->nItem);
1048 /* the iterator should restart on the next iterator_next */
1049 TRACE("done\n");
1051 return TRUE;
1054 /******** Misc helper functions ************************************/
1056 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1057 WPARAM wParam, LPARAM lParam, BOOL isW)
1059 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1060 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1063 /******** Internal API functions ************************************/
1065 /* The Invalidate* are macros, so we preserve the caller location */
1066 #define LISTVIEW_InvalidateRect(infoPtr, rect) while(infoPtr->bRedraw) { \
1067 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
1068 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
1069 break;\
1072 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
1073 RECT rcBox; \
1074 if(LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox)) \
1075 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
1076 } while (0)
1078 #define LISTVIEW_InvalidateSubItem(infoPtr, nItem, nSubItem) do { \
1079 POINT Origin, Position; \
1080 RECT rcBox; \
1081 if (LISTVIEW_GetOrigin(infoPtr, &Origin) && \
1082 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position) && \
1083 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox)) { \
1084 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y); \
1085 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
1087 } while (0)
1089 #define LISTVIEW_InvalidateList(infoPtr)\
1090 LISTVIEW_InvalidateRect(infoPtr, NULL)
1093 static inline BOOL LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1095 COLUMN_INFO *columnInfo;
1097 columnInfo = (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1098 if (!columnInfo) return FALSE;
1099 *lprc = columnInfo->rcHeader;
1100 return TRUE;
1103 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1105 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1108 /***
1109 * DESCRIPTION:
1110 * Retrieves the number of items that can fit vertically in the client area.
1112 * PARAMETER(S):
1113 * [I] infoPtr : valid pointer to the listview structure
1115 * RETURN:
1116 * Number of items per row.
1118 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1120 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1122 return max(nListWidth/infoPtr->nItemWidth, 1);
1125 /***
1126 * DESCRIPTION:
1127 * Retrieves the number of items that can fit horizontally in the client
1128 * area.
1130 * PARAMETER(S):
1131 * [I] infoPtr : valid pointer to the listview structure
1133 * RETURN:
1134 * Number of items per column.
1136 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1138 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1140 return max(nListHeight / infoPtr->nItemHeight, 1);
1144 /*************************************************************************
1145 * LISTVIEW_ProcessLetterKeys
1147 * Processes keyboard messages generated by pressing the letter keys
1148 * on the keyboard.
1149 * What this does is perform a case insensitive search from the
1150 * current position with the following quirks:
1151 * - If two chars or more are pressed in quick succession we search
1152 * for the corresponding string (e.g. 'abc').
1153 * - If there is a delay we wipe away the current search string and
1154 * restart with just that char.
1155 * - If the user keeps pressing the same character, whether slowly or
1156 * fast, so that the search string is entirely composed of this
1157 * character ('aaaaa' for instance), then we search for first item
1158 * that starting with that character.
1159 * - If the user types the above character in quick succession, then
1160 * we must also search for the corresponding string ('aaaaa'), and
1161 * go to that string if there is a match.
1163 * PARAMETERS
1164 * [I] hwnd : handle to the window
1165 * [I] charCode : the character code, the actual character
1166 * [I] keyData : key data
1168 * RETURNS
1170 * Zero.
1172 * BUGS
1174 * - The current implementation has a list of characters it will
1175 * accept and it ignores averything else. In particular it will
1176 * ignore accentuated characters which seems to match what
1177 * Windows does. But I'm not sure it makes sense to follow
1178 * Windows there.
1179 * - We don't sound a beep when the search fails.
1181 * SEE ALSO
1183 * TREEVIEW_ProcessLetterKeys
1185 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1187 INT nItem;
1188 INT endidx,idx;
1189 LVITEMW item;
1190 WCHAR buffer[MAX_PATH];
1191 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1193 /* simple parameter checking */
1194 if (!charCode || !keyData) return 0;
1196 /* only allow the valid WM_CHARs through */
1197 if (!isalnum(charCode) &&
1198 charCode != '.' && charCode != '`' && charCode != '!' &&
1199 charCode != '@' && charCode != '#' && charCode != '$' &&
1200 charCode != '%' && charCode != '^' && charCode != '&' &&
1201 charCode != '*' && charCode != '(' && charCode != ')' &&
1202 charCode != '-' && charCode != '_' && charCode != '+' &&
1203 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1204 charCode != '}' && charCode != '[' && charCode != '{' &&
1205 charCode != '/' && charCode != '?' && charCode != '>' &&
1206 charCode != '<' && charCode != ',' && charCode != '~')
1207 return 0;
1209 /* if there's one item or less, there is no where to go */
1210 if (infoPtr->nItemCount <= 1) return 0;
1212 /* update the search parameters */
1213 infoPtr->lastKeyPressTimestamp = GetTickCount();
1214 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1215 if (infoPtr->nSearchParamLength < MAX_PATH)
1216 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1217 if (infoPtr->charCode != charCode)
1218 infoPtr->charCode = charCode = 0;
1219 } else {
1220 infoPtr->charCode=charCode;
1221 infoPtr->szSearchParam[0]=charCode;
1222 infoPtr->nSearchParamLength=1;
1223 /* Redundant with the 1 char string */
1224 charCode=0;
1227 /* and search from the current position */
1228 nItem=-1;
1229 if (infoPtr->nFocusedItem >= 0) {
1230 endidx=infoPtr->nFocusedItem;
1231 idx=endidx;
1232 /* if looking for single character match,
1233 * then we must always move forward
1235 if (infoPtr->nSearchParamLength == 1)
1236 idx++;
1237 } else {
1238 endidx=infoPtr->nItemCount;
1239 idx=0;
1241 do {
1242 if (idx == infoPtr->nItemCount) {
1243 if (endidx == infoPtr->nItemCount || endidx == 0)
1244 break;
1245 idx=0;
1248 /* get item */
1249 item.mask = LVIF_TEXT;
1250 item.iItem = idx;
1251 item.iSubItem = 0;
1252 item.pszText = buffer;
1253 item.cchTextMax = MAX_PATH;
1254 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1256 /* check for a match */
1257 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1258 nItem=idx;
1259 break;
1260 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1261 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1262 /* This would work but we must keep looking for a longer match */
1263 nItem=idx;
1265 idx++;
1266 } while (idx != endidx);
1268 if (nItem != -1)
1269 LISTVIEW_KeySelection(infoPtr, nItem);
1271 return 0;
1274 /*************************************************************************
1275 * LISTVIEW_UpdateHeaderSize [Internal]
1277 * Function to resize the header control
1279 * PARAMS
1280 * [I] hwnd : handle to a window
1281 * [I] nNewScrollPos : scroll pos to set
1283 * RETURNS
1284 * None.
1286 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1288 RECT winRect;
1289 POINT point[2];
1291 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1293 GetWindowRect(infoPtr->hwndHeader, &winRect);
1294 point[0].x = winRect.left;
1295 point[0].y = winRect.top;
1296 point[1].x = winRect.right;
1297 point[1].y = winRect.bottom;
1299 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1300 point[0].x = -nNewScrollPos;
1301 point[1].x += nNewScrollPos;
1303 SetWindowPos(infoPtr->hwndHeader,0,
1304 point[0].x,point[0].y,point[1].x,point[1].y,
1305 SWP_NOZORDER | SWP_NOACTIVATE);
1308 /***
1309 * DESCRIPTION:
1310 * Update the scrollbars. This functions should be called whenever
1311 * the content, size or view changes.
1313 * PARAMETER(S):
1314 * [I] infoPtr : valid pointer to the listview structure
1316 * RETURN:
1317 * None
1319 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1321 LONG lStyle = infoPtr->dwStyle;
1322 UINT uView = lStyle & LVS_TYPEMASK;
1323 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1324 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1325 SCROLLINFO scrollInfo;
1327 if (lStyle & LVS_NOSCROLL) return;
1329 scrollInfo.cbSize = sizeof(SCROLLINFO);
1331 if (uView == LVS_LIST)
1333 /* update horizontal scrollbar */
1334 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1335 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1337 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1338 infoPtr->nItemCount, nCountPerColumn, nCountPerRow);
1340 scrollInfo.nMin = 0;
1341 scrollInfo.nMax = infoPtr->nItemCount / nCountPerColumn;
1342 if((infoPtr->nItemCount % nCountPerColumn) == 0)
1343 scrollInfo.nMax--;
1344 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1345 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1346 scrollInfo.nPage = nCountPerRow;
1347 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1348 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1349 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1351 else if (uView == LVS_REPORT)
1353 BOOL test;
1355 /* update vertical scrollbar */
1356 scrollInfo.nMin = 0;
1357 scrollInfo.nMax = infoPtr->nItemCount - 1;
1358 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1359 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1360 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1361 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1362 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1363 scrollInfo.nMax, scrollInfo.nPage, test);
1364 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1365 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1367 /* update horizontal scrollbar */
1368 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1369 scrollInfo.fMask = SIF_POS;
1370 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1371 || infoPtr->nItemCount == 0)
1373 scrollInfo.nPos = 0;
1375 scrollInfo.nMin = 0;
1376 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1377 scrollInfo.nPage = nListWidth;
1378 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1379 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1380 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1381 scrollInfo.nMax, scrollInfo.nPage, test);
1382 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1383 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1385 /* Update the Header Control */
1386 scrollInfo.fMask = SIF_POS;
1387 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1388 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1391 else
1393 RECT rcView;
1395 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1397 INT nViewWidth = rcView.right - rcView.left;
1398 INT nViewHeight = rcView.bottom - rcView.top;
1400 /* Update Horizontal Scrollbar */
1401 scrollInfo.fMask = SIF_POS;
1402 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1403 || infoPtr->nItemCount == 0)
1405 scrollInfo.nPos = 0;
1407 scrollInfo.nMin = 0;
1408 scrollInfo.nMax = max(nViewWidth, 0)-1;
1409 scrollInfo.nPage = nListWidth;
1410 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1411 TRACE("LVS_ICON/SMALLICON Horz.\n");
1412 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1414 /* Update Vertical Scrollbar */
1415 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1416 scrollInfo.fMask = SIF_POS;
1417 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1418 || infoPtr->nItemCount == 0)
1420 scrollInfo.nPos = 0;
1422 scrollInfo.nMin = 0;
1423 scrollInfo.nMax = max(nViewHeight,0)-1;
1424 scrollInfo.nPage = nListHeight;
1425 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1426 TRACE("LVS_ICON/SMALLICON Vert.\n");
1427 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1433 /***
1434 * DESCRIPTION:
1435 * Shows/hides the focus rectangle.
1437 * PARAMETER(S):
1438 * [I] infoPtr : valid pointer to the listview structure
1439 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1441 * RETURN:
1442 * None
1444 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1446 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1447 HDC hdc;
1449 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1451 if (infoPtr->nFocusedItem < 0) return;
1453 /* we need some gymnastics in ICON mode to handle large items */
1454 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1456 RECT rcBox;
1458 if (!LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox))
1459 return;
1460 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1462 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1463 return;
1467 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1469 /* for some reason, owner draw should work only in report mode */
1470 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1472 DRAWITEMSTRUCT dis;
1473 LVITEMW item;
1475 item.iItem = infoPtr->nFocusedItem;
1476 item.iSubItem = 0;
1477 item.mask = LVIF_PARAM;
1478 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1480 ZeroMemory(&dis, sizeof(dis));
1481 dis.CtlType = ODT_LISTVIEW;
1482 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1483 dis.itemID = item.iItem;
1484 dis.itemAction = ODA_FOCUS;
1485 if (fShow) dis.itemState |= ODS_FOCUS;
1486 dis.hwndItem = infoPtr->hwndSelf;
1487 dis.hDC = hdc;
1488 if (!LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem)) goto done;
1489 dis.itemData = item.lParam;
1491 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1493 else
1495 DrawFocusRect(hdc, &infoPtr->rcFocus);
1497 done:
1498 ReleaseDC(infoPtr->hwndSelf, hdc);
1501 /***
1502 * Invalidates all visible selected items.
1504 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1506 ITERATOR i;
1508 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1509 while(iterator_next(&i))
1511 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1512 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1514 iterator_destroy(&i);
1518 /***
1519 * DESCRIPTION:
1520 * Prints a message for unsupported window styles.
1521 * A kind of TODO list for window styles.
1523 * PARAMETER(S):
1524 * [I] lStyle : window style
1526 * RETURN:
1527 * None
1529 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1531 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1532 FIXME(" LVS_NOSCROLL\n");
1534 if (lStyle & LVS_NOLABELWRAP)
1535 FIXME(" LVS_NOLABELWRAP\n");
1537 if (lStyle & LVS_SORTASCENDING)
1538 FIXME(" LVS_SORTASCENDING\n");
1540 if (lStyle & LVS_SORTDESCENDING)
1541 FIXME(" LVS_SORTDESCENDING\n");
1545 /***
1546 * DESCRIPTION: [INTERNAL]
1547 * Computes an item's (left,top) corner, relative to rcView.
1548 * That is, the position has NOT been made relative to the Origin.
1549 * This is deliberate, to avoid computing the Origin over, and
1550 * over again, when this function is call in a loop. Instead,
1551 * one ca factor the computation of the Origin before the loop,
1552 * and offset the value retured by this function, on every iteration.
1554 * PARAMETER(S):
1555 * [I] infoPtr : valid pointer to the listview structure
1556 * [I] nItem : item number
1557 * [O] lpptOrig : item top, left corner
1559 * RETURN:
1560 * TRUE if computations OK
1561 * FALSE otherwise
1563 static BOOL LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1565 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1567 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
1569 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1571 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1572 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1574 else if (uView == LVS_LIST)
1576 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1577 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1578 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1580 else /* LVS_REPORT */
1582 lpptPosition->x = REPORT_MARGINX;
1583 lpptPosition->y = nItem * infoPtr->nItemHeight;
1586 return TRUE;
1589 /***
1590 * DESCRIPTION: [INTERNAL]
1591 * Compute the rectangles of an item. This is to localize all
1592 * the computations in one place. If you are not interested in some
1593 * of these values, simply pass in a NULL -- the fucntion is smart
1594 * enough to compute only what's necessary. The function computes
1595 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1596 * one, the BOX rectangle. This rectangle is very cheap to compute,
1597 * and is guaranteed to contain all the other rectangles. Computing
1598 * the ICON rect is also cheap, but all the others are potentaily
1599 * expensive. This gives an easy and effective optimization when
1600 * searching (like point inclusion, or rectangle intersection):
1601 * first test against the BOX, and if TRUE, test agains the desired
1602 * rectangle.
1603 * If the function does not have all the necessary information
1604 * to computed the requested rectangles, will crash with a
1605 * failed assertion. This is done so we catch all programming
1606 * errors, given that the function is called only from our code.
1608 * We have the following 'special' meanings for a few fields:
1609 * * If LVIS_FOCUSED is set, we assume the item has the focus
1610 * This is important in ICON mode, where it might get a larger
1611 * then usual rectange
1613 * Please note that subitem support works only in REPORT mode.
1615 * PARAMETER(S):
1616 * [I] infoPtr : valid pointer to the listview structure
1617 * [I] lpLVItem : item to compute the measures for
1618 * [O] lprcBox : ptr to Box rectangle
1619 * The internal LVIR_BOX rectangle
1620 * [0] lprcState : ptr to State icon rectangle
1621 * The internal LVIR_STATE rectangle
1622 * [O] lprcIcon : ptr to Icon rectangle
1623 * Same as LVM_GETITEMRECT with LVIR_ICON
1624 * [O] lprcLabel : ptr to Label rectangle
1625 * Same as LVM_GETITEMRECT with LVIR_LABEL
1627 * RETURN:
1628 * TRUE if computations OK
1629 * FALSE otherwise
1631 static BOOL LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem,
1632 LPRECT lprcBox, LPRECT lprcState,
1633 LPRECT lprcIcon, LPRECT lprcLabel)
1635 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1636 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1637 RECT Box, State, Icon, Label;
1638 COLUMN_INFO *lpColumnInfo = NULL;
1640 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1642 /* Be smart and try to figure out the minimum we have to do */
1643 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1644 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1646 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1647 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1649 if (lprcLabel) doLabel = TRUE;
1650 if (doLabel || lprcIcon) doIcon = TRUE;
1651 if (doIcon || lprcState) doState = TRUE;
1653 /************************************************************/
1654 /* compute the box rectangle (it should be cheap to do) */
1655 /************************************************************/
1656 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1658 lpColumnInfo = DPA_GetPtr(infoPtr->hdpaColumns, lpLVItem->iSubItem);
1659 assert(lpColumnInfo);
1662 if (lpLVItem->iSubItem)
1664 Box = lpColumnInfo->rcHeader;
1666 else
1668 Box.left = 0;
1669 Box.right = infoPtr->nItemWidth;
1671 Box.top = 0;
1672 Box.bottom = infoPtr->nItemHeight;
1674 /************************************************************/
1675 /* compute STATEICON bounding box */
1676 /************************************************************/
1677 if (doState)
1679 if (uView == LVS_ICON)
1681 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1682 if (infoPtr->himlNormal)
1683 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1684 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1686 else
1688 /* we need the ident in report mode, if we don't have it, we fail */
1689 State.left = Box.left;
1690 if (uView == LVS_REPORT)
1692 State.left += REPORT_MARGINX;
1693 if (lpLVItem->iSubItem == 0)
1695 assert(lpLVItem->mask & LVIF_INDENT);
1696 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1699 State.top = Box.top;
1701 State.right = State.left;
1702 State.bottom = State.top;
1703 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1705 State.right += infoPtr->iconStateSize.cx;
1706 State.bottom += infoPtr->iconStateSize.cy;
1708 if (lprcState) *lprcState = State;
1709 TRACE(" - state=%s\n", debugrect(&State));
1712 /************************************************************/
1713 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1714 /************************************************************/
1715 if (doIcon)
1717 if (uView == LVS_ICON)
1719 Icon.left = Box.left;
1720 if (infoPtr->himlNormal)
1721 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1722 Icon.top = Box.top + ICON_TOP_PADDING;
1723 Icon.right = Icon.left;
1724 Icon.bottom = Icon.top;
1725 if (infoPtr->himlNormal)
1727 Icon.right += infoPtr->iconSize.cx;
1728 Icon.bottom += infoPtr->iconSize.cy;
1731 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1733 Icon.left = State.right;
1734 if (!IsRectEmpty(&State)) Icon.left += IMAGE_PADDING;
1735 Icon.top = Box.top;
1736 Icon.right = Icon.left;
1737 if (infoPtr->himlSmall && (!lpColumnInfo || lpColumnInfo->hasImage))
1738 Icon.right += infoPtr->iconSize.cx;
1739 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1741 if(lprcIcon) *lprcIcon = Icon;
1742 TRACE(" - icon=%s\n", debugrect(&Icon));
1745 /************************************************************/
1746 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1747 /************************************************************/
1748 if (doLabel)
1750 SIZE labelSize = { 0, 0 };
1752 /* calculate how far to the right can the label strech */
1753 Label.right = Box.right;
1754 if (uView == LVS_REPORT)
1756 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1757 Label.right -= REPORT_MARGINX;
1760 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1762 labelSize.cx = infoPtr->nItemWidth;
1763 labelSize.cy = infoPtr->nItemHeight;
1764 goto calc_label;
1767 /* we need the text in non owner draw mode */
1768 assert(lpLVItem->mask & LVIF_TEXT);
1769 if (is_textT(lpLVItem->pszText, TRUE))
1771 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1772 HDC hdc = GetDC(infoPtr->hwndSelf);
1773 HFONT hOldFont = SelectObject(hdc, hFont);
1774 UINT uFormat;
1775 RECT rcText;
1777 /* compute rough rectangle where the label will go */
1778 SetRectEmpty(&rcText);
1779 rcText.right = infoPtr->nItemWidth - TRAILING_PADDING;
1780 rcText.bottom = infoPtr->nItemHeight;
1781 if (uView == LVS_ICON)
1782 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1784 /* now figure out the flags */
1785 if (uView == LVS_ICON)
1786 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1787 else
1788 uFormat = LV_SL_DT_FLAGS;
1790 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1792 labelSize.cx = min(rcText.right - rcText.left + TRAILING_PADDING, infoPtr->nItemWidth);
1793 labelSize.cy = rcText.bottom - rcText.top;
1795 SelectObject(hdc, hOldFont);
1796 ReleaseDC(infoPtr->hwndSelf, hdc);
1799 calc_label:
1800 if (uView == LVS_ICON)
1802 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1803 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1804 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1805 Label.right = Label.left + labelSize.cx;
1806 Label.bottom = Label.top + infoPtr->nItemHeight;
1807 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1809 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1810 labelSize.cy /= infoPtr->ntmHeight;
1811 labelSize.cy = max(labelSize.cy, 1);
1812 labelSize.cy *= infoPtr->ntmHeight;
1814 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1816 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1818 Label.left = Icon.right;
1819 if (!IsRectEmpty(&Icon) || !IsRectEmpty(&State)) Label.left += IMAGE_PADDING;
1820 Label.top = Box.top;
1821 Label.right = min(Label.left + labelSize.cx, Label.right);
1822 Label.bottom = Label.top + infoPtr->nItemHeight;
1825 if (lprcLabel) *lprcLabel = Label;
1826 TRACE(" - label=%s\n", debugrect(&Label));
1829 /* Fix the Box if necessary */
1830 if (lprcBox)
1832 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1833 else *lprcBox = Box;
1835 TRACE(" - box=%s\n", debugrect(&Box));
1837 return TRUE;
1840 /***
1841 * DESCRIPTION: [INTERNAL]
1843 * PARAMETER(S):
1844 * [I] infoPtr : valid pointer to the listview structure
1845 * [I] nItem : item number
1846 * [O] lprcBox : ptr to Box rectangle
1848 * RETURN:
1849 * TRUE if computations OK
1850 * FALSE otherwise
1852 static BOOL LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
1854 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1855 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1856 POINT Position, Origin;
1857 LVITEMW lvItem;
1859 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) return FALSE;
1860 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
1862 /* Be smart and try to figure out the minimum we have to do */
1863 lvItem.mask = 0;
1864 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1865 lvItem.mask |= LVIF_TEXT;
1866 lvItem.iItem = nItem;
1867 lvItem.iSubItem = 0;
1868 lvItem.pszText = szDispText;
1869 lvItem.cchTextMax = DISP_TEXT_SIZE;
1870 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
1871 if (uView == LVS_ICON)
1873 lvItem.mask |= LVIF_STATE;
1874 lvItem.stateMask = LVIS_FOCUSED;
1875 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
1877 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0)) return FALSE;
1879 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
1881 return TRUE;
1884 /***
1885 * DESCRIPTION:
1886 * Aligns the items with the top edge of the window.
1888 * PARAMETER(S):
1889 * [I] infoPtr : valid pointer to the listview structure
1891 * RETURN:
1892 * None
1894 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1896 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1897 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1898 POINT ptItem;
1899 RECT rcView;
1900 INT i, off_x=0, off_y=0;
1902 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1904 /* Since SetItemPosition uses upper-left of icon, and for
1905 style=LVS_ICON the icon is not left adjusted, get the offset */
1906 if (uView == LVS_ICON)
1908 off_y = ICON_TOP_PADDING;
1909 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1911 ptItem.x = off_x;
1912 ptItem.y = off_y;
1913 ZeroMemory(&rcView, sizeof(RECT));
1914 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1915 off_x, off_y,
1916 infoPtr->rcList.left, infoPtr->rcList.right);
1918 if (nListWidth > infoPtr->nItemWidth)
1920 for (i = 0; i < infoPtr->nItemCount; i++)
1922 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1924 ptItem.x = off_x;
1925 ptItem.y += infoPtr->nItemHeight;
1928 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1929 ptItem.x += infoPtr->nItemWidth;
1930 rcView.right = max(rcView.right, ptItem.x);
1933 rcView.right -= off_x;
1934 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1936 else
1938 for (i = 0; i < infoPtr->nItemCount; i++)
1940 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1941 ptItem.y += infoPtr->nItemHeight;
1944 rcView.right = infoPtr->nItemWidth;
1945 rcView.bottom = ptItem.y-off_y;
1948 infoPtr->rcView = rcView;
1952 /***
1953 * DESCRIPTION:
1954 * Aligns the items with the left edge of the window.
1956 * PARAMETER(S):
1957 * [I] infoPtr : valid pointer to the listview structure
1959 * RETURN:
1960 * None
1962 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1964 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1965 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1966 POINT ptItem;
1967 RECT rcView;
1968 INT i, off_x=0, off_y=0;
1970 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1972 /* Since SetItemPosition uses upper-left of icon, and for
1973 style=LVS_ICON the icon is not left adjusted, get the offset */
1974 if (uView == LVS_ICON)
1976 off_y = ICON_TOP_PADDING;
1977 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1979 ptItem.x = off_x;
1980 ptItem.y = off_y;
1981 ZeroMemory(&rcView, sizeof(RECT));
1982 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1984 if (nListHeight > infoPtr->nItemHeight)
1986 for (i = 0; i < infoPtr->nItemCount; i++)
1988 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1990 ptItem.y = off_y;
1991 ptItem.x += infoPtr->nItemWidth;
1994 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1995 ptItem.y += infoPtr->nItemHeight;
1996 rcView.bottom = max(rcView.bottom, ptItem.y);
1999 rcView.right = ptItem.x + infoPtr->nItemWidth;
2001 else
2003 for (i = 0; i < infoPtr->nItemCount; i++)
2005 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
2006 ptItem.x += infoPtr->nItemWidth;
2009 rcView.bottom = infoPtr->nItemHeight;
2010 rcView.right = ptItem.x;
2013 infoPtr->rcView = rcView;
2018 /***
2019 * DESCRIPTION:
2020 * Retrieves the bounding rectangle of all the items.
2022 * PARAMETER(S):
2023 * [I] infoPtr : valid pointer to the listview structure
2024 * [O] lprcView : bounding rectangle
2026 * RETURN:
2027 * SUCCESS : TRUE
2028 * FAILURE : FALSE
2030 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2032 POINT ptOrigin;
2034 TRACE("(lprcView=%p)\n", lprcView);
2036 if (!lprcView) return FALSE;
2038 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return FALSE;
2040 *lprcView = infoPtr->rcView;
2041 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2043 TRACE("lprcView=%s\n", debugrect(lprcView));
2045 return TRUE;
2048 /***
2049 * DESCRIPTION:
2050 * Retrieves the subitem pointer associated with the subitem index.
2052 * PARAMETER(S):
2053 * [I] hdpaSubItems : DPA handle for a specific item
2054 * [I] nSubItem : index of subitem
2056 * RETURN:
2057 * SUCCESS : subitem pointer
2058 * FAILURE : NULL
2060 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2062 LISTVIEW_SUBITEM *lpSubItem;
2063 INT i;
2065 /* we should binary search here if need be */
2066 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2068 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
2069 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
2070 return lpSubItem;
2073 return NULL;
2077 /***
2078 * DESCRIPTION:
2079 * Calculates the width of a specific item.
2081 * PARAMETER(S):
2082 * [I] infoPtr : valid pointer to the listview structure
2083 * [I] nItem : item to calculate width, or -1 for max of all
2085 * RETURN:
2086 * Returns the width of an item width an item.
2088 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr, INT nItem)
2090 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2091 INT nItemWidth = 0, i;
2093 if (uView == LVS_ICON)
2094 nItemWidth = infoPtr->iconSpacing.cx;
2095 else if (uView == LVS_REPORT)
2097 RECT rcHeaderItem;
2099 /* calculate width of header */
2100 for (i = 0; i < infoPtr->hdpaColumns->nItemCount; i++)
2101 if (LISTVIEW_GetHeaderRect(infoPtr, i, &rcHeaderItem))
2102 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
2104 else
2106 INT nLabelWidth;
2108 if (infoPtr->nItemCount == 0) return DEFAULT_COLUMN_WIDTH;
2110 /* get width of string */
2111 if (nItem == -1)
2113 for (i = 0; i < infoPtr->nItemCount; i++)
2115 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
2116 nItemWidth = max(nItemWidth, nLabelWidth);
2119 else
2120 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
2121 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
2122 nItemWidth += WIDTH_PADDING;
2123 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2124 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2125 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
2128 return max(nItemWidth, 1);
2131 /***
2132 * DESCRIPTION:
2133 * Calculates the max width of any item in the list.
2135 * PARAMETER(S):
2136 * [I] infoPtr : valid pointer to the listview structure
2138 * RETURN:
2139 * Returns item width.
2141 static inline INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *infoPtr)
2143 return LISTVIEW_CalculateItemWidth(infoPtr, -1);
2146 /***
2147 * DESCRIPTION:
2148 * Retrieves and saves important text metrics info for the current
2149 * Listview font.
2151 * PARAMETER(S):
2152 * [I] infoPtr : valid pointer to the listview structure
2155 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2157 HDC hdc = GetDC(infoPtr->hwndSelf);
2158 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2159 HFONT hOldFont = SelectObject(hdc, hFont);
2160 TEXTMETRICW tm;
2162 if (GetTextMetricsW(hdc, &tm))
2163 infoPtr->ntmHeight = tm.tmHeight;
2164 SelectObject(hdc, hOldFont);
2165 ReleaseDC(infoPtr->hwndSelf, hdc);
2167 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2171 /***
2172 * DESCRIPTION:
2173 * Calculates the height of an item.
2175 * PARAMETER(S):
2176 * [I] infoPtr : valid pointer to the listview structure
2178 * RETURN:
2179 * Returns item height.
2181 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *infoPtr)
2183 INT nItemHeight;
2185 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
2186 nItemHeight = infoPtr->iconSpacing.cy;
2187 else
2189 nItemHeight = infoPtr->ntmHeight;
2190 if (infoPtr->himlState)
2191 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2192 if (infoPtr->himlSmall)
2193 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2194 if (infoPtr->himlState || infoPtr->himlSmall)
2195 nItemHeight += HEIGHT_PADDING;
2197 return nItemHeight;
2200 /***
2201 * DESCRIPTION:
2202 * A compare function for ranges
2204 * PARAMETER(S)
2205 * [I] range1 : pointer to range 1;
2206 * [I] range2 : pointer to range 2;
2207 * [I] flags : flags
2209 * RETURNS:
2210 * > 0 : if range 1 > range 2
2211 * < 0 : if range 2 > range 1
2212 * = 0 : if range intersects range 2
2214 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2216 INT cmp;
2218 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2219 cmp = -1;
2220 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2221 cmp = 1;
2222 else
2223 cmp = 0;
2225 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2227 return cmp;
2230 #if DEBUG_RANGES
2231 #define ranges_check ranges_assert
2232 #else
2233 #define ranges_check(ranges, desc) do { } while(0)
2234 #endif
2236 static void ranges_assert(RANGES ranges, LPCSTR desc)
2238 INT i;
2239 RANGE *prev, *curr;
2241 assert (ranges);
2242 assert (ranges->hdpa->nItemCount >= 0);
2243 if (ranges->hdpa->nItemCount == 0) return;
2244 TRACE("*** Checking %s ***\n", desc);
2245 ranges_dump(ranges);
2246 assert ((prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0))->lower >= 0);
2247 /* assert (((RANGE *)DPA_GetPtr(ranges->hdpa, ranges->hdpa->nItemCount - 1))->upper <= nUpper); */
2248 for (i = 1; i < ranges->hdpa->nItemCount; i++)
2250 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2251 assert (prev->upper <= curr->lower);
2252 prev = curr;
2254 TRACE("--- Done checking---\n");
2257 static RANGES ranges_create(int count)
2259 RANGES ranges = (RANGES)COMCTL32_Alloc(sizeof(struct tagRANGES));
2260 if (!ranges) return NULL;
2261 ranges->hdpa = DPA_Create(count);
2262 if (ranges->hdpa) return ranges;
2263 COMCTL32_Free(ranges);
2264 return NULL;
2267 static void ranges_destroy(RANGES ranges)
2269 if (!ranges) return;
2270 if (ranges->hdpa)
2272 INT i;
2274 for(i = 0; i < ranges->hdpa->nItemCount; i++)
2275 COMCTL32_Free(DPA_GetPtr(ranges->hdpa, i));
2276 DPA_Destroy(ranges->hdpa);
2277 ranges->hdpa = NULL;
2279 COMCTL32_Free(ranges);
2282 static RANGES ranges_clone(RANGES ranges)
2284 RANGES clone;
2285 INT i;
2287 if (!ranges) return NULL;
2288 clone = ranges_create(ranges->hdpa->nItemCount);
2289 if (!clone) return NULL;
2291 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2293 RANGE *newrng = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2294 if (!newrng)
2296 ranges_destroy(clone);
2297 return NULL;
2299 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2300 DPA_InsertPtr(clone->hdpa, i, newrng);
2302 return clone;
2305 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2307 INT i;
2309 if (!ranges || !sub) return ranges;
2311 for (i = 0; i < sub->hdpa->nItemCount; i++)
2312 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2314 return ranges;
2317 static void ranges_dump(RANGES ranges)
2319 INT i;
2321 if (!ranges) return;
2322 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2323 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2326 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2328 RANGE srchrng = { nItem, nItem + 1 };
2330 TRACE("(nItem=%d)\n", nItem);
2331 if (!ranges) return FALSE;
2332 if (TRACE_ON(listview)) ranges_dump(ranges);
2333 ranges_check(ranges, "before contain");
2334 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2337 static INT ranges_itemcount(RANGES ranges)
2339 INT i, count = 0;
2341 if (!ranges) return 0;
2342 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2344 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2345 count += sel->upper - sel->lower;
2348 return count;
2351 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2353 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2354 INT index;
2356 if (!ranges) return FALSE;
2357 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2358 if (index == -1) return TRUE;
2360 for (;index < ranges->hdpa->nItemCount; index++)
2362 chkrng = DPA_GetPtr(ranges->hdpa, index);
2363 if (chkrng->lower >= nItem)
2364 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2365 if (chkrng->upper > nItem)
2366 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2368 return TRUE;
2371 static BOOL ranges_add(RANGES ranges, RANGE range)
2373 RANGE srchrgn;
2374 INT index;
2376 TRACE("(%s)\n", debugrange(&range));
2377 ranges_check(ranges, "before add");
2378 if (!ranges) goto fail;
2380 /* try find overlapping regions first */
2381 srchrgn.lower = range.lower - 1;
2382 srchrgn.upper = range.upper + 1;
2383 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2385 if (index == -1)
2387 RANGE *newrgn;
2389 TRACE("Adding new range\n");
2391 /* create the brand new range to insert */
2392 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2393 if(!newrgn) goto fail;
2394 *newrgn = range;
2396 /* figure out where to insert it */
2397 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2398 TRACE("index=%d\n", index);
2399 if (index == -1) index = 0;
2401 /* and get it over with */
2402 DPA_InsertPtr(ranges->hdpa, index, newrgn);
2404 else
2406 RANGE *chkrgn, *mrgrgn;
2407 INT fromindex, mergeindex;
2409 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2410 if (!chkrgn) goto fail;
2411 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2413 chkrgn->lower = min(range.lower, chkrgn->lower);
2414 chkrgn->upper = max(range.upper, chkrgn->upper);
2416 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2418 /* merge now common anges */
2419 fromindex = 0;
2420 srchrgn.lower = chkrgn->lower - 1;
2421 srchrgn.upper = chkrgn->upper + 1;
2425 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2426 if (mergeindex == -1) break;
2427 if (mergeindex == index)
2429 fromindex = index + 1;
2430 continue;
2433 TRACE("Merge with index %i\n", mergeindex);
2435 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2436 if (!mrgrgn) goto fail;
2438 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2439 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2440 COMCTL32_Free(mrgrgn);
2441 DPA_DeletePtr(ranges->hdpa, mergeindex);
2442 if (mergeindex < index) index --;
2443 } while(1);
2446 ranges_check(ranges, "after add");
2447 if (TRACE_ON(listview)) ranges_dump(ranges);
2448 return TRUE;
2450 fail:
2451 ranges_check(ranges, "failed add");
2452 return FALSE;
2455 static BOOL ranges_del(RANGES ranges, RANGE range)
2457 RANGE *chkrgn;
2458 INT index;
2460 TRACE("(%s)\n", debugrange(&range));
2461 ranges_check(ranges, "before del");
2462 if (!ranges) goto fail;
2464 /* we don't use DPAS_SORTED here, since we need *
2465 * to find the first overlapping range */
2466 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2467 while(index != -1)
2469 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2470 if (!chkrgn) goto fail;
2472 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2474 /* case 1: Same range */
2475 if ( (chkrgn->upper == range.upper) &&
2476 (chkrgn->lower == range.lower) )
2478 DPA_DeletePtr(ranges->hdpa, index);
2479 break;
2481 /* case 2: engulf */
2482 else if ( (chkrgn->upper <= range.upper) &&
2483 (chkrgn->lower >= range.lower) )
2485 DPA_DeletePtr(ranges->hdpa, index);
2487 /* case 3: overlap upper */
2488 else if ( (chkrgn->upper <= range.upper) &&
2489 (chkrgn->lower < range.lower) )
2491 chkrgn->upper = range.lower;
2493 /* case 4: overlap lower */
2494 else if ( (chkrgn->upper > range.upper) &&
2495 (chkrgn->lower >= range.lower) )
2497 chkrgn->lower = range.upper;
2498 break;
2500 /* case 5: fully internal */
2501 else
2503 RANGE tmprgn = *chkrgn, *newrgn;
2505 if (!(newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE)))) goto fail;
2506 newrgn->lower = chkrgn->lower;
2507 newrgn->upper = range.lower;
2508 chkrgn->lower = range.upper;
2509 DPA_InsertPtr(ranges->hdpa, index, newrgn);
2510 chkrgn = &tmprgn;
2511 break;
2514 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2517 ranges_check(ranges, "after del");
2518 return TRUE;
2520 fail:
2521 ranges_check(ranges, "failed del");
2522 return FALSE;
2525 /***
2526 * DESCRIPTION:
2527 * Removes all selection ranges
2529 * Parameters(s):
2530 * [I] infoPtr : valid pointer to the listview structure
2531 * [I] toSkip : item range to skip removing the selection
2533 * RETURNS:
2534 * SUCCESS : TRUE
2535 * FAILURE : TRUE
2537 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2539 LVITEMW lvItem;
2540 ITERATOR i;
2542 TRACE("()\n");
2543 if (TRACE_ON(listview)) ranges_dump(toSkip);
2545 lvItem.state = 0;
2546 lvItem.stateMask = LVIS_SELECTED;
2548 /* need to clone the DPA because callbacks can change it */
2549 iterator_ranges(&i, ranges_diff(ranges_clone(infoPtr->selectionRanges), toSkip));
2550 while(iterator_next(&i))
2551 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2552 /* note that the iterator destructor will free the cloned range */
2553 iterator_destroy(&i);
2555 return TRUE;
2558 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2560 RANGES toSkip = ranges_create(1);
2562 if (!toSkip) return FALSE;
2563 if (nItem != -1) ranges_additem(toSkip, nItem);
2564 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2565 ranges_destroy(toSkip);
2566 return TRUE;
2569 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2571 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2574 /***
2575 * DESCRIPTION:
2576 * Retrieves the number of items that are marked as selected.
2578 * PARAMETER(S):
2579 * [I] infoPtr : valid pointer to the listview structure
2581 * RETURN:
2582 * Number of items selected.
2584 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2586 INT nSelectedCount = 0;
2588 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2590 INT i;
2591 for (i = 0; i < infoPtr->nItemCount; i++)
2593 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2594 nSelectedCount++;
2597 else
2598 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2600 TRACE("nSelectedCount=%d\n", nSelectedCount);
2601 return nSelectedCount;
2604 /***
2605 * DESCRIPTION:
2606 * Manages the item focus.
2608 * PARAMETER(S):
2609 * [I] infoPtr : valid pointer to the listview structure
2610 * [I] nItem : item index
2612 * RETURN:
2613 * TRUE : focused item changed
2614 * FALSE : focused item has NOT changed
2616 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2618 INT oldFocus = infoPtr->nFocusedItem;
2619 LVITEMW lvItem;
2621 if (nItem == infoPtr->nFocusedItem) return FALSE;
2623 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2624 lvItem.stateMask = LVIS_FOCUSED;
2625 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2627 return oldFocus != infoPtr->nFocusedItem;
2630 /* Helper function for LISTVIEW_ShiftIndices *only* */
2631 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2633 if (nShiftItem < nItem) return nShiftItem;
2635 if (nShiftItem > nItem) return nShiftItem + direction;
2637 if (direction > 0) return nShiftItem + direction;
2639 return min(nShiftItem, infoPtr->nItemCount - 1);
2643 * DESCRIPTION:
2644 * Updates the various indices after an item has been inserted or deleted.
2646 * PARAMETER(S):
2647 * [I] infoPtr : valid pointer to the listview structure
2648 * [I] nItem : item index
2649 * [I] direction : Direction of shift, +1 or -1.
2651 * RETURN:
2652 * None
2654 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2656 INT nNewFocus;
2658 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2660 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2662 assert(abs(direction) == 1);
2664 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2666 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2667 if (nNewFocus != infoPtr->nFocusedItem)
2668 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2670 /* But we are not supposed to modify nHotItem! */
2675 * DESCRIPTION:
2676 * Adds a block of selections.
2678 * PARAMETER(S):
2679 * [I] infoPtr : valid pointer to the listview structure
2680 * [I] nItem : item index
2682 * RETURN:
2683 * None
2685 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2687 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2688 INT nLast = max(infoPtr->nSelectionMark, nItem);
2689 INT i;
2690 LVITEMW item;
2692 if (nFirst == -1)
2693 nFirst = nItem;
2695 item.state = LVIS_SELECTED;
2696 item.stateMask = LVIS_SELECTED;
2698 /* FIXME: this is not correct LVS_OWNERDATA
2699 * See docu for LVN_ITEMCHANGED. Is there something similar for
2700 * RemoveGroupSelection (is there such a thing?)?
2702 for (i = nFirst; i <= nLast; i++)
2703 LISTVIEW_SetItemState(infoPtr,i,&item);
2705 LISTVIEW_SetItemFocus(infoPtr, nItem);
2706 infoPtr->nSelectionMark = nItem;
2710 /***
2711 * DESCRIPTION:
2712 * Sets a single group selection.
2714 * PARAMETER(S):
2715 * [I] infoPtr : valid pointer to the listview structure
2716 * [I] nItem : item index
2718 * RETURN:
2719 * None
2721 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2723 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2724 RANGES selection;
2725 LVITEMW item;
2726 ITERATOR i;
2728 if (!(selection = ranges_create(100))) return;
2730 item.state = LVIS_SELECTED;
2731 item.stateMask = LVIS_SELECTED;
2733 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2735 if (infoPtr->nSelectionMark == -1)
2737 infoPtr->nSelectionMark = nItem;
2738 ranges_additem(selection, nItem);
2740 else
2742 RANGE sel;
2744 sel.lower = min(infoPtr->nSelectionMark, nItem);
2745 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
2746 ranges_add(selection, sel);
2749 else
2751 RECT rcItem, rcSel, rcSelMark;
2752 POINT ptItem;
2754 rcItem.left = LVIR_BOUNDS;
2755 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2756 rcSelMark.left = LVIR_BOUNDS;
2757 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2758 UnionRect(&rcSel, &rcItem, &rcSelMark);
2759 iterator_frameditems(&i, infoPtr, &rcSel);
2760 while(iterator_next(&i))
2762 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
2763 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
2765 iterator_destroy(&i);
2768 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
2769 iterator_ranges(&i, selection);
2770 while(iterator_next(&i))
2771 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
2772 /* this will also destroy the selection */
2773 iterator_destroy(&i);
2775 LISTVIEW_SetItemFocus(infoPtr, nItem);
2778 /***
2779 * DESCRIPTION:
2780 * Sets a single selection.
2782 * PARAMETER(S):
2783 * [I] infoPtr : valid pointer to the listview structure
2784 * [I] nItem : item index
2786 * RETURN:
2787 * None
2789 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2791 LVITEMW lvItem;
2793 TRACE("nItem=%d\n", nItem);
2795 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
2797 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2798 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2799 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2801 infoPtr->nSelectionMark = nItem;
2804 /***
2805 * DESCRIPTION:
2806 * Set selection(s) with keyboard.
2808 * PARAMETER(S):
2809 * [I] infoPtr : valid pointer to the listview structure
2810 * [I] INT : item index
2812 * RETURN:
2813 * SUCCESS : TRUE (needs to be repainted)
2814 * FAILURE : FALSE (nothing has changed)
2816 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2818 /* FIXME: pass in the state */
2819 LONG lStyle = infoPtr->dwStyle;
2820 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2821 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2822 BOOL bResult = FALSE;
2824 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2826 if (lStyle & LVS_SINGLESEL)
2828 bResult = TRUE;
2829 LISTVIEW_SetSelection(infoPtr, nItem);
2831 else
2833 if (wShift)
2835 bResult = TRUE;
2836 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2838 else if (wCtrl)
2840 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2842 else
2844 bResult = TRUE;
2845 LISTVIEW_SetSelection(infoPtr, nItem);
2848 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2851 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2852 return bResult;
2856 /***
2857 * DESCRIPTION:
2858 * Called when the mouse is being actively tracked and has hovered for a specified
2859 * amount of time
2861 * PARAMETER(S):
2862 * [I] infoPtr : valid pointer to the listview structure
2863 * [I] fwKeys : key indicator
2864 * [I] pts : mouse position
2866 * RETURN:
2867 * 0 if the message was processed, non-zero if there was an error
2869 * INFO:
2870 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2871 * over the item for a certain period of time.
2874 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2876 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2877 /* FIXME: select the item!!! */
2878 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2880 return 0;
2883 /***
2884 * DESCRIPTION:
2885 * Called whenever WM_MOUSEMOVE is received.
2887 * PARAMETER(S):
2888 * [I] infoPtr : valid pointer to the listview structure
2889 * [I] fwKeys : key indicator
2890 * [I] pts : mouse position
2892 * RETURN:
2893 * 0 if the message is processed, non-zero if there was an error
2895 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2897 TRACKMOUSEEVENT trackinfo;
2899 /* see if we are supposed to be tracking mouse hovering */
2900 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2901 /* fill in the trackinfo struct */
2902 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2903 trackinfo.dwFlags = TME_QUERY;
2904 trackinfo.hwndTrack = infoPtr->hwndSelf;
2905 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2907 /* see if we are already tracking this hwnd */
2908 _TrackMouseEvent(&trackinfo);
2910 if(!(trackinfo.dwFlags & TME_HOVER)) {
2911 trackinfo.dwFlags = TME_HOVER;
2913 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2914 _TrackMouseEvent(&trackinfo);
2918 return 0;
2922 /***
2923 * Tests wheather the item is assignable to a list with style lStyle
2925 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2927 if ( (lpLVItem->mask & LVIF_TEXT) &&
2928 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2929 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2931 return TRUE;
2935 /***
2936 * DESCRIPTION:
2937 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2939 * PARAMETER(S):
2940 * [I] infoPtr : valid pointer to the listview structure
2941 * [I] lpLVItem : valid pointer to new item atttributes
2942 * [I] isNew : the item being set is being inserted
2943 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2944 * [O] bChanged : will be set to TRUE if the item really changed
2946 * RETURN:
2947 * SUCCESS : TRUE
2948 * FAILURE : FALSE
2950 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
2952 LISTVIEW_ITEM *lpItem;
2953 NMLISTVIEW nmlv;
2954 UINT uChanged = 0;
2955 LVITEMW item;
2957 TRACE("()\n");
2959 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
2961 if (lpLVItem->mask == 0) return TRUE;
2963 if (infoPtr->dwStyle & LVS_OWNERDATA)
2965 /* a virtual listview we stores only selection and focus */
2966 if ((lpLVItem->mask & ~LVIF_STATE) || (lpLVItem->stateMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
2967 return FALSE;
2968 lpItem = NULL;
2970 else
2972 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2973 if (!hdpaSubItems) return FALSE;
2974 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0))) return FALSE;
2977 /* we need to get the lParam and state of the item */
2978 item.iItem = lpLVItem->iItem;
2979 item.iSubItem = lpLVItem->iSubItem;
2980 item.mask = LVIF_STATE | LVIF_PARAM;
2981 item.stateMask = ~0;
2982 item.state = 0;
2983 item.lParam = 0;
2984 if (!isNew && !LISTVIEW_GetItemT(infoPtr, &item, TRUE)) return FALSE;
2986 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
2987 /* determine what fields will change */
2988 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
2989 uChanged |= LVIF_STATE;
2991 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2992 uChanged |= LVIF_IMAGE;
2994 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2995 uChanged |= LVIF_PARAM;
2997 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2998 uChanged |= LVIF_INDENT;
3000 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3001 uChanged |= LVIF_TEXT;
3003 TRACE("uChanged=0x%x\n", uChanged);
3004 if (!uChanged) return TRUE;
3005 *bChanged = TRUE;
3007 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3008 nmlv.iItem = lpLVItem->iItem;
3009 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3010 nmlv.uOldState = item.state;
3011 nmlv.uChanged = uChanged;
3012 nmlv.lParam = item.lParam;
3014 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3015 /* and we are _NOT_ virtual (LVS_OWERNDATA) */
3016 if(lpItem && !isNew && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3017 return FALSE;
3019 /* copy information */
3020 if (lpLVItem->mask & LVIF_TEXT)
3021 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3023 if (lpLVItem->mask & LVIF_IMAGE)
3024 lpItem->hdr.iImage = lpLVItem->iImage;
3026 if (lpLVItem->mask & LVIF_PARAM)
3027 lpItem->lParam = lpLVItem->lParam;
3029 if (lpLVItem->mask & LVIF_INDENT)
3030 lpItem->iIndent = lpLVItem->iIndent;
3032 if (uChanged & LVIF_STATE)
3034 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED))
3036 lpItem->state &= ~lpLVItem->stateMask;
3037 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3039 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3041 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3042 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3044 else if (lpLVItem->stateMask & LVIS_SELECTED)
3045 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3047 /* if we are asked to change focus, and we manage it, do it */
3048 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3050 if (lpLVItem->state & LVIS_FOCUSED)
3052 LISTVIEW_SetItemFocus(infoPtr, -1);
3053 infoPtr->nFocusedItem = lpLVItem->iItem;
3054 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
3056 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3057 infoPtr->nFocusedItem = -1;
3061 /* if we're inserting the item, we're done */
3062 if (isNew) return TRUE;
3064 /* send LVN_ITEMCHANGED notification */
3065 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3066 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3068 return TRUE;
3071 /***
3072 * DESCRIPTION:
3073 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3075 * PARAMETER(S):
3076 * [I] infoPtr : valid pointer to the listview structure
3077 * [I] lpLVItem : valid pointer to new subitem atttributes
3078 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3079 * [O] bChanged : will be set to TRUE if the item really changed
3081 * RETURN:
3082 * SUCCESS : TRUE
3083 * FAILURE : FALSE
3085 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged)
3087 HDPA hdpaSubItems;
3088 LISTVIEW_SUBITEM *lpSubItem;
3090 /* we do not support subitems for virtual listviews */
3091 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3093 /* set subitem only if column is present */
3094 if (lpLVItem->iSubItem >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3096 /* First do some sanity checks */
3097 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3098 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3100 /* get the subitem structure, and create it if not there */
3101 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3102 if (!hdpaSubItems) return FALSE;
3104 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3105 if (!lpSubItem)
3107 LISTVIEW_SUBITEM *tmpSubItem;
3108 INT i;
3110 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
3111 if (!lpSubItem) return FALSE;
3112 /* we could binary search here, if need be...*/
3113 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3115 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3116 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3118 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3120 COMCTL32_Free(lpSubItem);
3121 return FALSE;
3123 lpSubItem->iSubItem = lpLVItem->iSubItem;
3124 *bChanged = TRUE;
3127 if (lpLVItem->mask & LVIF_IMAGE)
3128 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3130 lpSubItem->hdr.iImage = lpLVItem->iImage;
3131 *bChanged = TRUE;
3134 if (lpLVItem->mask & LVIF_TEXT)
3135 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3137 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3138 *bChanged = TRUE;
3141 return TRUE;
3144 /***
3145 * DESCRIPTION:
3146 * Sets item attributes.
3148 * PARAMETER(S):
3149 * [I] infoPtr : valid pointer to the listview structure
3150 * [I] lpLVItem : new item atttributes
3151 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3153 * RETURN:
3154 * SUCCESS : TRUE
3155 * FAILURE : FALSE
3157 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
3159 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3160 LPWSTR pszText = NULL;
3161 BOOL bResult, bChanged = FALSE;
3163 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3165 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3166 return FALSE;
3168 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3169 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3171 pszText = lpLVItem->pszText;
3172 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3175 /* actually set the fields */
3176 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3178 if (lpLVItem->iSubItem)
3179 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3180 else
3181 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3183 /* redraw item, if necessary */
3184 if (bChanged && !infoPtr->bIsDrawing)
3186 /* this little optimization eliminates some nasty flicker */
3187 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3188 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3189 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3190 else
3191 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3193 /* restore text */
3194 if (pszText)
3196 textfreeT(lpLVItem->pszText, isW);
3197 lpLVItem->pszText = pszText;
3200 return bResult;
3203 /***
3204 * DESCRIPTION:
3205 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3207 * PARAMETER(S):
3208 * [I] infoPtr : valid pointer to the listview structure
3210 * RETURN:
3211 * item index
3213 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3215 LONG lStyle = infoPtr->dwStyle;
3216 UINT uView = lStyle & LVS_TYPEMASK;
3217 INT nItem = 0;
3218 SCROLLINFO scrollInfo;
3220 scrollInfo.cbSize = sizeof(SCROLLINFO);
3221 scrollInfo.fMask = SIF_POS;
3223 if (uView == LVS_LIST)
3225 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3226 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3228 else if (uView == LVS_REPORT)
3230 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3231 nItem = scrollInfo.nPos;
3233 else
3235 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3236 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3239 TRACE("nItem=%d\n", nItem);
3241 return nItem;
3245 /***
3246 * DESCRIPTION:
3247 * Erases the background of the given rectangle
3249 * PARAMETER(S):
3250 * [I] infoPtr : valid pointer to the listview structure
3251 * [I] hdc : device context handle
3252 * [I] lprcBox : clipping rectangle
3254 * RETURN:
3255 * Success: TRUE
3256 * Failure: FALSE
3258 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
3260 if (!infoPtr->hBkBrush) return FALSE;
3262 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3264 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3267 /***
3268 * DESCRIPTION:
3269 * Draws an item.
3271 * PARAMETER(S):
3272 * [I] infoPtr : valid pointer to the listview structure
3273 * [I] hdc : device context handle
3274 * [I] nItem : item index
3275 * [I] nSubItem : subitem index
3276 * [I] pos : item position in client coordinates
3277 * [I] cdmode : custom draw mode
3279 * RETURN:
3280 * Success: TRUE
3281 * Failure: FALSE
3283 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3285 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3286 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3287 DWORD cditemmode = CDRF_DODEFAULT;
3288 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3289 NMLVCUSTOMDRAW nmlvcd;
3290 HIMAGELIST himl;
3291 LVITEMW lvItem;
3293 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3295 /* get information needed for drawing the item */
3296 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3297 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3298 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3299 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3300 lvItem.iItem = nItem;
3301 lvItem.iSubItem = nSubItem;
3302 lvItem.cchTextMax = DISP_TEXT_SIZE;
3303 lvItem.pszText = szDispText;
3304 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3305 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3307 /* now check if we need to update the focus rectangle */
3308 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3310 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3311 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel)) return FALSE;
3312 OffsetRect(&rcBox, pos.x, pos.y);
3313 OffsetRect(&rcState, pos.x, pos.y);
3314 OffsetRect(&rcIcon, pos.x, pos.y);
3315 OffsetRect(&rcLabel, pos.x, pos.y);
3316 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3317 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3319 /* fill in the custom draw structure */
3320 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox);
3321 nmlvcd.nmcd.dwItemSpec = lvItem.iItem;
3322 nmlvcd.iSubItem = lvItem.iSubItem;
3323 if (lvItem.state & LVIS_SELECTED) nmlvcd.nmcd.uItemState |= CDIS_SELECTED;
3324 if (lvItem.state & LVIS_FOCUSED) nmlvcd.nmcd.uItemState |= CDIS_FOCUS;
3325 if (lvItem.iItem == infoPtr->nHotItem) nmlvcd.nmcd.uItemState |= CDIS_HOT;
3326 nmlvcd.nmcd.lItemlParam = lvItem.lParam;
3327 if ((lvItem.state & LVIS_SELECTED) &&
3328 (lvItem.iSubItem == 0 ||
3329 (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))))
3331 if (infoPtr->bFocus)
3333 nmlvcd.clrTextBk = comctl32_color.clrHighlight;
3334 nmlvcd.clrText = comctl32_color.clrHighlightText;
3336 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
3338 nmlvcd.clrTextBk = comctl32_color.clr3dFace;
3339 nmlvcd.clrText = comctl32_color.clrBtnText;
3343 if (cdmode & CDRF_NOTIFYITEMDRAW)
3344 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3345 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3347 /* state icons */
3348 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3350 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3351 if (uStateImage)
3352 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3355 /* small icons */
3356 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3357 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3358 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3359 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3361 /* Don't bother painting item being edited */
3362 if (infoPtr->hwndEdit && lprcFocus && nSubItem == 0) goto postpaint;
3364 /* Set the text attributes */
3365 SetBkMode(hdc, nmlvcd.clrTextBk == CLR_NONE ? TRANSPARENT : OPAQUE);
3366 if (nmlvcd.clrTextBk != CLR_NONE)
3367 SetBkColor(hdc, nmlvcd.clrTextBk == CLR_DEFAULT ? infoPtr->clrTextBkDefault : nmlvcd.clrTextBk);
3368 SetTextColor(hdc, nmlvcd.clrText);
3370 /* draw the selection background, if we're drawing the main item */
3371 if (nSubItem == 0)
3373 rcSelect = rcLabel;
3374 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3375 rcSelect.right = rcBox.right;
3377 if (lvItem.state & LVIS_SELECTED)
3378 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3379 if(lprcFocus) *lprcFocus = rcSelect;
3382 /* figure out the text drawing flags */
3383 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3384 if (uView == LVS_ICON)
3385 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3386 else if (nSubItem)
3388 COLUMN_INFO *lpColumnInfo = DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
3389 if (lpColumnInfo) uFormat |= lpColumnInfo->align;
3391 if (!(uFormat & (DT_RIGHT | DT_CENTER))) rcLabel.left += 2;
3392 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3394 postpaint:
3395 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3396 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3397 return TRUE;
3400 /***
3401 * DESCRIPTION:
3402 * Draws listview items when in owner draw mode.
3404 * PARAMETER(S):
3405 * [I] infoPtr : valid pointer to the listview structure
3406 * [I] hdc : device context handle
3408 * RETURN:
3409 * None
3411 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
3413 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3414 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3415 POINT Origin, Position;
3416 DRAWITEMSTRUCT dis;
3417 LVITEMW item;
3418 ITERATOR i;
3420 TRACE("()\n");
3422 ZeroMemory(&dis, sizeof(dis));
3424 /* Get scroll info once before loop */
3425 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3427 /* figure out what we need to draw */
3428 iterator_visibleitems(&i, infoPtr, hdc);
3430 /* send cache hint notification */
3431 if (infoPtr->dwStyle & LVS_OWNERDATA)
3433 RANGE range = iterator_range(&i);
3434 NMLVCACHEHINT nmlv;
3436 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3437 nmlv.iFrom = range.lower;
3438 nmlv.iTo = range.upper - 1;
3439 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3442 /* iterate through the invalidated rows */
3443 while(iterator_prev(&i))
3445 item.iItem = i.nItem;
3446 item.iSubItem = 0;
3447 item.mask = LVIF_PARAM | LVIF_STATE;
3448 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3449 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3451 dis.CtlType = ODT_LISTVIEW;
3452 dis.CtlID = uID;
3453 dis.itemID = item.iItem;
3454 dis.itemAction = ODA_DRAWENTIRE;
3455 dis.itemState = 0;
3456 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3457 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3458 dis.hwndItem = infoPtr->hwndSelf;
3459 dis.hDC = hdc;
3460 if (!LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position)) continue;
3461 dis.rcItem.left = Position.x + Origin.x;
3462 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3463 dis.rcItem.top = Position.y + Origin.y;
3464 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3465 dis.itemData = item.lParam;
3467 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3468 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3470 iterator_destroy(&i);
3473 /***
3474 * DESCRIPTION:
3475 * Draws listview items when in report display mode.
3477 * PARAMETER(S):
3478 * [I] infoPtr : valid pointer to the listview structure
3479 * [I] hdc : device context handle
3480 * [I] cdmode : custom draw mode
3482 * RETURN:
3483 * None
3485 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3487 INT rgntype, nFirstCol, nLastCol, nCol;
3488 RECT rcClip, rcItem;
3489 POINT Origin, Position;
3490 ITERATOR i;
3492 TRACE("()\n");
3494 /* figure out what to draw */
3495 rgntype = GetClipBox(hdc, &rcClip);
3496 if (rgntype == NULLREGION) return;
3498 /* Get scroll info once before loop */
3499 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3501 /* narrow down the columns we need to paint */
3502 for(nFirstCol = 0; nFirstCol < infoPtr->hdpaColumns->nItemCount; nFirstCol++)
3503 if (LISTVIEW_GetHeaderRect(infoPtr, nFirstCol, &rcItem) &&
3504 rcItem.right + Origin.x >= rcClip.left) break;
3505 for(nLastCol = infoPtr->hdpaColumns->nItemCount - 1; nLastCol >= 0; nLastCol--)
3506 if (LISTVIEW_GetHeaderRect(infoPtr, nLastCol, &rcItem) &&
3507 rcItem.left + Origin.x < rcClip.right) break;
3509 /* figure out what we need to draw */
3510 iterator_visibleitems(&i, infoPtr, hdc);
3512 /* a last few bits before we start drawing */
3513 TRACE("Colums=(%di - %d)\n", nFirstCol, nLastCol);
3515 /* iterate through the invalidated rows */
3516 while(iterator_prev(&i))
3518 /* iterate through the invalidated columns */
3519 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
3521 if (!LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position)) continue;
3522 Position.x += Origin.x;
3523 Position.y += Origin.y;
3525 if (rgntype == COMPLEXREGION && LISTVIEW_GetHeaderRect(infoPtr, nCol, &rcItem))
3527 rcItem.top = 0;
3528 rcItem.bottom = infoPtr->nItemHeight;
3529 OffsetRect(&rcItem, Position.x, Position.y);
3530 if (!RectVisible(hdc, &rcItem)) continue;
3533 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, nCol, Position, cdmode);
3536 iterator_destroy(&i);
3539 /***
3540 * DESCRIPTION:
3541 * Draws listview items when in list display mode.
3543 * PARAMETER(S):
3544 * [I] infoPtr : valid pointer to the listview structure
3545 * [I] hdc : device context handle
3546 * [I] cdmode : custom draw mode
3548 * RETURN:
3549 * None
3551 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3553 POINT Origin, Position;
3554 ITERATOR i;
3556 /* Get scroll info once before loop */
3557 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3559 /* figure out what we need to draw */
3560 iterator_visibleitems(&i, infoPtr, hdc);
3562 while(iterator_prev(&i))
3564 if (!LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position)) continue;
3565 Position.x += Origin.x;
3566 Position.y += Origin.y;
3568 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, 0, Position, cdmode);
3570 iterator_destroy(&i);
3574 /***
3575 * DESCRIPTION:
3576 * Draws listview items.
3578 * PARAMETER(S):
3579 * [I] infoPtr : valid pointer to the listview structure
3580 * [I] hdc : device context handle
3582 * RETURN:
3583 * NoneX
3585 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3587 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3588 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3589 NMLVCUSTOMDRAW nmlvcd;
3590 HFONT hOldFont;
3591 DWORD cdmode;
3592 INT oldBkMode;
3593 RECT rcClient;
3595 LISTVIEW_DUMP(infoPtr);
3597 infoPtr->bIsDrawing = TRUE;
3599 /* save dc values we're gonna trash while drawing */
3600 hOldFont = SelectObject(hdc, infoPtr->hFont);
3601 oldBkMode = GetBkMode(hdc);
3602 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3603 oldTextColor = GetTextColor(hdc);
3605 oldClrTextBk = infoPtr->clrTextBk;
3606 oldClrText = infoPtr->clrText;
3608 GetClientRect(infoPtr->hwndSelf, &rcClient);
3609 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient);
3610 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3611 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3613 /* Use these colors to draw the items */
3614 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3615 infoPtr->clrText = nmlvcd.clrText;
3617 /* nothing to draw */
3618 if(infoPtr->nItemCount == 0) goto enddraw;
3620 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3621 LISTVIEW_RefreshOwnerDraw(infoPtr, hdc);
3622 else
3624 if (uView == LVS_REPORT)
3625 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3626 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3627 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3629 /* if we have a focus rect, draw it */
3630 if (infoPtr->bFocus)
3631 DrawFocusRect(hdc, &infoPtr->rcFocus);
3634 enddraw:
3635 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3636 notify_customdraw(infoPtr, CDDS_POSTPAINT, &nmlvcd);
3638 infoPtr->clrTextBk = oldClrTextBk;
3639 infoPtr->clrText = oldClrText;
3641 SelectObject(hdc, hOldFont);
3642 SetBkMode(hdc, oldBkMode);
3643 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3644 SetTextColor(hdc, oldTextColor);
3645 infoPtr->bIsDrawing = FALSE;
3649 /***
3650 * DESCRIPTION:
3651 * Calculates the approximate width and height of a given number of items.
3653 * PARAMETER(S):
3654 * [I] infoPtr : valid pointer to the listview structure
3655 * [I] nItemCount : number of items
3656 * [I] wWidth : width
3657 * [I] wHeight : height
3659 * RETURN:
3660 * Returns a DWORD. The width in the low word and the height in high word.
3662 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3663 WORD wWidth, WORD wHeight)
3665 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3666 INT nItemCountPerColumn = 1;
3667 INT nColumnCount = 0;
3668 DWORD dwViewRect = 0;
3670 if (nItemCount == -1)
3671 nItemCount = infoPtr->nItemCount;
3673 if (uView == LVS_LIST)
3675 if (wHeight == 0xFFFF)
3677 /* use current height */
3678 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3681 if (wHeight < infoPtr->nItemHeight)
3682 wHeight = infoPtr->nItemHeight;
3684 if (nItemCount > 0)
3686 if (infoPtr->nItemHeight > 0)
3688 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3689 if (nItemCountPerColumn == 0)
3690 nItemCountPerColumn = 1;
3692 if (nItemCount % nItemCountPerColumn != 0)
3693 nColumnCount = nItemCount / nItemCountPerColumn;
3694 else
3695 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3699 /* Microsoft padding magic */
3700 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3701 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3703 dwViewRect = MAKELONG(wWidth, wHeight);
3705 else if (uView == LVS_REPORT)
3706 FIXME("uView == LVS_REPORT: not implemented\n");
3707 else if (uView == LVS_SMALLICON)
3708 FIXME("uView == LVS_SMALLICON: not implemented\n");
3709 else if (uView == LVS_ICON)
3710 FIXME("uView == LVS_ICON: not implemented\n");
3712 return dwViewRect;
3715 /***
3716 * DESCRIPTION:
3717 * Arranges listview items in icon display mode.
3719 * PARAMETER(S):
3720 * [I] infoPtr : valid pointer to the listview structure
3721 * [I] nAlignCode : alignment code
3723 * RETURN:
3724 * SUCCESS : TRUE
3725 * FAILURE : FALSE
3727 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3729 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3730 BOOL bResult = FALSE;
3732 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3734 switch (nAlignCode)
3736 case LVA_ALIGNLEFT:
3737 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3738 break;
3739 case LVA_ALIGNTOP:
3740 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3741 break;
3742 case LVA_DEFAULT:
3743 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3744 break;
3745 case LVA_SNAPTOGRID:
3746 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3747 break;
3751 return bResult;
3754 /* << LISTVIEW_CreateDragImage >> */
3757 /***
3758 * DESCRIPTION:
3759 * Removes all listview items and subitems.
3761 * PARAMETER(S):
3762 * [I] infoPtr : valid pointer to the listview structure
3764 * RETURN:
3765 * SUCCESS : TRUE
3766 * FAILURE : FALSE
3768 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3770 LONG lStyle = infoPtr->dwStyle;
3771 UINT uView = lStyle & LVS_TYPEMASK;
3772 LISTVIEW_ITEM *lpItem;
3773 LISTVIEW_SUBITEM *lpSubItem;
3774 NMLISTVIEW nmlv;
3775 BOOL bSuppress;
3776 BOOL bResult = FALSE;
3777 HDPA hdpaSubItems;
3779 TRACE("()\n");
3781 LISTVIEW_DeselectAll(infoPtr);
3782 infoPtr->nSelectionMark=-1;
3783 infoPtr->nFocusedItem=-1;
3784 SetRectEmpty(&infoPtr->rcFocus);
3785 /* But we are supposed to leave nHotItem as is! */
3787 if (lStyle & LVS_OWNERDATA)
3789 infoPtr->nItemCount = 0;
3790 LISTVIEW_InvalidateList(infoPtr);
3791 return TRUE;
3794 if (infoPtr->nItemCount > 0)
3796 INT i, j;
3798 /* send LVN_DELETEALLITEMS notification */
3799 /* verify if subsequent LVN_DELETEITEM notifications should be
3800 suppressed */
3801 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3802 nmlv.iItem = -1;
3803 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3805 for (i = 0; i < infoPtr->nItemCount; i++)
3807 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3808 if (hdpaSubItems != NULL)
3810 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3812 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3813 if (lpSubItem != NULL)
3815 /* free subitem string */
3816 if (is_textW(lpSubItem->hdr.pszText))
3817 COMCTL32_Free(lpSubItem->hdr.pszText);
3819 /* free subitem */
3820 COMCTL32_Free(lpSubItem);
3824 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3825 if (lpItem != NULL)
3827 if (!bSuppress)
3829 /* send LVN_DELETEITEM notification */
3830 nmlv.iItem = i;
3831 nmlv.lParam = lpItem->lParam;
3832 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3835 /* free item string */
3836 if (is_textW(lpItem->hdr.pszText))
3837 COMCTL32_Free(lpItem->hdr.pszText);
3839 /* free item */
3840 COMCTL32_Free(lpItem);
3843 DPA_Destroy(hdpaSubItems);
3847 /* reinitialize listview memory */
3848 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3849 infoPtr->nItemCount = 0;
3850 DPA_DeleteAllPtrs(infoPtr->hdpaPosX);
3851 DPA_DeleteAllPtrs(infoPtr->hdpaPosY);
3853 /* align items (set position of each item) */
3854 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3856 if (lStyle & LVS_ALIGNLEFT)
3858 LISTVIEW_AlignLeft(infoPtr);
3860 else
3862 LISTVIEW_AlignTop(infoPtr);
3866 LISTVIEW_UpdateScroll(infoPtr);
3868 LISTVIEW_InvalidateList(infoPtr);
3871 return bResult;
3874 /***
3875 * DESCRIPTION:
3876 * Scrolls, and updates the columns, when a column is changing width.
3878 * PARAMETER(S):
3879 * [I] infoPtr : valid pointer to the listview structure
3880 * [I] nColumn : column to scroll
3881 * [I] dx : amount of scroll, in pixels
3883 * RETURN:
3884 * SUCCESS : TRUE
3885 * FAILURE : FALSE
3887 static BOOL LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
3889 COLUMN_INFO *lpColumnInfo;
3890 RECT rcOld, rcCol;
3891 INT nCol;
3893 if ((lpColumnInfo = DPA_GetPtr(infoPtr->hdpaColumns, nColumn)))
3894 rcCol = lpColumnInfo->rcHeader;
3896 /* ajust the other columns */
3897 for (nCol = nColumn; (lpColumnInfo = DPA_GetPtr(infoPtr->hdpaColumns, nCol)); nCol++)
3899 lpColumnInfo->rcHeader.left += dx;
3900 lpColumnInfo->rcHeader.right += dx;
3903 /* do not update screen if not in report mode */
3904 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return TRUE;
3906 /* if we have a focus, must first erase the focus rect */
3907 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
3909 /* Need to reset the item width when inserting a new column */
3910 infoPtr->nItemWidth += dx;
3912 LISTVIEW_UpdateScroll(infoPtr);
3914 /* scroll to cover the deleted column, and invalidate for redraw */
3915 rcOld = infoPtr->rcList;
3916 rcOld.left = rcCol.left;
3917 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3919 /* we can restore focus now */
3920 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
3922 return TRUE;
3925 /***
3926 * DESCRIPTION:
3927 * Removes a column from the listview control.
3929 * PARAMETER(S):
3930 * [I] infoPtr : valid pointer to the listview structure
3931 * [I] nColumn : column index
3933 * RETURN:
3934 * SUCCESS : TRUE
3935 * FAILURE : FALSE
3937 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3939 RECT rcCol;
3941 TRACE("nColumn=%d\n", nColumn);
3943 if (nColumn <= 0) return FALSE;
3945 if (!LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol))
3946 return FALSE;
3948 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3949 return FALSE;
3951 COMCTL32_Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
3952 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
3954 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3956 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3957 HDPA hdpaSubItems;
3958 INT nItem, nSubItem, i;
3960 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3962 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3963 if (!hdpaSubItems) continue;
3964 nSubItem = 0;
3965 lpDelItem = 0;
3966 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3968 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3969 if (!lpSubItem) break;
3970 if (lpSubItem->iSubItem == nColumn)
3972 nSubItem = i;
3973 lpDelItem = lpSubItem;
3975 else if (lpSubItem->iSubItem > nColumn)
3977 lpSubItem->iSubItem--;
3981 /* if we found our subitem, zapp it */
3982 if (nSubItem > 0)
3984 /* free string */
3985 if (is_textW(lpDelItem->hdr.pszText))
3986 COMCTL32_Free(lpDelItem->hdr.pszText);
3988 /* free item */
3989 COMCTL32_Free(lpDelItem);
3991 /* free dpa memory */
3992 DPA_DeletePtr(hdpaSubItems, nSubItem);
3997 /* update the other column info */
3998 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4000 return TRUE;
4003 /***
4004 * DESCRIPTION:
4005 * Removes an item from the listview control.
4007 * PARAMETER(S):
4008 * [I] infoPtr : valid pointer to the listview structure
4009 * [I] nItem : item index
4011 * RETURN:
4012 * SUCCESS : TRUE
4013 * FAILURE : FALSE
4015 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4017 LONG lStyle = infoPtr->dwStyle;
4018 UINT uView = lStyle & LVS_TYPEMASK;
4019 NMLISTVIEW nmlv;
4020 BOOL bResult = FALSE;
4021 HDPA hdpaSubItems;
4022 LISTVIEW_ITEM *lpItem;
4023 LISTVIEW_SUBITEM *lpSubItem;
4024 LVITEMW item;
4025 INT i;
4027 TRACE("(nItem=%d)\n", nItem);
4029 /* remove selection, and focus */
4030 item.state = 0;
4031 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4032 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4034 /* send LVN_DELETEITEM notification. */
4035 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
4036 nmlv.iItem = nItem;
4037 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
4039 if (lStyle & LVS_OWNERDATA)
4041 infoPtr->nItemCount--;
4042 LISTVIEW_InvalidateList(infoPtr); /*FIXME: optimize */
4043 return TRUE;
4046 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
4048 /* initialize memory */
4049 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4051 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4052 if (hdpaSubItems != NULL)
4054 infoPtr->nItemCount--;
4055 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4057 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4058 if (lpSubItem != NULL)
4060 /* free item string */
4061 if (is_textW(lpSubItem->hdr.pszText))
4062 COMCTL32_Free(lpSubItem->hdr.pszText);
4064 /* free item */
4065 COMCTL32_Free(lpSubItem);
4069 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4070 if (lpItem != NULL)
4072 /* free item string */
4073 if (is_textW(lpItem->hdr.pszText))
4074 COMCTL32_Free(lpItem->hdr.pszText);
4076 /* free item */
4077 COMCTL32_Free(lpItem);
4080 bResult = DPA_Destroy(hdpaSubItems);
4081 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4082 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4085 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4087 /* align items (set position of each item) */
4088 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4090 if (lStyle & LVS_ALIGNLEFT)
4091 LISTVIEW_AlignLeft(infoPtr);
4092 else
4093 LISTVIEW_AlignTop(infoPtr);
4096 LISTVIEW_UpdateScroll(infoPtr);
4098 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
4101 return bResult;
4105 /***
4106 * DESCRIPTION:
4107 * Callback implementation for editlabel control
4109 * PARAMETER(S):
4110 * [I] infoPtr : valid pointer to the listview structure
4111 * [I] pszText : modified text
4112 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4114 * RETURN:
4115 * SUCCESS : TRUE
4116 * FAILURE : FALSE
4118 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4120 NMLVDISPINFOW dispInfo;
4122 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4124 ZeroMemory(&dispInfo, sizeof(dispInfo));
4125 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4126 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4127 dispInfo.item.iSubItem = 0;
4128 dispInfo.item.stateMask = ~0;
4129 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4130 /* add the text from the edit in */
4131 dispInfo.item.mask |= LVIF_TEXT;
4132 dispInfo.item.pszText = pszText;
4133 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4135 /* Do we need to update the Item Text */
4136 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4137 if (!pszText) return TRUE;
4139 ZeroMemory(&dispInfo, sizeof(dispInfo));
4140 dispInfo.item.mask = LVIF_TEXT;
4141 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4142 dispInfo.item.iSubItem = 0;
4143 dispInfo.item.pszText = pszText;
4144 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4145 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4148 /***
4149 * DESCRIPTION:
4150 * Begin in place editing of specified list view item
4152 * PARAMETER(S):
4153 * [I] infoPtr : valid pointer to the listview structure
4154 * [I] nItem : item index
4155 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4157 * RETURN:
4158 * SUCCESS : TRUE
4159 * FAILURE : FALSE
4161 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4163 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4164 NMLVDISPINFOW dispInfo;
4165 RECT rect;
4167 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4169 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4170 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4172 infoPtr->nEditLabelItem = nItem;
4174 /* Is the EditBox still there, if so remove it */
4175 if(infoPtr->hwndEdit != 0)
4177 SetFocus(infoPtr->hwndSelf);
4178 infoPtr->hwndEdit = 0;
4181 LISTVIEW_SetSelection(infoPtr, nItem);
4182 LISTVIEW_SetItemFocus(infoPtr, nItem);
4184 rect.left = LVIR_LABEL;
4185 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4187 ZeroMemory(&dispInfo, sizeof(dispInfo));
4188 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4189 dispInfo.item.iItem = nItem;
4190 dispInfo.item.iSubItem = 0;
4191 dispInfo.item.stateMask = ~0;
4192 dispInfo.item.pszText = szDispText;
4193 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4194 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4196 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4197 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4198 if (!infoPtr->hwndEdit) return 0;
4200 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4202 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4203 infoPtr->hwndEdit = 0;
4204 return 0;
4207 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4208 SetFocus(infoPtr->hwndEdit);
4209 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4210 return infoPtr->hwndEdit;
4214 /***
4215 * DESCRIPTION:
4216 * Ensures the specified item is visible, scrolling into view if necessary.
4218 * PARAMETER(S):
4219 * [I] infoPtr : valid pointer to the listview structure
4220 * [I] nItem : item index
4221 * [I] bPartial : partially or entirely visible
4223 * RETURN:
4224 * SUCCESS : TRUE
4225 * FAILURE : FALSE
4227 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4229 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4230 INT nScrollPosHeight = 0;
4231 INT nScrollPosWidth = 0;
4232 INT nHorzAdjust = 0;
4233 INT nVertAdjust = 0;
4234 INT nHorzDiff = 0;
4235 INT nVertDiff = 0;
4236 RECT rcItem, rcTemp;
4238 rcItem.left = LVIR_BOUNDS;
4239 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4241 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4243 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4245 /* scroll left/right, but in LVS_REPORT mode */
4246 if (uView == LVS_LIST)
4247 nScrollPosWidth = infoPtr->nItemWidth;
4248 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4249 nScrollPosWidth = 1;
4251 if (rcItem.left < infoPtr->rcList.left)
4253 nHorzAdjust = -1;
4254 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4256 else
4258 nHorzAdjust = 1;
4259 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4263 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4265 /* scroll up/down, but not in LVS_LIST mode */
4266 if (uView == LVS_REPORT)
4267 nScrollPosHeight = infoPtr->nItemHeight;
4268 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4269 nScrollPosHeight = 1;
4271 if (rcItem.top < infoPtr->rcList.top)
4273 nVertAdjust = -1;
4274 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4276 else
4278 nVertAdjust = 1;
4279 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4283 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4285 if (nScrollPosWidth)
4287 INT diff = nHorzDiff / nScrollPosWidth;
4288 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4289 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4292 if (nScrollPosHeight)
4294 INT diff = nVertDiff / nScrollPosHeight;
4295 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4296 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4299 return TRUE;
4302 /***
4303 * DESCRIPTION:
4304 * Searches for an item with specific characteristics.
4306 * PARAMETER(S):
4307 * [I] hwnd : window handle
4308 * [I] nStart : base item index
4309 * [I] lpFindInfo : item information to look for
4311 * RETURN:
4312 * SUCCESS : index of item
4313 * FAILURE : -1
4315 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4316 LPLVFINDINFOW lpFindInfo)
4318 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4319 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4320 BOOL bWrap = FALSE, bNearest = FALSE;
4321 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4322 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4323 POINT Position, Destination;
4324 LVITEMW lvItem;
4326 if (!lpFindInfo || nItem < 0) return -1;
4328 lvItem.mask = 0;
4329 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4331 lvItem.mask |= LVIF_TEXT;
4332 lvItem.pszText = szDispText;
4333 lvItem.cchTextMax = DISP_TEXT_SIZE;
4336 if (lpFindInfo->flags & LVFI_WRAP)
4337 bWrap = TRUE;
4339 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4340 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4342 POINT Origin;
4344 FIXME("LVFI_NEARESTXY is slow.\n");
4345 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
4346 Destination.x = lpFindInfo->pt.x - Origin.x;
4347 Destination.y = lpFindInfo->pt.y - Origin.y;
4348 switch(lpFindInfo->vkDirection)
4350 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4351 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4352 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4353 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4354 case VK_HOME: Destination.x = Destination.y = 0; break;
4355 case VK_END: Destination.x = infoPtr->rcView.right; Destination.y = infoPtr->rcView.bottom; break;
4356 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4357 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4358 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4360 bNearest = TRUE;
4363 /* if LVFI_PARAM is specified, all other flags are ignored */
4364 if (lpFindInfo->flags & LVFI_PARAM)
4366 lvItem.mask |= LVIF_PARAM;
4367 bNearest = FALSE;
4368 lvItem.mask &= ~LVIF_TEXT;
4371 again:
4372 for (; nItem < nLast; nItem++)
4374 lvItem.iItem = nItem;
4375 lvItem.iSubItem = 0;
4376 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4378 if (lvItem.mask & LVIF_PARAM && lpFindInfo->lParam == lvItem.lParam)
4379 return nItem;
4381 if (lvItem.mask & LVIF_TEXT)
4383 if (lpFindInfo->flags & LVFI_PARTIAL)
4385 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4387 else
4389 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4393 if (!bNearest) return nItem;
4395 /* This is very inefficient. To do a good job here,
4396 * we need a sorted array of (x,y) item positions */
4397 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) continue;
4399 /* compute the distance^2 to the destination */
4400 xdist = Destination.x - Position.x;
4401 ydist = Destination.y - Position.y;
4402 dist = xdist * xdist + ydist * ydist;
4404 /* remember the distance, and item if it's closer */
4405 if (dist < mindist)
4407 mindist = dist;
4408 nNearestItem = nItem;
4412 if (bWrap)
4414 nItem = 0;
4415 nLast = min(nStart + 1, infoPtr->nItemCount);
4416 bWrap = FALSE;
4417 goto again;
4420 return nNearestItem;
4423 /***
4424 * DESCRIPTION:
4425 * Searches for an item with specific characteristics.
4427 * PARAMETER(S):
4428 * [I] hwnd : window handle
4429 * [I] nStart : base item index
4430 * [I] lpFindInfo : item information to look for
4432 * RETURN:
4433 * SUCCESS : index of item
4434 * FAILURE : -1
4436 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4437 LPLVFINDINFOA lpFindInfo)
4439 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4440 LVFINDINFOW fiw;
4441 LRESULT res;
4443 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4444 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4445 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4446 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4447 return res;
4450 /***
4451 * DESCRIPTION:
4452 * Retrieves the background image of the listview control.
4454 * PARAMETER(S):
4455 * [I] infoPtr : valid pointer to the listview structure
4456 * [O] lpBkImage : background image attributes
4458 * RETURN:
4459 * SUCCESS : TRUE
4460 * FAILURE : FALSE
4462 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4463 /* { */
4464 /* FIXME (listview, "empty stub!\n"); */
4465 /* return FALSE; */
4466 /* } */
4468 /***
4469 * DESCRIPTION:
4470 * Retrieves column attributes.
4472 * PARAMETER(S):
4473 * [I] infoPtr : valid pointer to the listview structure
4474 * [I] nItem : column index
4475 * [IO] lpColumn : column information
4476 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4477 * otherwise it is in fact a LPLVCOLUMNA
4479 * RETURN:
4480 * SUCCESS : TRUE
4481 * FAILURE : FALSE
4483 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4485 HDITEMW hdi;
4486 BOOL bResult = FALSE;
4488 if (lpColumn != NULL)
4491 /* initialize memory */
4492 ZeroMemory(&hdi, sizeof(hdi));
4494 if (lpColumn->mask & LVCF_FMT)
4495 hdi.mask |= HDI_FORMAT;
4497 if (lpColumn->mask & LVCF_WIDTH)
4498 hdi.mask |= HDI_WIDTH;
4500 if (lpColumn->mask & LVCF_TEXT)
4502 hdi.mask |= HDI_TEXT;
4503 hdi.cchTextMax = lpColumn->cchTextMax;
4504 hdi.pszText = lpColumn->pszText;
4507 if (lpColumn->mask & LVCF_IMAGE)
4508 hdi.mask |= HDI_IMAGE;
4510 if (lpColumn->mask & LVCF_ORDER)
4511 hdi.mask |= HDI_ORDER;
4513 if (isW)
4514 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4515 else
4516 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4518 if (bResult)
4520 if (lpColumn->mask & LVCF_FMT)
4522 lpColumn->fmt = 0;
4524 if (hdi.fmt & HDF_LEFT)
4525 lpColumn->fmt |= LVCFMT_LEFT;
4526 else if (hdi.fmt & HDF_RIGHT)
4527 lpColumn->fmt |= LVCFMT_RIGHT;
4528 else if (hdi.fmt & HDF_CENTER)
4529 lpColumn->fmt |= LVCFMT_CENTER;
4531 if (hdi.fmt & HDF_IMAGE)
4532 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4534 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4535 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4538 if (lpColumn->mask & LVCF_WIDTH)
4539 lpColumn->cx = hdi.cxy;
4541 if (lpColumn->mask & LVCF_IMAGE)
4542 lpColumn->iImage = hdi.iImage;
4544 if (lpColumn->mask & LVCF_ORDER)
4545 lpColumn->iOrder = hdi.iOrder;
4547 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4548 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4553 return bResult;
4557 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4559 INT i;
4561 if (!lpiArray)
4562 return FALSE;
4564 /* FIXME: little hack */
4565 for (i = 0; i < iCount; i++)
4566 lpiArray[i] = i;
4568 return TRUE;
4571 /***
4572 * DESCRIPTION:
4573 * Retrieves the column width.
4575 * PARAMETER(S):
4576 * [I] infoPtr : valid pointer to the listview structure
4577 * [I] int : column index
4579 * RETURN:
4580 * SUCCESS : column width
4581 * FAILURE : zero
4583 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4585 INT nColumnWidth = 0;
4586 RECT rcHeader;
4588 TRACE("nColumn=%d\n", nColumn);
4590 /* we have a 'column' in LIST and REPORT mode only */
4591 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4593 case LVS_LIST:
4594 nColumnWidth = infoPtr->nItemWidth;
4595 break;
4596 case LVS_REPORT:
4597 if (LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader))
4598 nColumnWidth = rcHeader.right - rcHeader.left;
4599 break;
4602 TRACE("nColumnWidth=%d\n", nColumnWidth);
4603 return nColumnWidth;
4606 /***
4607 * DESCRIPTION:
4608 * In list or report display mode, retrieves the number of items that can fit
4609 * vertically in the visible area. In icon or small icon display mode,
4610 * retrieves the total number of visible items.
4612 * PARAMETER(S):
4613 * [I] infoPtr : valid pointer to the listview structure
4615 * RETURN:
4616 * Number of fully visible items.
4618 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4620 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4621 INT nItemCount = 0;
4623 if (uView == LVS_LIST)
4625 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4627 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4628 LISTVIEW_GetCountPerColumn(infoPtr);
4631 else if (uView == LVS_REPORT)
4633 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4635 else
4637 nItemCount = infoPtr->nItemCount;
4640 return nItemCount;
4644 /***
4645 * DESCRIPTION:
4646 * Retrieves an image list handle.
4648 * PARAMETER(S):
4649 * [I] infoPtr : valid pointer to the listview structure
4650 * [I] nImageList : image list identifier
4652 * RETURN:
4653 * SUCCESS : image list handle
4654 * FAILURE : NULL
4656 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4658 HIMAGELIST himl = NULL;
4660 switch (nImageList)
4662 case LVSIL_NORMAL:
4663 himl = infoPtr->himlNormal;
4664 break;
4665 case LVSIL_SMALL:
4666 himl = infoPtr->himlSmall;
4667 break;
4668 case LVSIL_STATE:
4669 himl = infoPtr->himlState;
4670 break;
4673 return (LRESULT)himl;
4676 /* LISTVIEW_GetISearchString */
4678 /***
4679 * DESCRIPTION:
4680 * Retrieves item attributes.
4682 * PARAMETER(S):
4683 * [I] hwnd : window handle
4684 * [IO] lpLVItem : item info
4685 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4686 * if FALSE, the lpLVItem is a LPLVITEMA.
4688 * NOTE:
4689 * This is the internal 'GetItem' interface -- it tries to
4690 * be smart, and avoids text copies, if possible, by modifing
4691 * lpLVItem->pszText to point to the text string. Please note
4692 * that this is not always possible (e.g. OWNERDATA), so on
4693 * entry you *must* supply valid values for pszText, and cchTextMax.
4694 * The only difference to the documented interface is that upon
4695 * return, you should use *only* the lpLVItem->pszText, rather than
4696 * the buffer pointer you provided on input. Most code already does
4697 * that, so it's not a problem.
4698 * For the two cases when the text must be copied (that is,
4699 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4701 * RETURN:
4702 * SUCCESS : TRUE
4703 * FAILURE : FALSE
4705 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4707 NMLVDISPINFOW dispInfo;
4708 LISTVIEW_ITEM *lpItem;
4709 ITEMHDR* pItemHdr;
4710 HDPA hdpaSubItems;
4712 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4714 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4715 return FALSE;
4717 if (lpLVItem->mask == 0) return TRUE;
4719 /* a quick optimization if all we're asked is the focus state
4720 * these queries are worth optimising since they are common,
4721 * and can be answered in constant time, without the heavy accesses */
4722 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
4723 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4725 lpLVItem->state = 0;
4726 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4727 lpLVItem->state |= LVIS_FOCUSED;
4728 return TRUE;
4731 ZeroMemory(&dispInfo, sizeof(dispInfo));
4733 /* if the app stores all the data, handle it separately */
4734 if (infoPtr->dwStyle & LVS_OWNERDATA)
4736 dispInfo.item.state = 0;
4738 /* if we need to callback, do it now */
4739 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4741 /* NOTE: copy only fields which we _know_ are initialized, some apps
4742 * depend on the uninitialized fields being 0 */
4743 dispInfo.item.mask = lpLVItem->mask;
4744 dispInfo.item.iItem = lpLVItem->iItem;
4745 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4746 if (lpLVItem->mask & LVIF_TEXT)
4748 dispInfo.item.pszText = lpLVItem->pszText;
4749 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4751 if (lpLVItem->mask & LVIF_STATE)
4752 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4753 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4754 dispInfo.item.stateMask = lpLVItem->stateMask;
4755 *lpLVItem = dispInfo.item;
4756 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4759 /* we store only a little state, so if we're not asked, we're done */
4760 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4762 /* if focus is handled by us, report it */
4763 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4765 lpLVItem->state &= ~LVIS_FOCUSED;
4766 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4767 lpLVItem->state |= LVIS_FOCUSED;
4770 /* and do the same for selection, if we handle it */
4771 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4773 lpLVItem->state &= ~LVIS_SELECTED;
4774 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4775 lpLVItem->state |= LVIS_SELECTED;
4778 return TRUE;
4781 /* find the item and subitem structures before we proceed */
4782 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4783 if (hdpaSubItems == NULL) return FALSE;
4785 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4786 return FALSE;
4788 if (lpLVItem->iSubItem)
4790 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4791 if(!lpSubItem) return FALSE;
4792 pItemHdr = &lpSubItem->hdr;
4794 else
4795 pItemHdr = &lpItem->hdr;
4797 /* Do we need to query the state from the app? */
4798 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4800 dispInfo.item.mask |= LVIF_STATE;
4801 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4804 /* Do we need to enquire about the image? */
4805 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK)
4806 dispInfo.item.mask |= LVIF_IMAGE;
4808 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
4809 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4811 dispInfo.item.mask |= LVIF_TEXT;
4812 dispInfo.item.pszText = lpLVItem->pszText;
4813 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4814 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4815 *dispInfo.item.pszText = '\0';
4818 /* If we don't have all the requested info, query the application */
4819 if (dispInfo.item.mask != 0)
4821 dispInfo.item.iItem = lpLVItem->iItem;
4822 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4823 dispInfo.item.lParam = lpItem->lParam;
4824 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4825 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4828 /* Now, handle the iImage field */
4829 if (dispInfo.item.mask & LVIF_IMAGE)
4831 lpLVItem->iImage = dispInfo.item.iImage;
4832 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
4833 pItemHdr->iImage = dispInfo.item.iImage;
4835 else if (lpLVItem->mask & LVIF_IMAGE)
4836 lpLVItem->iImage = pItemHdr->iImage;
4838 /* The pszText field */
4839 if (dispInfo.item.mask & LVIF_TEXT)
4841 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4842 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4844 lpLVItem->pszText = dispInfo.item.pszText;
4846 else if (lpLVItem->mask & LVIF_TEXT)
4848 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4849 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4852 /* if this is a subitem, we're done */
4853 if (lpLVItem->iSubItem) return TRUE;
4855 /* Next is the lParam field */
4856 if (dispInfo.item.mask & LVIF_PARAM)
4858 lpLVItem->lParam = dispInfo.item.lParam;
4859 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4860 lpItem->lParam = dispInfo.item.lParam;
4862 else if (lpLVItem->mask & LVIF_PARAM)
4863 lpLVItem->lParam = lpItem->lParam;
4865 /* ... the state field (this one is different due to uCallbackmask) */
4866 if (lpLVItem->mask & LVIF_STATE)
4868 lpLVItem->state = lpItem->state;
4869 if (dispInfo.item.mask & LVIF_STATE)
4871 lpLVItem->state &= ~dispInfo.item.stateMask;
4872 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4874 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4876 lpLVItem->state &= ~LVIS_FOCUSED;
4877 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4878 lpLVItem->state |= LVIS_FOCUSED;
4880 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4882 lpLVItem->state &= ~LVIS_SELECTED;
4883 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4884 lpLVItem->state |= LVIS_SELECTED;
4888 /* and last, but not least, the indent field */
4889 if (lpLVItem->mask & LVIF_INDENT)
4890 lpLVItem->iIndent = lpItem->iIndent;
4892 return TRUE;
4895 /***
4896 * DESCRIPTION:
4897 * Retrieves item attributes.
4899 * PARAMETER(S):
4900 * [I] hwnd : window handle
4901 * [IO] lpLVItem : item info
4902 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4903 * if FALSE, the lpLVItem is a LPLVITEMA.
4905 * NOTE:
4906 * This is the external 'GetItem' interface -- it properly copies
4907 * the text in the provided buffer.
4909 * RETURN:
4910 * SUCCESS : TRUE
4911 * FAILURE : FALSE
4913 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4915 LPWSTR pszText;
4916 BOOL bResult;
4918 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4919 return FALSE;
4921 pszText = lpLVItem->pszText;
4922 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4923 if (bResult && lpLVItem->pszText != pszText)
4924 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4925 lpLVItem->pszText = pszText;
4927 return bResult;
4931 /***
4932 * DESCRIPTION:
4933 * Retrieves the position (upper-left) of the listview control item.
4934 * Note that for LVS_ICON style, the upper-left is that of the icon
4935 * and not the bounding box.
4937 * PARAMETER(S):
4938 * [I] infoPtr : valid pointer to the listview structure
4939 * [I] nItem : item index
4940 * [O] lpptPosition : coordinate information
4942 * RETURN:
4943 * SUCCESS : TRUE
4944 * FAILURE : FALSE
4946 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4948 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4949 POINT Origin;
4951 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4953 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4954 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition)) return FALSE;
4955 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
4957 if (uView == LVS_ICON)
4959 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
4960 lpptPosition->y += ICON_TOP_PADDING;
4962 lpptPosition->x += Origin.x;
4963 lpptPosition->y += Origin.y;
4965 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
4966 return TRUE;
4970 /***
4971 * DESCRIPTION:
4972 * Retrieves the bounding rectangle for a listview control item.
4974 * PARAMETER(S):
4975 * [I] infoPtr : valid pointer to the listview structure
4976 * [I] nItem : item index
4977 * [IO] lprc : bounding rectangle coordinates
4978 * lprc->left specifies the portion of the item for which the bounding
4979 * rectangle will be retrieved.
4981 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
4982 * including the icon and label.
4984 * * For LVS_ICON
4985 * * Experiment shows that native control returns:
4986 * * width = min (48, length of text line)
4987 * * .left = position.x - (width - iconsize.cx)/2
4988 * * .right = .left + width
4989 * * height = #lines of text * ntmHeight + icon height + 8
4990 * * .top = position.y - 2
4991 * * .bottom = .top + height
4992 * * separation between items .y = itemSpacing.cy - height
4993 * * .x = itemSpacing.cx - width
4994 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
4996 * * For LVS_ICON
4997 * * Experiment shows that native control returns:
4998 * * width = iconSize.cx + 16
4999 * * .left = position.x - (width - iconsize.cx)/2
5000 * * .right = .left + width
5001 * * height = iconSize.cy + 4
5002 * * .top = position.y - 2
5003 * * .bottom = .top + height
5004 * * separation between items .y = itemSpacing.cy - height
5005 * * .x = itemSpacing.cx - width
5006 * LVIR_LABEL Returns the bounding rectangle of the item text.
5008 * * For LVS_ICON
5009 * * Experiment shows that native control returns:
5010 * * width = text length
5011 * * .left = position.x - width/2
5012 * * .right = .left + width
5013 * * height = ntmH * linecount + 2
5014 * * .top = position.y + iconSize.cy + 6
5015 * * .bottom = .top + height
5016 * * separation between items .y = itemSpacing.cy - height
5017 * * .x = itemSpacing.cx - width
5018 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5019 * rectangles, but excludes columns in report view.
5021 * RETURN:
5022 * SUCCESS : TRUE
5023 * FAILURE : FALSE
5025 * NOTES
5026 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5027 * upon whether the window has the focus currently and on whether the item
5028 * is the one with the focus. Ensure that the control's record of which
5029 * item has the focus agrees with the items' records.
5031 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5033 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5034 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5035 BOOL doLabel = TRUE, oversizedBox = FALSE;
5036 POINT Position, Origin;
5037 LVITEMW lvItem;
5038 RECT label_rect;
5040 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5042 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5043 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
5044 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) return FALSE;
5046 /* Be smart and try to figure out the minimum we have to do */
5047 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5048 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5049 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5050 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5051 oversizedBox = TRUE;
5053 /* get what we need from the item before hand, so we make
5054 * only one request. This can speed up things, if data
5055 * is stored on the app side */
5056 lvItem.mask = 0;
5057 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5058 if (doLabel) lvItem.mask |= LVIF_TEXT;
5059 lvItem.iItem = nItem;
5060 lvItem.iSubItem = 0;
5061 lvItem.pszText = szDispText;
5062 lvItem.cchTextMax = DISP_TEXT_SIZE;
5063 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5064 /* we got the state already up, simulate it here, to avoid a reget */
5065 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5067 lvItem.mask |= LVIF_STATE;
5068 lvItem.stateMask = LVIS_FOCUSED;
5069 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5072 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5073 lprc->left = LVIR_BOUNDS;
5074 switch(lprc->left)
5076 case LVIR_ICON:
5077 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL)) return FALSE;
5078 break;
5080 case LVIR_LABEL:
5081 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc)) return FALSE;
5082 break;
5084 case LVIR_BOUNDS:
5085 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL)) return FALSE;
5086 break;
5088 case LVIR_SELECTBOUNDS:
5089 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect)) return FALSE;
5090 UnionRect(lprc, lprc, &label_rect);
5091 break;
5093 default:
5094 WARN("Unknown value: %d\n", lprc->left);
5095 return FALSE;
5098 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5100 TRACE(" rect=%s\n", debugrect(lprc));
5102 return TRUE;
5105 /***
5106 * DESCRIPTION:
5107 * Retrieves the spacing between listview control items.
5109 * PARAMETER(S):
5110 * [I] infoPtr : valid pointer to the listview structure
5111 * [IO] lprc : rectangle to receive the output
5112 * on input, lprc->top = nSubItem
5113 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5115 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5116 * not only those of the first column.
5117 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5119 * RETURN:
5120 * TRUE: success
5121 * FALSE: failure
5123 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5125 POINT Position, Origin;
5126 LVITEMW lvItem;
5128 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5130 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5132 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
5133 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5135 lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5136 lvItem.iItem = nItem;
5137 lvItem.iSubItem = lprc->top;
5139 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5140 switch(lprc->left)
5142 case LVIR_ICON:
5143 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL)) return FALSE;
5144 break;
5146 case LVIR_LABEL:
5147 case LVIR_BOUNDS:
5148 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL)) return FALSE;
5149 break;
5151 default:
5152 ERR("Unknown bounds=%d\n", lprc->left);
5153 return FALSE;
5156 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5157 return TRUE;
5161 /***
5162 * DESCRIPTION:
5163 * Retrieves the width of a label.
5165 * PARAMETER(S):
5166 * [I] infoPtr : valid pointer to the listview structure
5168 * RETURN:
5169 * SUCCESS : string width (in pixels)
5170 * FAILURE : zero
5172 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5174 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5175 LVITEMW lvItem;
5177 TRACE("(nItem=%d)\n", nItem);
5179 lvItem.mask = LVIF_TEXT;
5180 lvItem.iItem = nItem;
5181 lvItem.iSubItem = 0;
5182 lvItem.pszText = szDispText;
5183 lvItem.cchTextMax = DISP_TEXT_SIZE;
5184 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5186 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5189 /***
5190 * DESCRIPTION:
5191 * Retrieves the spacing between listview control items.
5193 * PARAMETER(S):
5194 * [I] infoPtr : valid pointer to the listview structure
5195 * [I] bSmall : flag for small or large icon
5197 * RETURN:
5198 * Horizontal + vertical spacing
5200 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5202 LONG lResult;
5204 if (!bSmall)
5206 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5208 else
5210 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5211 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5212 else
5213 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5215 return lResult;
5218 /***
5219 * DESCRIPTION:
5220 * Retrieves the state of a listview control item.
5222 * PARAMETER(S):
5223 * [I] infoPtr : valid pointer to the listview structure
5224 * [I] nItem : item index
5225 * [I] uMask : state mask
5227 * RETURN:
5228 * State specified by the mask.
5230 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5232 LVITEMW lvItem;
5234 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5236 lvItem.iItem = nItem;
5237 lvItem.iSubItem = 0;
5238 lvItem.mask = LVIF_STATE;
5239 lvItem.stateMask = uMask;
5240 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5242 return lvItem.state & uMask;
5245 /***
5246 * DESCRIPTION:
5247 * Retrieves the text of a listview control item or subitem.
5249 * PARAMETER(S):
5250 * [I] hwnd : window handle
5251 * [I] nItem : item index
5252 * [IO] lpLVItem : item information
5253 * [I] isW : TRUE if lpLVItem is Unicode
5255 * RETURN:
5256 * SUCCESS : string length
5257 * FAILURE : 0
5259 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5261 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5263 lpLVItem->mask = LVIF_TEXT;
5264 lpLVItem->iItem = nItem;
5265 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5267 return textlenT(lpLVItem->pszText, isW);
5270 /***
5271 * DESCRIPTION:
5272 * Searches for an item based on properties + relationships.
5274 * PARAMETER(S):
5275 * [I] infoPtr : valid pointer to the listview structure
5276 * [I] nItem : item index
5277 * [I] uFlags : relationship flag
5279 * FIXME:
5280 * This function is very, very inefficient! Needs work.
5282 * RETURN:
5283 * SUCCESS : item index
5284 * FAILURE : -1
5286 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5288 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5289 UINT uMask = 0;
5290 LVFINDINFOW lvFindInfo;
5291 INT nCountPerColumn;
5292 INT i;
5294 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5295 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5297 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5299 if (uFlags & LVNI_CUT)
5300 uMask |= LVIS_CUT;
5302 if (uFlags & LVNI_DROPHILITED)
5303 uMask |= LVIS_DROPHILITED;
5305 if (uFlags & LVNI_FOCUSED)
5306 uMask |= LVIS_FOCUSED;
5308 if (uFlags & LVNI_SELECTED)
5309 uMask |= LVIS_SELECTED;
5311 /* if we're asked for the focused item, that's only one,
5312 * so it's worth optimizing */
5313 if (uFlags & LVNI_FOCUSED)
5315 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5316 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5319 if (uFlags & LVNI_ABOVE)
5321 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5323 while (nItem >= 0)
5325 nItem--;
5326 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5327 return nItem;
5330 else
5332 lvFindInfo.flags = LVFI_NEARESTXY;
5333 lvFindInfo.vkDirection = VK_UP;
5334 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5335 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5337 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5338 return nItem;
5342 else if (uFlags & LVNI_BELOW)
5344 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5346 while (nItem < infoPtr->nItemCount)
5348 nItem++;
5349 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5350 return nItem;
5353 else
5355 lvFindInfo.flags = LVFI_NEARESTXY;
5356 lvFindInfo.vkDirection = VK_DOWN;
5357 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5358 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5360 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5361 return nItem;
5365 else if (uFlags & LVNI_TOLEFT)
5367 if (uView == LVS_LIST)
5369 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5370 while (nItem - nCountPerColumn >= 0)
5372 nItem -= nCountPerColumn;
5373 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5374 return nItem;
5377 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5379 lvFindInfo.flags = LVFI_NEARESTXY;
5380 lvFindInfo.vkDirection = VK_LEFT;
5381 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5382 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5384 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5385 return nItem;
5389 else if (uFlags & LVNI_TORIGHT)
5391 if (uView == LVS_LIST)
5393 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5394 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5396 nItem += nCountPerColumn;
5397 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5398 return nItem;
5401 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5403 lvFindInfo.flags = LVFI_NEARESTXY;
5404 lvFindInfo.vkDirection = VK_RIGHT;
5405 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5406 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5408 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5409 return nItem;
5413 else
5415 nItem++;
5417 /* search by index */
5418 for (i = nItem; i < infoPtr->nItemCount; i++)
5420 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5421 return i;
5425 return -1;
5428 /* LISTVIEW_GetNumberOfWorkAreas */
5430 /***
5431 * DESCRIPTION:
5432 * Retrieves the origin coordinates when in icon or small icon display mode.
5434 * PARAMETER(S):
5435 * [I] infoPtr : valid pointer to the listview structure
5436 * [O] lpptOrigin : coordinate information
5438 * RETURN:
5439 * SUCCESS : TRUE
5440 * FAILURE : FALSE
5442 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5444 DWORD lStyle = infoPtr->dwStyle;
5445 UINT uView = lStyle & LVS_TYPEMASK;
5446 INT nHorzPos = 0, nVertPos = 0;
5447 SCROLLINFO scrollInfo;
5449 if (!lpptOrigin) return FALSE;
5451 scrollInfo.cbSize = sizeof(SCROLLINFO);
5452 scrollInfo.fMask = SIF_POS;
5454 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5455 nHorzPos = scrollInfo.nPos;
5456 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5457 nVertPos = scrollInfo.nPos;
5459 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5461 lpptOrigin->x = infoPtr->rcList.left;
5462 lpptOrigin->y = infoPtr->rcList.top;
5463 if (uView == LVS_LIST)
5464 nHorzPos *= infoPtr->nItemWidth;
5465 else if (uView == LVS_REPORT)
5466 nVertPos *= infoPtr->nItemHeight;
5468 lpptOrigin->x -= nHorzPos;
5469 lpptOrigin->y -= nVertPos;
5471 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5473 return TRUE;
5476 /***
5477 * DESCRIPTION:
5478 * Retrieves the width of a string.
5480 * PARAMETER(S):
5481 * [I] hwnd : window handle
5482 * [I] lpszText : text string to process
5483 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5485 * RETURN:
5486 * SUCCESS : string width (in pixels)
5487 * FAILURE : zero
5489 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5491 SIZE stringSize;
5493 stringSize.cx = 0;
5494 if (is_textT(lpszText, isW))
5496 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5497 HDC hdc = GetDC(infoPtr->hwndSelf);
5498 HFONT hOldFont = SelectObject(hdc, hFont);
5500 if (isW)
5501 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5502 else
5503 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5504 SelectObject(hdc, hOldFont);
5505 ReleaseDC(infoPtr->hwndSelf, hdc);
5507 return stringSize.cx;
5510 /***
5511 * DESCRIPTION:
5512 * Determines which listview item is located at the specified position.
5514 * PARAMETER(S):
5515 * [I] infoPtr : valid pointer to the listview structure
5516 * [IO] lpht : hit test information
5517 * [I] subitem : fill out iSubItem.
5518 * [I] select : return the index only if the hit selects the item
5520 * NOTE:
5521 * (mm 20001022): We must not allow iSubItem to be touched, for
5522 * an app might pass only a structure with space up to iItem!
5523 * (MS Office 97 does that for instance in the file open dialog)
5525 * RETURN:
5526 * SUCCESS : item index
5527 * FAILURE : -1
5529 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5531 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5532 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5533 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5534 POINT Origin, Position, opt;
5535 LVITEMW lvItem;
5536 ITERATOR i;
5538 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5540 lpht->flags = 0;
5541 lpht->iItem = -1;
5542 if (subitem) lpht->iSubItem = 0;
5544 if (infoPtr->rcList.left > lpht->pt.x)
5545 lpht->flags |= LVHT_TOLEFT;
5546 else if (infoPtr->rcList.right < lpht->pt.x)
5547 lpht->flags |= LVHT_TORIGHT;
5549 if (infoPtr->rcList.top > lpht->pt.y)
5550 lpht->flags |= LVHT_ABOVE;
5551 else if (infoPtr->rcList.bottom < lpht->pt.y)
5552 lpht->flags |= LVHT_BELOW;
5554 TRACE("lpht->flags=0x%x\n", lpht->flags);
5555 if (lpht->flags) return -1;
5557 lpht->flags |= LVHT_NOWHERE;
5559 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
5561 /* first deal with the large items */
5562 rcSearch.left = lpht->pt.x;
5563 rcSearch.top = lpht->pt.y;
5564 rcSearch.right = rcSearch.left + 1;
5565 rcSearch.bottom = rcSearch.top + 1;
5567 iterator_frameditems(&i, infoPtr, &rcSearch);
5568 iterator_next(&i); /* go to first item in the sequence */
5569 lpht->iItem = i.nItem;
5570 iterator_destroy(&i);
5572 TRACE("lpht->iItem=%d\n", lpht->iItem);
5573 if (lpht->iItem == -1) return -1;
5575 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5576 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5577 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5578 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5579 lvItem.iItem = lpht->iItem;
5580 lvItem.iSubItem = 0;
5581 lvItem.pszText = szDispText;
5582 lvItem.cchTextMax = DISP_TEXT_SIZE;
5583 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5584 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5586 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel)) return -1;
5587 if (!LISTVIEW_GetItemOrigin(infoPtr, lpht->iItem, &Position)) return -1;
5588 opt.x = lpht->pt.x - Position.x - Origin.x;
5589 opt.y = lpht->pt.y - Position.y - Origin.y;
5591 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
5592 rcBounds = rcBox;
5593 else
5594 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5595 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5596 if (!PtInRect(&rcBounds, opt)) return -1;
5598 if (PtInRect(&rcIcon, opt))
5599 lpht->flags |= LVHT_ONITEMICON;
5600 else if (PtInRect(&rcLabel, opt))
5601 lpht->flags |= LVHT_ONITEMLABEL;
5602 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5603 lpht->flags |= LVHT_ONITEMSTATEICON;
5604 if (lpht->flags & LVHT_ONITEM)
5605 lpht->flags &= ~LVHT_NOWHERE;
5607 TRACE("lpht->flags=0x%x\n", lpht->flags);
5608 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5610 INT j;
5612 rcBounds.right = rcBounds.left;
5613 for (j = 0; j < infoPtr->hdpaColumns->nItemCount; j++)
5615 rcBounds.left = rcBounds.right;
5616 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5617 if (PtInRect(&rcBounds, opt))
5619 lpht->iSubItem = j;
5620 break;
5625 if (!select || lpht->iItem == -1) return lpht->iItem;
5627 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5629 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5630 return PtInRect(&rcBounds, opt) ? lpht->iItem : -1;
5634 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5635 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5636 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5637 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5638 their own sort proc. when sending LVM_SORTITEMS.
5640 /* Platform SDK:
5641 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5643 LVS_SORTXXX must be specified,
5644 LVS_OWNERDRAW is not set,
5645 <item>.pszText is not LPSTR_TEXTCALLBACK.
5647 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5648 are sorted based on item text..."
5650 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5652 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5653 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5654 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5656 /* if we're sorting descending, negate the return value */
5657 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5660 /***
5661 * nESCRIPTION:
5662 * Inserts a new item in the listview control.
5664 * PARAMETER(S):
5665 * [I] infoPtr : valid pointer to the listview structure
5666 * [I] lpLVItem : item information
5667 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5669 * RETURN:
5670 * SUCCESS : new item index
5671 * FAILURE : -1
5673 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5675 LONG lStyle = infoPtr->dwStyle;
5676 UINT uView = lStyle & LVS_TYPEMASK;
5677 INT nItem = -1;
5678 HDPA hdpaSubItems;
5679 NMLISTVIEW nmlv;
5680 LISTVIEW_ITEM *lpItem;
5681 BOOL is_sorted, has_changed;
5683 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5685 if (lStyle & LVS_OWNERDATA)
5687 nItem = infoPtr->nItemCount;
5688 infoPtr->nItemCount++;
5689 return nItem;
5692 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5693 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
5695 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5697 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5698 return -1;
5700 /* insert item in listview control data structure */
5701 if ( (hdpaSubItems = DPA_Create(8)) )
5702 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5703 if (nItem == -1) goto fail;
5705 /* FIXME: is the handling of this LVS_OWNERDRAWFIXED correct? */
5706 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5707 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5709 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
5710 is_sorted ? infoPtr->nItemCount + 1 : lpLVItem->iItem,
5711 hdpaSubItems );
5712 if (nItem == -1) goto fail;
5713 /* the array may be sparsly populated, we can't just increment the count here */
5714 infoPtr->nItemCount = infoPtr->hdpaItems->nItemCount;
5716 /* set the item attributes */
5717 if (!set_main_item(infoPtr, lpLVItem, TRUE, isW, &has_changed)) goto undo;
5719 /* if we're sorted, sort the list, and update the index */
5720 if (is_sorted)
5722 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5723 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5724 assert(nItem != -1);
5727 /* make room for the position, if we are in the right mode */
5728 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5730 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5731 goto undo;
5732 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5734 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5735 goto undo;
5739 /* Add the subitem list to the items array. Do this last in case we go to
5740 * fail during the above.
5742 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5744 /* send LVN_INSERTITEM notification */
5745 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5746 nmlv.iItem = nItem;
5747 nmlv.lParam = lpItem->lParam;
5748 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5750 /* align items (set position of each item) */
5751 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5753 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
5754 else LISTVIEW_AlignTop(infoPtr);
5757 LISTVIEW_UpdateScroll(infoPtr);
5759 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
5761 TRACE(" <- %d\n", nItem);
5762 return nItem;
5764 undo:
5765 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5766 infoPtr->nItemCount--;
5767 fail:
5768 DPA_DeletePtr(hdpaSubItems, 0);
5769 DPA_Destroy (hdpaSubItems);
5770 COMCTL32_Free (lpItem);
5771 return -1;
5774 /***
5775 * DESCRIPTION:
5776 * Redraws a range of items.
5778 * PARAMETER(S):
5779 * [I] infoPtr : valid pointer to the listview structure
5780 * [I] nFirst : first item
5781 * [I] nLast : last item
5783 * RETURN:
5784 * SUCCESS : TRUE
5785 * FAILURE : FALSE
5787 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
5789 INT i;
5791 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
5792 max(nFirst, nLast) >= infoPtr->nItemCount)
5793 return FALSE;
5795 for (i = nFirst; i <= nLast; i++)
5796 LISTVIEW_InvalidateItem(infoPtr, i);
5798 return TRUE;
5801 /***
5802 * DESCRIPTION:
5803 * Scroll the content of a listview.
5805 * PARAMETER(S):
5806 * [I] infoPtr : valid pointer to the listview structure
5807 * [I] dx : horizontal scroll amount in pixels
5808 * [I] dy : vertical scroll amount in pixels
5810 * RETURN:
5811 * SUCCESS : TRUE
5812 * FAILURE : FALSE
5814 * COMMENTS:
5815 * If the control is in report mode (LVS_REPORT) the control can
5816 * be scrolled only in line increments. "dy" will be rounded to the
5817 * nearest number of pixels that are a whole line. Ex: if line height
5818 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
5819 * is passed the the scroll will be 0. (per MSDN 7/2002)
5821 * For: (per experimentaion with native control and CSpy ListView)
5822 * LVS_ICON dy=1 = 1 pixel (vertical only)
5823 * dx ignored
5824 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
5825 * dx ignored
5826 * LVS_LIST dx=1 = 1 column (horizontal only)
5827 * but will only scroll 1 column per message
5828 * no matter what the value.
5829 * dy must be 0 or FALSE returned.
5830 * LVS_REPORT dx=1 = 1 pixel
5831 * dy= see above
5834 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
5836 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
5837 case LVS_REPORT:
5838 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
5839 dy /= infoPtr->nItemHeight;
5840 break;
5841 case LVS_LIST:
5842 if (dy != 0) return FALSE;
5843 break;
5844 default: /* icon */
5845 dx = 0;
5846 break;
5849 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
5850 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
5852 return TRUE;
5855 /***
5856 * DESCRIPTION:
5857 * Sets the background color.
5859 * PARAMETER(S):
5860 * [I] infoPtr : valid pointer to the listview structure
5861 * [I] clrBk : background color
5863 * RETURN:
5864 * SUCCESS : TRUE
5865 * FAILURE : FALSE
5867 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
5869 TRACE("(clrBk=%lx)\n", clrBk);
5871 if(infoPtr->clrBk != clrBk) {
5872 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
5873 infoPtr->clrBk = clrBk;
5874 if (clrBk == CLR_NONE)
5875 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
5876 else
5877 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
5878 LISTVIEW_InvalidateList(infoPtr);
5881 return TRUE;
5884 /* LISTVIEW_SetBkImage */
5886 /*** Helper for {Insert,Set}ColumnT *only* */
5887 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5889 if (lpColumn->mask & LVCF_FMT)
5891 /* format member is valid */
5892 lphdi->mask |= HDI_FORMAT;
5894 /* set text alignment (leftmost column must be left-aligned) */
5895 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5896 lphdi->fmt |= HDF_LEFT;
5897 else if (lpColumn->fmt & LVCFMT_RIGHT)
5898 lphdi->fmt |= HDF_RIGHT;
5899 else if (lpColumn->fmt & LVCFMT_CENTER)
5900 lphdi->fmt |= HDF_CENTER;
5902 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5903 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
5905 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5907 lphdi->fmt |= HDF_IMAGE;
5908 lphdi->iImage = I_IMAGECALLBACK;
5912 if (lpColumn->mask & LVCF_WIDTH)
5914 lphdi->mask |= HDI_WIDTH;
5915 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5917 /* make it fill the remainder of the controls width */
5918 RECT rcHeader;
5919 INT item_index;
5921 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5922 if (LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader))
5923 lphdi->cxy += rcHeader.right - rcHeader.left;
5925 /* retrieve the layout of the header */
5926 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5927 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
5929 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
5931 else
5932 lphdi->cxy = lpColumn->cx;
5935 if (lpColumn->mask & LVCF_TEXT)
5937 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
5938 lphdi->fmt |= HDF_STRING;
5939 lphdi->pszText = lpColumn->pszText;
5940 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
5943 if (lpColumn->mask & LVCF_IMAGE)
5945 lphdi->mask |= HDI_IMAGE;
5946 lphdi->iImage = lpColumn->iImage;
5949 if (lpColumn->mask & LVCF_ORDER)
5951 lphdi->mask |= HDI_ORDER;
5952 lphdi->iOrder = lpColumn->iOrder;
5956 /*** Helper for {Insert,Set}ColumnT *only* */
5957 static BOOL column_fill_info(LISTVIEW_INFO *infoPtr, COLUMN_INFO *lpColumnInfo, INT nColumn, LPLVCOLUMNW lpColumn)
5959 RECT rcCol;
5961 if (!Header_GetItemRect(infoPtr->hwndHeader, nColumn, &rcCol)) return FALSE;
5962 lpColumnInfo->rcHeader = rcCol;
5963 if (lpColumn->mask & LVCF_FMT)
5965 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT) lpColumnInfo->align = DT_LEFT;
5966 else if (lpColumn->fmt & LVCFMT_RIGHT) lpColumnInfo->align = DT_RIGHT;
5967 else if (lpColumn->fmt & LVCFMT_CENTER) lpColumnInfo->align = DT_CENTER;
5969 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_IMAGE)) lpColumnInfo->hasImage = TRUE;
5971 else
5973 lpColumnInfo->align = DT_LEFT;
5974 lpColumnInfo->hasImage = (nColumn == 0);
5977 return TRUE;
5980 /***
5981 * DESCRIPTION:
5982 * Inserts a new column.
5984 * PARAMETER(S):
5985 * [I] infoPtr : valid pointer to the listview structure
5986 * [I] nColumn : column index
5987 * [I] lpColumn : column information
5988 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
5990 * RETURN:
5991 * SUCCESS : new column index
5992 * FAILURE : -1
5994 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5995 LPLVCOLUMNW lpColumn, BOOL isW)
5997 COLUMN_INFO *lpColumnInfo;
5998 INT nNewColumn;
5999 HDITEMW hdi;
6001 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6003 if (!lpColumn) return -1;
6005 ZeroMemory(&hdi, sizeof(HDITEMW));
6006 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6008 /* insert item in header control */
6009 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6010 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6011 (WPARAM)nColumn, (LPARAM)&hdi);
6012 if (nNewColumn == -1) return -1;
6014 /* create our own column info */
6015 if (!(lpColumnInfo = COMCTL32_Alloc(sizeof(COLUMN_INFO)))) goto fail;
6016 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6018 if (!column_fill_info(infoPtr, lpColumnInfo, nNewColumn, lpColumn)) goto fail;
6020 /* now we have to actually adjust the data */
6021 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6023 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
6024 HDPA hdpaSubItems;
6025 INT nItem, i;
6027 /* preallocate memory, so we can fail gracefully */
6028 if (nNewColumn == 0)
6030 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->nItemCount);
6031 if (!lpNewItems) goto fail;
6032 for (i = 0; i < infoPtr->nItemCount; i++)
6033 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
6034 if (i != infoPtr->nItemCount)
6036 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
6037 COMCTL32_Free(lpNewItems);
6038 goto fail;
6042 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6044 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6045 if (!hdpaSubItems) continue;
6046 for (i = 1; i < hdpaSubItems->nItemCount; i++)
6048 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
6049 if (!lpSubItem) break;
6050 if (lpSubItem->iSubItem >= nNewColumn)
6051 lpSubItem->iSubItem++;
6054 /* if we found our subitem, zapp it */
6055 if (nNewColumn == 0)
6057 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
6058 lpSubItem = lpNewItems[nItem];
6059 lpSubItem->hdr = lpMainItem->hdr;
6060 lpSubItem->iSubItem = 1;
6061 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
6062 lpMainItem->iSubItem = 0;
6063 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
6067 COMCTL32_Free(lpNewItems);
6070 /* make space for the new column */
6071 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6073 return nNewColumn;
6075 fail:
6076 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6077 if (lpColumnInfo)
6079 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6080 COMCTL32_Free(lpColumnInfo);
6082 return -1;
6085 /***
6086 * DESCRIPTION:
6087 * Sets the attributes of a header item.
6089 * PARAMETER(S):
6090 * [I] infoPtr : valid pointer to the listview structure
6091 * [I] nColumn : column index
6092 * [I] lpColumn : column attributes
6093 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6095 * RETURN:
6096 * SUCCESS : TRUE
6097 * FAILURE : FALSE
6099 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6100 LPLVCOLUMNW lpColumn, BOOL isW)
6102 COLUMN_INFO *lpColumnInfo;
6103 HDITEMW hdi, hdiget;
6104 BOOL bResult;
6106 if (!lpColumn || nColumn < 0 || nColumn < infoPtr->hdpaColumns->nItemCount) return FALSE;
6108 ZeroMemory(&hdi, sizeof(HDITEMW));
6109 if (lpColumn->mask & LVCF_FMT)
6111 /* format member is valid */
6112 hdi.mask |= HDI_FORMAT;
6114 /* get current format first */
6115 hdiget.mask = HDI_FORMAT;
6116 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6117 /* preserve HDF_STRING if present */
6118 hdi.fmt = hdiget.fmt & HDF_STRING;
6121 if (!(lpColumnInfo = DPA_GetPtr(infoPtr->hdpaColumns, nColumn))) return FALSE;
6123 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6125 /* set header item attributes */
6126 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6127 if (!bResult) return FALSE;
6129 if (!column_fill_info(infoPtr, lpColumnInfo, nColumn, lpColumn)) return FALSE;
6131 return TRUE;
6134 /***
6135 * DESCRIPTION:
6136 * Sets the column order array
6138 * PARAMETERS:
6139 * [I] infoPtr : valid pointer to the listview structure
6140 * [I] iCount : number of elements in column order array
6141 * [I] lpiArray : pointer to column order array
6143 * RETURN:
6144 * SUCCESS : TRUE
6145 * FAILURE : FALSE
6147 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6149 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6151 if (!lpiArray)
6152 return FALSE;
6154 return TRUE;
6158 /***
6159 * DESCRIPTION:
6160 * Sets the width of a column
6162 * PARAMETERS:
6163 * [I] infoPtr : valid pointer to the listview structure
6164 * [I] iCol : column index
6165 * [I] cx : column width
6167 * RETURN:
6168 * SUCCESS : TRUE
6169 * FAILURE : FALSE
6171 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6173 HDITEMW hdi;
6174 LRESULT lret;
6175 LONG lStyle = infoPtr->dwStyle;
6176 UINT uView = lStyle & LVS_TYPEMASK;
6177 HDC hdc;
6178 HFONT header_font;
6179 HFONT old_font;
6180 SIZE size;
6181 WCHAR text_buffer[DISP_TEXT_SIZE];
6182 INT header_item_count;
6183 INT item_index;
6184 INT nLabelWidth;
6185 RECT rcHeader;
6186 LVITEMW lvItem;
6187 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6189 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6191 /* set column width only if in report or list mode */
6192 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6194 /* take care of invalid cx values */
6195 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6196 else if (uView == LVS_LIST && cx < 1) return FALSE;
6198 /* resize all columns if in LVS_LIST mode */
6199 if(uView == LVS_LIST)
6201 infoPtr->nItemWidth = cx;
6202 LISTVIEW_InvalidateList(infoPtr);
6203 return TRUE;
6206 /* FIXME: update COLUMN_INFO */
6208 /* autosize based on listview items width */
6209 if(cx == LVSCW_AUTOSIZE)
6211 /* set the width of the column to the width of the widest item */
6212 if (iCol == 0 || uView == LVS_LIST)
6214 cx = 0;
6215 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6217 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6218 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6220 if (infoPtr->himlSmall)
6221 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6223 else
6225 lvItem.iSubItem = iCol;
6226 lvItem.mask = LVIF_TEXT;
6227 lvItem.pszText = szDispText;
6228 lvItem.cchTextMax = DISP_TEXT_SIZE;
6229 cx = 0;
6230 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6232 lvItem.iItem = item_index;
6233 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6234 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6235 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6238 cx += TRAILING_PADDING;
6239 } /* autosize based on listview header width */
6240 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6242 header_item_count = infoPtr->hdpaColumns->nItemCount;
6244 /* if iCol is the last column make it fill the remainder of the controls width */
6245 if(iCol == (header_item_count - 1)) {
6246 cx = 0;
6248 for(item_index = 0; item_index < (header_item_count - 1); item_index++)
6250 if (LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader))
6251 cx += rcHeader.right - rcHeader.left;
6254 /* retrieve the layout of the header */
6255 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6257 cx = (rcHeader.right - rcHeader.left) - cx;
6259 else
6261 /* Despite what the MS docs say, if this is not the last
6262 column, then MS resizes the column to the width of the
6263 largest text string in the column, including headers
6264 and items. This is different from LVSCW_AUTOSIZE in that
6265 LVSCW_AUTOSIZE ignores the header string length.
6268 /* retrieve header font */
6269 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6271 /* retrieve header text */
6272 hdi.mask = HDI_TEXT;
6273 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6274 hdi.pszText = text_buffer;
6276 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6278 /* determine the width of the text in the header */
6279 hdc = GetDC(infoPtr->hwndSelf);
6280 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6282 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6284 SelectObject(hdc, old_font); /* restore the old font */
6285 ReleaseDC(infoPtr->hwndSelf, hdc);
6287 lvItem.iSubItem = iCol;
6288 lvItem.mask = LVIF_TEXT;
6289 lvItem.pszText = szDispText;
6290 lvItem.cchTextMax = DISP_TEXT_SIZE;
6291 cx = size.cx;
6292 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6294 lvItem.iItem = item_index;
6295 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6296 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6297 nLabelWidth += TRAILING_PADDING;
6298 /* While it is possible for subitems to have icons, even MS messes
6299 up the positioning, so I suspect no applications actually use
6300 them. */
6301 if (item_index == 0 && infoPtr->himlSmall)
6302 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6303 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6308 /* call header to update the column change */
6309 hdi.mask = HDI_WIDTH;
6311 hdi.cxy = cx;
6312 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6314 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6316 return lret;
6319 /***
6320 * DESCRIPTION:
6321 * Sets the extended listview style.
6323 * PARAMETERS:
6324 * [I] infoPtr : valid pointer to the listview structure
6325 * [I] dwMask : mask
6326 * [I] dwStyle : style
6328 * RETURN:
6329 * SUCCESS : previous style
6330 * FAILURE : 0
6332 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6334 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6336 /* set new style */
6337 if (dwMask)
6338 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6339 else
6340 infoPtr->dwLvExStyle = dwStyle;
6342 return dwOldStyle;
6345 /***
6346 * DESCRIPTION:
6347 * Sets the new hot cursor used during hot tracking and hover selection.
6349 * PARAMETER(S):
6350 * [I] infoPtr : valid pointer to the listview structure
6351 * [I} hCurosr : the new hot cursor handle
6353 * RETURN:
6354 * Returns the previous hot cursor
6356 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6358 HCURSOR oldCursor = infoPtr->hHotCursor;
6360 infoPtr->hHotCursor = hCursor;
6362 return oldCursor;
6366 /***
6367 * DESCRIPTION:
6368 * Sets the hot item index.
6370 * PARAMETERS:
6371 * [I] infoPtr : valid pointer to the listview structure
6372 * [I] iIndex : index
6374 * RETURN:
6375 * SUCCESS : previous hot item index
6376 * FAILURE : -1 (no hot item)
6378 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6380 INT iOldIndex = infoPtr->nHotItem;
6382 infoPtr->nHotItem = iIndex;
6384 return iOldIndex;
6388 /***
6389 * DESCRIPTION:
6390 * Sets the amount of time the cursor must hover over an item before it is selected.
6392 * PARAMETER(S):
6393 * [I] infoPtr : valid pointer to the listview structure
6394 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6396 * RETURN:
6397 * Returns the previous hover time
6399 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6401 DWORD oldHoverTime = infoPtr->dwHoverTime;
6403 infoPtr->dwHoverTime = dwHoverTime;
6405 return oldHoverTime;
6408 /***
6409 * DESCRIPTION:
6410 * Sets spacing for icons of LVS_ICON style.
6412 * PARAMETER(S):
6413 * [I] infoPtr : valid pointer to the listview structure
6414 * [I] spacing : MAKELONG(cx, cy)
6416 * RETURN:
6417 * MAKELONG(oldcx, oldcy)
6419 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6421 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6422 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6423 LONG lStyle = infoPtr->dwStyle;
6424 UINT uView = lStyle & LVS_TYPEMASK;
6426 TRACE("requested=(%d,%d)\n", cx, cy);
6428 /* this is supported only for LVS_ICON style */
6429 if (uView != LVS_ICON) return oldspacing;
6431 /* set to defaults, if instructed to */
6432 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6433 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6435 /* if 0 then compute width
6436 * FIXME: Should scan each item and determine max width of
6437 * icon or label, then make that the width */
6438 if (cx == 0)
6439 cx = infoPtr->iconSpacing.cx;
6441 /* if 0 then compute height */
6442 if (cy == 0)
6443 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6444 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6447 infoPtr->iconSpacing.cx = cx;
6448 infoPtr->iconSpacing.cy = cy;
6450 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6451 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6452 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6453 infoPtr->ntmHeight);
6455 /* these depend on the iconSpacing */
6456 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6457 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6459 return oldspacing;
6462 inline void update_icon_size(HIMAGELIST himl, BOOL small, SIZE *size)
6464 INT cx, cy;
6466 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6468 size->cx = cx;
6469 size->cy = cy;
6471 else
6473 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6474 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6478 /***
6479 * DESCRIPTION:
6480 * Sets image lists.
6482 * PARAMETER(S):
6483 * [I] infoPtr : valid pointer to the listview structure
6484 * [I] nType : image list type
6485 * [I] himl : image list handle
6487 * RETURN:
6488 * SUCCESS : old image list
6489 * FAILURE : NULL
6491 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6493 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6494 INT oldHeight = infoPtr->nItemHeight;
6495 HIMAGELIST himlOld = 0;
6497 switch (nType)
6499 case LVSIL_NORMAL:
6500 himlOld = infoPtr->himlNormal;
6501 infoPtr->himlNormal = himl;
6502 if (uView == LVS_ICON) update_icon_size(himl, FALSE, &infoPtr->iconSize);
6503 LISTVIEW_SetIconSpacing(infoPtr, 0);
6504 break;
6506 case LVSIL_SMALL:
6507 himlOld = infoPtr->himlSmall;
6508 infoPtr->himlSmall = himl;
6509 if (uView != LVS_ICON) update_icon_size(himl, TRUE, &infoPtr->iconSize);
6510 break;
6512 case LVSIL_STATE:
6513 himlOld = infoPtr->himlState;
6514 infoPtr->himlState = himl;
6515 update_icon_size(himl, TRUE, &infoPtr->iconStateSize);
6516 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6517 break;
6519 default:
6520 ERR("Unknown icon type=%d\n", nType);
6521 return NULL;
6524 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6525 if (infoPtr->nItemHeight != oldHeight)
6526 LISTVIEW_UpdateScroll(infoPtr);
6528 return himlOld;
6531 /***
6532 * DESCRIPTION:
6533 * Preallocates memory (does *not* set the actual count of items !)
6535 * PARAMETER(S):
6536 * [I] infoPtr : valid pointer to the listview structure
6537 * [I] nItems : item count (projected number of items to allocate)
6538 * [I] dwFlags : update flags
6540 * RETURN:
6541 * SUCCESS : TRUE
6542 * FAILURE : FALSE
6544 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6546 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6548 if (infoPtr->dwStyle & LVS_OWNERDATA)
6550 int precount,topvisible;
6552 TRACE("LVS_OWNERDATA is set!\n");
6553 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6554 FIXME("flags %s %s not implemented\n",
6555 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6556 : "",
6557 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6559 LISTVIEW_DeselectAll(infoPtr);
6561 precount = infoPtr->nItemCount;
6562 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6563 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6565 infoPtr->nItemCount = nItems;
6566 infoPtr->nItemWidth = max(LISTVIEW_CalculateMaxWidth(infoPtr),
6567 DEFAULT_COLUMN_WIDTH);
6569 LISTVIEW_UpdateSize(infoPtr);
6570 LISTVIEW_UpdateScroll(infoPtr);
6572 if (min(precount,infoPtr->nItemCount) < topvisible)
6573 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6575 else
6577 /* According to MSDN for non-LVS_OWNERDATA this is just
6578 * a performance issue. The control allocates its internal
6579 * data structures for the number of items specified. It
6580 * cuts down on the number of memory allocations. Therefore
6581 * we will just issue a WARN here
6583 WARN("for non-ownerdata performance option not implemented.\n");
6586 return TRUE;
6589 /***
6590 * DESCRIPTION:
6591 * Sets the position of an item.
6593 * PARAMETER(S):
6594 * [I] infoPtr : valid pointer to the listview structure
6595 * [I] nItem : item index
6596 * [I] pt : coordinate
6598 * RETURN:
6599 * SUCCESS : TRUE
6600 * FAILURE : FALSE
6602 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6604 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6605 POINT old;
6607 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6609 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6610 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6612 /* This point value seems to be an undocumented feature.
6613 * The best guess is that it means either at the origin,
6614 * or at true beginning of the list. I will assume the origin. */
6615 if ((pt.x == -1) && (pt.y == -1))
6616 LISTVIEW_GetOrigin(infoPtr, &pt);
6617 else if (uView == LVS_ICON)
6619 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6620 pt.y -= ICON_TOP_PADDING;
6623 /* save the old position */
6624 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
6625 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
6627 /* Is the position changing? */
6628 if (pt.x == old.x && pt.y == old.y) return TRUE;
6630 /* FIXME: shouldn't we invalidate, as the item moved? */
6632 /* Allocating a POINTER for every item is too resource intensive,
6633 * so we'll keep the (x,y) in different arrays */
6634 if (DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)pt.x) &&
6635 DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)pt.y) )
6636 return TRUE;
6638 ERR("We should never fail here (nItem=%d, pt=%s), please report.\n",
6639 nItem, debugpoint(&pt));
6640 return FALSE;
6643 /***
6644 * DESCRIPTION:
6645 * Sets the state of one or many items.
6647 * PARAMETER(S):
6648 * [I] infoPtr : valid pointer to the listview structure
6649 * [I] nItem : item index
6650 * [I] lpLVItem : item or subitem info
6652 * RETURN:
6653 * SUCCESS : TRUE
6654 * FAILURE : FALSE
6656 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6658 BOOL bResult = TRUE;
6659 LVITEMW lvItem;
6661 lvItem.iItem = nItem;
6662 lvItem.iSubItem = 0;
6663 lvItem.mask = LVIF_STATE;
6664 lvItem.state = lpLVItem->state;
6665 lvItem.stateMask = lpLVItem->stateMask;
6666 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6668 if (nItem == -1)
6670 /* apply to all items */
6671 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6672 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6674 else
6675 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6677 return bResult;
6680 /***
6681 * DESCRIPTION:
6682 * Sets the text of an item or subitem.
6684 * PARAMETER(S):
6685 * [I] hwnd : window handle
6686 * [I] nItem : item index
6687 * [I] lpLVItem : item or subitem info
6688 * [I] isW : TRUE if input is Unicode
6690 * RETURN:
6691 * SUCCESS : TRUE
6692 * FAILURE : FALSE
6694 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6696 LVITEMW lvItem;
6698 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6700 lvItem.iItem = nItem;
6701 lvItem.iSubItem = lpLVItem->iSubItem;
6702 lvItem.mask = LVIF_TEXT;
6703 lvItem.pszText = lpLVItem->pszText;
6704 lvItem.cchTextMax = lpLVItem->cchTextMax;
6706 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6708 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6711 /***
6712 * DESCRIPTION:
6713 * Set item index that marks the start of a multiple selection.
6715 * PARAMETER(S):
6716 * [I] infoPtr : valid pointer to the listview structure
6717 * [I] nIndex : index
6719 * RETURN:
6720 * Index number or -1 if there is no selection mark.
6722 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6724 INT nOldIndex = infoPtr->nSelectionMark;
6726 TRACE("(nIndex=%d)\n", nIndex);
6728 infoPtr->nSelectionMark = nIndex;
6730 return nOldIndex;
6733 /***
6734 * DESCRIPTION:
6735 * Sets the text background color.
6737 * PARAMETER(S):
6738 * [I] infoPtr : valid pointer to the listview structure
6739 * [I] clrTextBk : text background color
6741 * RETURN:
6742 * SUCCESS : TRUE
6743 * FAILURE : FALSE
6745 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6747 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6749 if (infoPtr->clrTextBk != clrTextBk)
6751 infoPtr->clrTextBk = clrTextBk;
6752 LISTVIEW_InvalidateList(infoPtr);
6755 return TRUE;
6758 /***
6759 * DESCRIPTION:
6760 * Sets the text foreground color.
6762 * PARAMETER(S):
6763 * [I] infoPtr : valid pointer to the listview structure
6764 * [I] clrText : text color
6766 * RETURN:
6767 * SUCCESS : TRUE
6768 * FAILURE : FALSE
6770 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6772 TRACE("(clrText=%lx)\n", clrText);
6774 if (infoPtr->clrText != clrText)
6776 infoPtr->clrText = clrText;
6777 LISTVIEW_InvalidateList(infoPtr);
6780 return TRUE;
6783 /* LISTVIEW_SetToolTips */
6784 /* LISTVIEW_SetUnicodeFormat */
6785 /* LISTVIEW_SetWorkAreas */
6787 /***
6788 * DESCRIPTION:
6789 * Callback internally used by LISTVIEW_SortItems()
6791 * PARAMETER(S):
6792 * [I] first : pointer to first LISTVIEW_ITEM to compare
6793 * [I] second : pointer to second LISTVIEW_ITEM to compare
6794 * [I] lParam : HWND of control
6796 * RETURN:
6797 * if first comes before second : negative
6798 * if first comes after second : positive
6799 * if first and second are equivalent : zero
6801 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6803 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6804 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6805 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6807 /* Forward the call to the client defined callback */
6808 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6811 /***
6812 * DESCRIPTION:
6813 * Sorts the listview items.
6815 * PARAMETER(S):
6816 * [I] infoPtr : valid pointer to the listview structure
6817 * [I] pfnCompare : application-defined value
6818 * [I] lParamSort : pointer to comparision callback
6820 * RETURN:
6821 * SUCCESS : TRUE
6822 * FAILURE : FALSE
6824 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6826 UINT lStyle = infoPtr->dwStyle;
6827 HDPA hdpaSubItems;
6828 LISTVIEW_ITEM *lpItem;
6829 LPVOID selectionMarkItem;
6830 LVITEMW item;
6831 int i;
6833 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6835 if (lStyle & LVS_OWNERDATA) return FALSE;
6837 if (!infoPtr->hdpaItems) return FALSE;
6839 /* if there are 0 or 1 items, there is no need to sort */
6840 if (infoPtr->nItemCount < 2) return TRUE;
6842 if (infoPtr->nFocusedItem >= 0)
6844 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6845 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6846 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6848 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
6849 /* clear the lpItem->state for non-selected ones */
6850 /* remove the selection ranges */
6852 infoPtr->pfnCompare = pfnCompare;
6853 infoPtr->lParamSort = lParamSort;
6854 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6856 /* Adjust selections and indices so that they are the way they should
6857 * be after the sort (otherwise, the list items move around, but
6858 * whatever is at the item's previous original position will be
6859 * selected instead)
6861 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6862 for (i=0; i < infoPtr->nItemCount; i++)
6864 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6865 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6867 if (lpItem->state & LVIS_SELECTED)
6869 item.state = LVIS_SELECTED;
6870 item.stateMask = LVIS_SELECTED;
6871 LISTVIEW_SetItemState(infoPtr, i, &item);
6873 if (lpItem->state & LVIS_FOCUSED)
6875 infoPtr->nFocusedItem = i;
6876 lpItem->state &= ~LVIS_FOCUSED;
6879 if (selectionMarkItem != NULL)
6880 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6881 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6883 /* align the items */
6884 LISTVIEW_AlignTop(infoPtr);
6886 /* refresh the display */
6887 LISTVIEW_InvalidateList(infoPtr); /* FIXME: display should not change for [SMALL]ICON view */
6889 return TRUE;
6892 /***
6893 * DESCRIPTION:
6894 * Updates an items or rearranges the listview control.
6896 * PARAMETER(S):
6897 * [I] infoPtr : valid pointer to the listview structure
6898 * [I] nItem : item index
6900 * RETURN:
6901 * SUCCESS : TRUE
6902 * FAILURE : FALSE
6904 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
6906 LONG lStyle = infoPtr->dwStyle;
6907 UINT uView = lStyle & LVS_TYPEMASK;
6909 TRACE("(nItem=%d)\n", nItem);
6911 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6913 /* rearrange with default alignment style */
6914 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
6915 LISTVIEW_Arrange(infoPtr, 0);
6916 else
6917 LISTVIEW_InvalidateItem(infoPtr, nItem);
6919 return TRUE;
6923 /***
6924 * DESCRIPTION:
6925 * Creates the listview control.
6927 * PARAMETER(S):
6928 * [I] hwnd : window handle
6929 * [I] lpcs : the create parameters
6931 * RETURN:
6932 * Success: 0
6933 * Failure: -1
6935 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
6937 LISTVIEW_INFO *infoPtr;
6938 UINT uView = lpcs->style & LVS_TYPEMASK;
6939 LOGFONTW logFont;
6941 TRACE("(lpcs=%p)\n", lpcs);
6943 /* initialize info pointer */
6944 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
6945 if (!infoPtr) return -1;
6947 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
6949 infoPtr->hwndSelf = hwnd;
6950 infoPtr->dwStyle = lpcs->style;
6951 /* determine the type of structures to use */
6952 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
6953 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
6955 /* initialize color information */
6956 infoPtr->clrBk = CLR_NONE;
6957 infoPtr->clrText = comctl32_color.clrWindowText;
6958 infoPtr->clrTextBk = CLR_DEFAULT;
6959 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
6961 /* set default values */
6962 infoPtr->nFocusedItem = -1;
6963 infoPtr->nSelectionMark = -1;
6964 infoPtr->nHotItem = -1;
6965 infoPtr->bRedraw = TRUE;
6966 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
6967 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
6968 infoPtr->nEditLabelItem = -1;
6970 /* get default font (icon title) */
6971 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
6972 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
6973 infoPtr->hFont = infoPtr->hDefaultFont;
6974 LISTVIEW_SaveTextMetrics(infoPtr);
6976 /* create header */
6977 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
6978 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
6979 0, 0, 0, 0, hwnd, (HMENU)0,
6980 lpcs->hInstance, NULL);
6982 /* set header unicode format */
6983 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
6985 /* set header font */
6986 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
6988 infoPtr->hdpaColumns = DPA_Create(10);
6990 if (uView == LVS_ICON)
6992 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
6993 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
6995 else if (uView == LVS_REPORT)
6997 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
6999 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7001 else
7003 /* set HDS_HIDDEN flag to hide the header bar */
7004 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7005 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7009 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7010 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7012 else
7014 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7015 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7018 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
7019 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
7021 /* display unsupported listview window styles */
7022 LISTVIEW_UnsupportedStyles(lpcs->style);
7024 /* allocate memory for the data structure */
7025 infoPtr->hdpaItems = DPA_Create(10);
7026 infoPtr->hdpaPosX = DPA_Create(10);
7027 infoPtr->hdpaPosY = DPA_Create(10);
7029 /* allocate memory for the selection ranges */
7030 infoPtr->selectionRanges = ranges_create(10);
7032 /* initialize size of items */
7033 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7034 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
7036 /* initialize the hover time to -1(indicating the default system hover time) */
7037 infoPtr->dwHoverTime = -1;
7039 return 0;
7042 /***
7043 * DESCRIPTION:
7044 * Erases the background of the listview control.
7046 * PARAMETER(S):
7047 * [I] infoPtr : valid pointer to the listview structure
7048 * [I] hdc : device context handle
7050 * RETURN:
7051 * SUCCESS : TRUE
7052 * FAILURE : FALSE
7054 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7056 RECT rc;
7058 TRACE("(hdc=%x)\n", hdc);
7060 if (!GetClipBox(hdc, &rc)) return FALSE;
7062 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7066 /***
7067 * DESCRIPTION:
7068 * Helper function for LISTVIEW_[HV]Scroll *only*.
7069 * Performs vertical/horizontal scrolling by a give amount.
7071 * PARAMETER(S):
7072 * [I] infoPtr : valid pointer to the listview structure
7073 * [I] dx : amount of horizontal scroll
7074 * [I] dy : amount of vertical scroll
7076 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7078 /* now we can scroll the list */
7079 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7080 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7081 /* if we have focus, adjust rect */
7082 OffsetRect(&infoPtr->rcFocus, dx, dy);
7083 UpdateWindow(infoPtr->hwndSelf);
7086 /***
7087 * DESCRIPTION:
7088 * Performs vertical scrolling.
7090 * PARAMETER(S):
7091 * [I] infoPtr : valid pointer to the listview structure
7092 * [I] nScrollCode : scroll code
7093 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7094 * [I] hScrollWnd : scrollbar control window handle
7096 * RETURN:
7097 * Zero
7099 * NOTES:
7100 * SB_LINEUP/SB_LINEDOWN:
7101 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7102 * for LVS_REPORT is 1 line
7103 * for LVS_LIST cannot occur
7106 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7107 INT nScrollDiff, HWND hScrollWnd)
7109 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7110 INT nOldScrollPos, nNewScrollPos;
7111 SCROLLINFO scrollInfo;
7112 BOOL is_an_icon;
7114 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7116 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7118 scrollInfo.cbSize = sizeof(SCROLLINFO);
7119 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7121 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7123 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7125 nOldScrollPos = scrollInfo.nPos;
7126 switch (nScrollCode)
7128 case SB_INTERNAL:
7129 break;
7131 case SB_LINEUP:
7132 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7133 break;
7135 case SB_LINEDOWN:
7136 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7137 break;
7139 case SB_PAGEUP:
7140 nScrollDiff = -scrollInfo.nPage;
7141 break;
7143 case SB_PAGEDOWN:
7144 nScrollDiff = scrollInfo.nPage;
7145 break;
7147 case SB_THUMBPOSITION:
7148 case SB_THUMBTRACK:
7149 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7150 break;
7152 default:
7153 nScrollDiff = 0;
7156 /* quit right away if pos isn't changing */
7157 if (nScrollDiff == 0) return 0;
7159 /* calculate new position, and handle overflows */
7160 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7161 if (nScrollDiff > 0) {
7162 if (nNewScrollPos < nOldScrollPos ||
7163 nNewScrollPos > scrollInfo.nMax)
7164 nNewScrollPos = scrollInfo.nMax;
7165 } else {
7166 if (nNewScrollPos > nOldScrollPos ||
7167 nNewScrollPos < scrollInfo.nMin)
7168 nNewScrollPos = scrollInfo.nMin;
7171 /* set the new position, and reread in case it changed */
7172 scrollInfo.fMask = SIF_POS;
7173 scrollInfo.nPos = nNewScrollPos;
7174 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7176 /* carry on only if it really changed */
7177 if (nNewScrollPos == nOldScrollPos) return 0;
7179 /* now adjust to client coordinates */
7180 nScrollDiff = nOldScrollPos - nNewScrollPos;
7181 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7183 /* and scroll the window */
7184 scroll_list(infoPtr, 0, nScrollDiff);
7186 return 0;
7189 /***
7190 * DESCRIPTION:
7191 * Performs horizontal scrolling.
7193 * PARAMETER(S):
7194 * [I] infoPtr : valid pointer to the listview structure
7195 * [I] nScrollCode : scroll code
7196 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7197 * [I] hScrollWnd : scrollbar control window handle
7199 * RETURN:
7200 * Zero
7202 * NOTES:
7203 * SB_LINELEFT/SB_LINERIGHT:
7204 * for LVS_ICON, LVS_SMALLICON 1 pixel
7205 * for LVS_REPORT is 1 pixel
7206 * for LVS_LIST is 1 column --> which is a 1 because the
7207 * scroll is based on columns not pixels
7210 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7211 INT nScrollDiff, HWND hScrollWnd)
7213 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7214 INT nOldScrollPos, nNewScrollPos;
7215 SCROLLINFO scrollInfo;
7217 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7219 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7221 scrollInfo.cbSize = sizeof(SCROLLINFO);
7222 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7224 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7226 nOldScrollPos = scrollInfo.nPos;
7228 switch (nScrollCode)
7230 case SB_INTERNAL:
7231 break;
7233 case SB_LINELEFT:
7234 nScrollDiff = -1;
7235 break;
7237 case SB_LINERIGHT:
7238 nScrollDiff = 1;
7239 break;
7241 case SB_PAGELEFT:
7242 nScrollDiff = -scrollInfo.nPage;
7243 break;
7245 case SB_PAGERIGHT:
7246 nScrollDiff = scrollInfo.nPage;
7247 break;
7249 case SB_THUMBPOSITION:
7250 case SB_THUMBTRACK:
7251 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7252 break;
7254 default:
7255 nScrollDiff = 0;
7258 /* quit right away if pos isn't changing */
7259 if (nScrollDiff == 0) return 0;
7261 /* calculate new position, and handle overflows */
7262 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7263 if (nScrollDiff > 0) {
7264 if (nNewScrollPos < nOldScrollPos ||
7265 nNewScrollPos > scrollInfo.nMax)
7266 nNewScrollPos = scrollInfo.nMax;
7267 } else {
7268 if (nNewScrollPos > nOldScrollPos ||
7269 nNewScrollPos < scrollInfo.nMin)
7270 nNewScrollPos = scrollInfo.nMin;
7273 /* set the new position, and reread in case it changed */
7274 scrollInfo.fMask = SIF_POS;
7275 scrollInfo.nPos = nNewScrollPos;
7276 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7278 /* carry on only if it really changed */
7279 if (nNewScrollPos == nOldScrollPos) return 0;
7281 if(uView == LVS_REPORT)
7282 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7284 /* now adjust to client coordinates */
7285 nScrollDiff = nOldScrollPos - nNewScrollPos;
7286 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7288 /* and scroll the window */
7289 scroll_list(infoPtr, nScrollDiff, 0);
7291 return 0;
7294 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7296 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7297 INT gcWheelDelta = 0;
7298 UINT pulScrollLines = 3;
7299 SCROLLINFO scrollInfo;
7301 TRACE("(wheelDelta=%d)\n", wheelDelta);
7303 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7304 gcWheelDelta -= wheelDelta;
7306 scrollInfo.cbSize = sizeof(SCROLLINFO);
7307 scrollInfo.fMask = SIF_POS;
7309 switch(uView)
7311 case LVS_ICON:
7312 case LVS_SMALLICON:
7314 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7315 * should be fixed in the future.
7317 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7318 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7319 scrollInfo.nPos + (gcWheelDelta < 0) ?
7320 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7321 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7322 break;
7324 case LVS_REPORT:
7325 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7327 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7329 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7330 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7331 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7334 break;
7336 case LVS_LIST:
7337 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7338 break;
7340 return 0;
7343 /***
7344 * DESCRIPTION:
7345 * ???
7347 * PARAMETER(S):
7348 * [I] infoPtr : valid pointer to the listview structure
7349 * [I] nVirtualKey : virtual key
7350 * [I] lKeyData : key data
7352 * RETURN:
7353 * Zero
7355 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7357 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7358 INT nItem = -1;
7359 NMLVKEYDOWN nmKeyDown;
7361 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7363 /* send LVN_KEYDOWN notification */
7364 nmKeyDown.wVKey = nVirtualKey;
7365 nmKeyDown.flags = 0;
7366 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7368 switch (nVirtualKey)
7370 case VK_RETURN:
7371 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7373 notify(infoPtr, NM_RETURN);
7374 notify(infoPtr, LVN_ITEMACTIVATE);
7376 break;
7378 case VK_HOME:
7379 if (infoPtr->nItemCount > 0)
7380 nItem = 0;
7381 break;
7383 case VK_END:
7384 if (infoPtr->nItemCount > 0)
7385 nItem = infoPtr->nItemCount - 1;
7386 break;
7388 case VK_LEFT:
7389 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7390 break;
7392 case VK_UP:
7393 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7394 break;
7396 case VK_RIGHT:
7397 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7398 break;
7400 case VK_DOWN:
7401 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7402 break;
7404 case VK_PRIOR:
7405 if (uView == LVS_REPORT)
7406 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7407 else
7408 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7409 * LISTVIEW_GetCountPerRow(infoPtr);
7410 if(nItem < 0) nItem = 0;
7411 break;
7413 case VK_NEXT:
7414 if (uView == LVS_REPORT)
7415 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7416 else
7417 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7418 * LISTVIEW_GetCountPerRow(infoPtr);
7419 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7420 break;
7423 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7424 LISTVIEW_KeySelection(infoPtr, nItem);
7426 return 0;
7429 /***
7430 * DESCRIPTION:
7431 * Kills the focus.
7433 * PARAMETER(S):
7434 * [I] infoPtr : valid pointer to the listview structure
7436 * RETURN:
7437 * Zero
7439 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7441 TRACE("()\n");
7443 /* if we did not have the focus, there's nothing to do */
7444 if (!infoPtr->bFocus) return 0;
7446 /* send NM_KILLFOCUS notification */
7447 notify(infoPtr, NM_KILLFOCUS);
7449 /* if we have a focus rectagle, get rid of it */
7450 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7452 /* set window focus flag */
7453 infoPtr->bFocus = FALSE;
7455 /* invalidate the selected items before reseting focus flag */
7456 LISTVIEW_InvalidateSelectedItems(infoPtr);
7458 return 0;
7461 /***
7462 * DESCRIPTION:
7463 * Processes double click messages (left mouse button).
7465 * PARAMETER(S):
7466 * [I] infoPtr : valid pointer to the listview structure
7467 * [I] wKey : key flag
7468 * [I] pts : mouse coordinate
7470 * RETURN:
7471 * Zero
7473 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7475 LVHITTESTINFO htInfo;
7477 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7479 /* send NM_RELEASEDCAPTURE notification */
7480 notify(infoPtr, NM_RELEASEDCAPTURE);
7482 htInfo.pt.x = pts.x;
7483 htInfo.pt.y = pts.y;
7485 /* send NM_DBLCLK notification */
7486 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7487 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7489 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7490 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7492 return 0;
7495 /***
7496 * DESCRIPTION:
7497 * Processes mouse down messages (left mouse button).
7499 * PARAMETER(S):
7500 * [I] infoPtr : valid pointer to the listview structure
7501 * [I] wKey : key flag
7502 * [I] pts : mouse coordinate
7504 * RETURN:
7505 * Zero
7507 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7509 LVHITTESTINFO lvHitTestInfo;
7510 LONG lStyle = infoPtr->dwStyle;
7511 static BOOL bGroupSelect = TRUE;
7512 POINT pt = { pts.x, pts.y };
7513 INT nItem;
7515 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7517 /* send NM_RELEASEDCAPTURE notification */
7518 notify(infoPtr, NM_RELEASEDCAPTURE);
7520 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7522 /* set left button down flag */
7523 infoPtr->bLButtonDown = TRUE;
7525 lvHitTestInfo.pt.x = pts.x;
7526 lvHitTestInfo.pt.y = pts.y;
7528 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7529 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7530 infoPtr->nEditLabelItem = -1;
7531 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7533 if (lStyle & LVS_SINGLESEL)
7535 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7536 infoPtr->nEditLabelItem = nItem;
7537 else
7538 LISTVIEW_SetSelection(infoPtr, nItem);
7540 else
7542 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7544 if (bGroupSelect)
7545 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7546 else
7548 LVITEMW item;
7550 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7551 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7553 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7554 infoPtr->nSelectionMark = nItem;
7557 else if (wKey & MK_CONTROL)
7559 LVITEMW item;
7561 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7563 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7564 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7565 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7566 infoPtr->nSelectionMark = nItem;
7568 else if (wKey & MK_SHIFT)
7570 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7572 else
7574 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7575 infoPtr->nEditLabelItem = nItem;
7577 /* set selection (clears other pre-existing selections) */
7578 LISTVIEW_SetSelection(infoPtr, nItem);
7582 else
7584 /* remove all selections */
7585 LISTVIEW_DeselectAll(infoPtr);
7588 return 0;
7591 /***
7592 * DESCRIPTION:
7593 * Processes mouse up messages (left mouse button).
7595 * PARAMETER(S):
7596 * [I] infoPtr : valid pointer to the listview structure
7597 * [I] wKey : key flag
7598 * [I] pts : mouse coordinate
7600 * RETURN:
7601 * Zero
7603 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7605 LVHITTESTINFO lvHitTestInfo;
7607 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7609 if (!infoPtr->bLButtonDown) return 0;
7611 lvHitTestInfo.pt.x = pts.x;
7612 lvHitTestInfo.pt.y = pts.y;
7614 /* send NM_CLICK notification */
7615 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7616 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7618 /* set left button flag */
7619 infoPtr->bLButtonDown = FALSE;
7621 /* if we clicked on a selected item, edit the label */
7622 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7623 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7625 return 0;
7628 /***
7629 * DESCRIPTION:
7630 * Destroys the listview control (called after WM_DESTROY).
7632 * PARAMETER(S):
7633 * [I] infoPtr : valid pointer to the listview structure
7635 * RETURN:
7636 * Zero
7638 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7640 LONG lStyle = infoPtr->dwStyle;
7642 TRACE("()\n");
7644 /* delete all items */
7645 LISTVIEW_DeleteAllItems(infoPtr);
7647 /* destroy data structure */
7648 DPA_Destroy(infoPtr->hdpaItems);
7649 ranges_destroy(infoPtr->selectionRanges);
7651 /* destroy image lists */
7652 if (!(lStyle & LVS_SHAREIMAGELISTS))
7654 if (infoPtr->himlNormal)
7655 ImageList_Destroy(infoPtr->himlNormal);
7656 if (infoPtr->himlSmall)
7657 ImageList_Destroy(infoPtr->himlSmall);
7658 if (infoPtr->himlState)
7659 ImageList_Destroy(infoPtr->himlState);
7662 /* destroy font, bkgnd brush */
7663 infoPtr->hFont = 0;
7664 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7665 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7667 /* free listview info pointer*/
7668 COMCTL32_Free(infoPtr);
7670 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7671 return 0;
7674 /***
7675 * DESCRIPTION:
7676 * Handles notifications from children.
7678 * PARAMETER(S):
7679 * [I] infoPtr : valid pointer to the listview structure
7680 * [I] nCtrlId : control identifier
7681 * [I] lpnmh : notification information
7683 * RETURN:
7684 * Zero
7686 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7688 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7690 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7692 /* handle notification from header control */
7693 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7695 LPNMHEADERW lphnm = (LPNMHEADERW)lpnmh;
7697 if (lpnmh->code == HDN_TRACKW || lpnmh->code == HDN_TRACKA)
7699 COLUMN_INFO *lpColumnInfo;
7700 RECT rcCol;
7701 INT dx;
7703 if (!(lpColumnInfo = DPA_GetPtr(infoPtr->hdpaColumns, lphnm->iItem))) return 0;
7704 if (!(lphnm->pitem->mask & HDI_WIDTH)) return 0;
7706 /* determine how much we change since the last know position */
7707 dx = lphnm->pitem->cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7709 /* ajust the column being tracked */
7710 lpColumnInfo->rcHeader.right += dx;
7712 /* compute the rectangle for the tracked column */
7713 rcCol.left = lpColumnInfo->rcHeader.left;
7714 rcCol.top = infoPtr->rcList.top;
7715 rcCol.right = lpColumnInfo->rcHeader.right;
7716 rcCol.bottom = infoPtr->rcList.bottom;
7718 LISTVIEW_ScrollColumns(infoPtr, lphnm->iItem + 1, dx);
7719 if (uView == LVS_REPORT) LISTVIEW_InvalidateRect(infoPtr, &rcCol);
7721 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7723 /* Handle sorting by Header Column */
7724 NMLISTVIEW nmlv;
7726 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7727 nmlv.iItem = -1;
7728 nmlv.iSubItem = lphnm->iItem;
7729 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7733 return 0;
7736 /***
7737 * DESCRIPTION:
7738 * Determines the type of structure to use.
7740 * PARAMETER(S):
7741 * [I] infoPtr : valid pointer to the listview structureof the sender
7742 * [I] hwndFrom : listview window handle
7743 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
7745 * RETURN:
7746 * Zero
7748 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7750 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7752 if (nCommand == NF_REQUERY)
7753 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7754 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7755 return 0;
7758 /***
7759 * DESCRIPTION:
7760 * Paints/Repaints the listview control.
7762 * PARAMETER(S):
7763 * [I] infoPtr : valid pointer to the listview structure
7764 * [I] hdc : device context handle
7766 * RETURN:
7767 * Zero
7769 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7771 TRACE("(hdc=%x)\n", hdc);
7773 if (hdc)
7774 LISTVIEW_Refresh(infoPtr, hdc);
7775 else
7777 PAINTSTRUCT ps;
7779 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7780 if (!hdc) return 1;
7781 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7782 LISTVIEW_Refresh(infoPtr, hdc);
7783 EndPaint(infoPtr->hwndSelf, &ps);
7786 return 0;
7789 /***
7790 * DESCRIPTION:
7791 * Processes double click messages (right mouse button).
7793 * PARAMETER(S):
7794 * [I] infoPtr : valid pointer to the listview structure
7795 * [I] wKey : key flag
7796 * [I] pts : mouse coordinate
7798 * RETURN:
7799 * Zero
7801 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7803 LVHITTESTINFO lvHitTestInfo;
7805 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7807 /* send NM_RELEASEDCAPTURE notification */
7808 notify(infoPtr, NM_RELEASEDCAPTURE);
7810 /* send NM_RDBLCLK notification */
7811 lvHitTestInfo.pt.x = pts.x;
7812 lvHitTestInfo.pt.y = pts.y;
7813 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7814 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
7816 return 0;
7819 /***
7820 * DESCRIPTION:
7821 * Processes mouse down messages (right mouse button).
7823 * PARAMETER(S):
7824 * [I] infoPtr : valid pointer to the listview structure
7825 * [I] wKey : key flag
7826 * [I] pts : mouse coordinate
7828 * RETURN:
7829 * Zero
7831 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7833 LVHITTESTINFO lvHitTestInfo;
7834 INT nItem;
7836 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7838 /* send NM_RELEASEDCAPTURE notification */
7839 notify(infoPtr, NM_RELEASEDCAPTURE);
7841 /* make sure the listview control window has the focus */
7842 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7844 /* set right button down flag */
7845 infoPtr->bRButtonDown = TRUE;
7847 /* determine the index of the selected item */
7848 lvHitTestInfo.pt.x = pts.x;
7849 lvHitTestInfo.pt.y = pts.y;
7850 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7852 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7854 LISTVIEW_SetItemFocus(infoPtr, nItem);
7855 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7856 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7857 LISTVIEW_SetSelection(infoPtr, nItem);
7859 else
7861 LISTVIEW_DeselectAll(infoPtr);
7864 return 0;
7867 /***
7868 * DESCRIPTION:
7869 * Processes mouse up messages (right mouse button).
7871 * PARAMETER(S):
7872 * [I] infoPtr : valid pointer to the listview structure
7873 * [I] wKey : key flag
7874 * [I] pts : mouse coordinate
7876 * RETURN:
7877 * Zero
7879 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7881 LVHITTESTINFO lvHitTestInfo;
7882 POINT pt;
7884 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7886 if (!infoPtr->bRButtonDown) return 0;
7888 /* set button flag */
7889 infoPtr->bRButtonDown = FALSE;
7891 /* Send NM_RClICK notification */
7892 lvHitTestInfo.pt.x = pts.x;
7893 lvHitTestInfo.pt.y = pts.y;
7894 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7895 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
7897 /* Change to screen coordinate for WM_CONTEXTMENU */
7898 pt = lvHitTestInfo.pt;
7899 ClientToScreen(infoPtr->hwndSelf, &pt);
7901 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
7902 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
7903 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
7905 return 0;
7909 /***
7910 * DESCRIPTION:
7911 * Sets the cursor.
7913 * PARAMETER(S):
7914 * [I] infoPtr : valid pointer to the listview structure
7915 * [I] hwnd : window handle of window containing the cursor
7916 * [I] nHittest : hit-test code
7917 * [I] wMouseMsg : ideintifier of the mouse message
7919 * RETURN:
7920 * TRUE if cursor is set
7921 * FALSE otherwise
7923 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
7925 LVHITTESTINFO lvHitTestInfo;
7927 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
7929 if(!infoPtr->hHotCursor) return FALSE;
7931 GetCursorPos(&lvHitTestInfo.pt);
7932 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
7934 SetCursor(infoPtr->hHotCursor);
7936 return TRUE;
7939 /***
7940 * DESCRIPTION:
7941 * Sets the focus.
7943 * PARAMETER(S):
7944 * [I] infoPtr : valid pointer to the listview structure
7945 * [I] hwndLoseFocus : handle of previously focused window
7947 * RETURN:
7948 * Zero
7950 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
7952 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
7954 /* if we have the focus already, there's nothing to do */
7955 if (infoPtr->bFocus) return 0;
7957 /* send NM_SETFOCUS notification */
7958 notify(infoPtr, NM_SETFOCUS);
7960 /* set window focus flag */
7961 infoPtr->bFocus = TRUE;
7963 /* put the focus rect back on */
7964 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
7966 /* redraw all visible selected items */
7967 LISTVIEW_InvalidateSelectedItems(infoPtr);
7969 return 0;
7972 /***
7973 * DESCRIPTION:
7974 * Sets the font.
7976 * PARAMETER(S):
7977 * [I] infoPtr : valid pointer to the listview structure
7978 * [I] fRedraw : font handle
7979 * [I] fRedraw : redraw flag
7981 * RETURN:
7982 * Zero
7984 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
7986 HFONT oldFont = infoPtr->hFont;
7988 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
7990 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
7991 if (infoPtr->hFont == oldFont) return 0;
7993 LISTVIEW_SaveTextMetrics(infoPtr);
7995 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
7996 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
7998 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8000 return 0;
8003 /***
8004 * DESCRIPTION:
8005 * Message handling for WM_SETREDRAW.
8006 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8008 * PARAMETER(S):
8009 * [I] infoPtr : valid pointer to the listview structure
8010 * [I] bRedraw: state of redraw flag
8012 * RETURN:
8013 * DefWinProc return value
8015 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8017 infoPtr->bRedraw = bRedraw;
8018 if(bRedraw)
8019 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
8020 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8021 return 0;
8024 /***
8025 * DESCRIPTION:
8026 * Resizes the listview control. This function processes WM_SIZE
8027 * messages. At this time, the width and height are not used.
8029 * PARAMETER(S):
8030 * [I] infoPtr : valid pointer to the listview structure
8031 * [I] Width : new width
8032 * [I] Height : new height
8034 * RETURN:
8035 * Zero
8037 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8039 LONG lStyle = infoPtr->dwStyle;
8040 UINT uView = lStyle & LVS_TYPEMASK;
8042 TRACE("(width=%d, height=%d)\n", Width, Height);
8044 if (LISTVIEW_UpdateSize(infoPtr))
8046 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8048 if (lStyle & LVS_ALIGNLEFT)
8049 LISTVIEW_AlignLeft(infoPtr);
8050 else
8051 LISTVIEW_AlignTop(infoPtr);
8054 LISTVIEW_UpdateScroll(infoPtr);
8056 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8059 return 0;
8062 /***
8063 * DESCRIPTION:
8064 * Sets the size information.
8066 * PARAMETER(S):
8067 * [I] infoPtr : valid pointer to the listview structure
8069 * RETURN:
8070 * Zero if no size change
8071 * 1 of size changed
8073 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8075 LONG lStyle = infoPtr->dwStyle;
8076 UINT uView = lStyle & LVS_TYPEMASK;
8077 RECT rcList;
8078 RECT rcOld;
8080 GetClientRect(infoPtr->hwndSelf, &rcList);
8081 CopyRect(&rcOld,&(infoPtr->rcList));
8082 infoPtr->rcList.left = 0;
8083 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8084 infoPtr->rcList.top = 0;
8085 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8087 if (uView == LVS_LIST)
8089 /* Apparently the "LIST" style is supposed to have the same
8090 * number of items in a column even if there is no scroll bar.
8091 * Since if a scroll bar already exists then the bottom is already
8092 * reduced, only reduce if the scroll bar does not currently exist.
8093 * The "2" is there to mimic the native control. I think it may be
8094 * related to either padding or edges. (GLA 7/2002)
8096 if (!(lStyle & WS_HSCROLL))
8098 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8099 if (infoPtr->rcList.bottom > nHScrollHeight)
8100 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8102 else
8104 if (infoPtr->rcList.bottom > 2)
8105 infoPtr->rcList.bottom -= 2;
8108 else if (uView == LVS_REPORT)
8110 HDLAYOUT hl;
8111 WINDOWPOS wp;
8113 hl.prc = &rcList;
8114 hl.pwpos = &wp;
8115 Header_Layout(infoPtr->hwndHeader, &hl);
8117 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8119 if (!(LVS_NOCOLUMNHEADER & lStyle))
8120 infoPtr->rcList.top = max(wp.cy, 0);
8122 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8125 /***
8126 * DESCRIPTION:
8127 * Processes WM_STYLECHANGED messages.
8129 * PARAMETER(S):
8130 * [I] infoPtr : valid pointer to the listview structure
8131 * [I] wStyleType : window style type (normal or extended)
8132 * [I] lpss : window style information
8134 * RETURN:
8135 * Zero
8137 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8138 LPSTYLESTRUCT lpss)
8140 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8141 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8143 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8144 wStyleType, lpss->styleOld, lpss->styleNew);
8146 if (wStyleType != GWL_STYLE) return 0;
8148 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8149 /* what if LVS_OWNERDATA changed? */
8150 /* or LVS_SINGLESEL */
8151 /* or LVS_SORT{AS,DES}CENDING */
8153 infoPtr->dwStyle = lpss->styleNew;
8155 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8156 ((lpss->styleNew & WS_HSCROLL) == 0))
8157 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8159 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8160 ((lpss->styleNew & WS_VSCROLL) == 0))
8161 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8163 if (uNewView != uOldView)
8165 SIZE oldIconSize = infoPtr->iconSize;
8166 HIMAGELIST himl;
8168 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8169 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8171 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8172 SetRectEmpty(&infoPtr->rcFocus);
8174 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8175 update_icon_size(himl, uNewView != LVS_ICON, &infoPtr->iconSize);
8177 if (uNewView == LVS_ICON)
8179 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8181 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8182 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8183 LISTVIEW_SetIconSpacing(infoPtr, 0);
8186 else if (uNewView == LVS_REPORT)
8188 HDLAYOUT hl;
8189 WINDOWPOS wp;
8191 hl.prc = &infoPtr->rcList;
8192 hl.pwpos = &wp;
8193 Header_Layout(infoPtr->hwndHeader, &hl);
8194 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8197 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8198 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8201 if (uNewView == LVS_REPORT)
8202 ShowWindow(infoPtr->hwndHeader, (LVS_NOCOLUMNHEADER & lpss->styleNew) ? SW_HIDE : SW_SHOWNORMAL);
8204 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8205 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8207 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
8208 LISTVIEW_AlignLeft(infoPtr);
8209 else
8210 LISTVIEW_AlignTop(infoPtr);
8213 /* update the size of the client area */
8214 LISTVIEW_UpdateSize(infoPtr);
8216 /* add scrollbars if needed */
8217 LISTVIEW_UpdateScroll(infoPtr);
8219 /* invalidate client area + erase background */
8220 LISTVIEW_InvalidateList(infoPtr);
8222 /* print the list of unsupported window styles */
8223 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8225 return 0;
8228 /***
8229 * DESCRIPTION:
8230 * Window procedure of the listview control.
8233 static LRESULT WINAPI
8234 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8236 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8238 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8240 if (!infoPtr && (uMsg != WM_CREATE))
8241 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8243 if (infoPtr)
8245 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8248 switch (uMsg)
8250 case LVM_APPROXIMATEVIEWRECT:
8251 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8252 LOWORD(lParam), HIWORD(lParam));
8253 case LVM_ARRANGE:
8254 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8256 /* case LVN_CANCELEDITLABEL */
8258 /* case LVM_CREATEDRAGIMAGE: */
8260 case LVM_DELETEALLITEMS:
8261 return LISTVIEW_DeleteAllItems(infoPtr);
8263 case LVM_DELETECOLUMN:
8264 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8266 case LVM_DELETEITEM:
8267 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8269 case LVM_EDITLABELW:
8270 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8272 case LVM_EDITLABELA:
8273 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8275 /* case LVN_ENABLEGROUPVIEW: */
8277 case LVM_ENSUREVISIBLE:
8278 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8280 case LVM_FINDITEMW:
8281 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8283 case LVM_FINDITEMA:
8284 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8286 case LVM_GETBKCOLOR:
8287 return infoPtr->clrBk;
8289 /* case LVM_GETBKIMAGE: */
8291 case LVM_GETCALLBACKMASK:
8292 return infoPtr->uCallbackMask;
8294 case LVM_GETCOLUMNA:
8295 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8297 case LVM_GETCOLUMNW:
8298 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8300 case LVM_GETCOLUMNORDERARRAY:
8301 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8303 case LVM_GETCOLUMNWIDTH:
8304 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8306 case LVM_GETCOUNTPERPAGE:
8307 return LISTVIEW_GetCountPerPage(infoPtr);
8309 case LVM_GETEDITCONTROL:
8310 return (LRESULT)infoPtr->hwndEdit;
8312 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8313 return infoPtr->dwLvExStyle;
8315 case LVM_GETHEADER:
8316 return (LRESULT)infoPtr->hwndHeader;
8318 case LVM_GETHOTCURSOR:
8319 return (LRESULT)infoPtr->hHotCursor;
8321 case LVM_GETHOTITEM:
8322 return infoPtr->nHotItem;
8324 case LVM_GETHOVERTIME:
8325 return infoPtr->dwHoverTime;
8327 case LVM_GETIMAGELIST:
8328 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8330 /* case LVN_GETINSERTMARK: */
8332 /* case LVN_GETINSERTMARKCOLOR: */
8334 /* case LVN_GETINSERTMARKRECT: */
8336 case LVM_GETISEARCHSTRINGA:
8337 case LVM_GETISEARCHSTRINGW:
8338 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8339 return FALSE;
8341 case LVM_GETITEMA:
8342 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8344 case LVM_GETITEMW:
8345 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8347 case LVM_GETITEMCOUNT:
8348 return infoPtr->nItemCount;
8350 case LVM_GETITEMPOSITION:
8351 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8353 case LVM_GETITEMRECT:
8354 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8356 case LVM_GETITEMSPACING:
8357 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8359 case LVM_GETITEMSTATE:
8360 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8362 case LVM_GETITEMTEXTA:
8363 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8365 case LVM_GETITEMTEXTW:
8366 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8368 case LVM_GETNEXTITEM:
8369 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8371 case LVM_GETNUMBEROFWORKAREAS:
8372 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8373 return 1;
8375 case LVM_GETORIGIN:
8376 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8378 /* case LVN_GETOUTLINECOLOR: */
8380 /* case LVM_GETSELECTEDCOLUMN: */
8382 case LVM_GETSELECTEDCOUNT:
8383 return LISTVIEW_GetSelectedCount(infoPtr);
8385 case LVM_GETSELECTIONMARK:
8386 return infoPtr->nSelectionMark;
8388 case LVM_GETSTRINGWIDTHA:
8389 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8391 case LVM_GETSTRINGWIDTHW:
8392 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8394 case LVM_GETSUBITEMRECT:
8395 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8397 case LVM_GETTEXTBKCOLOR:
8398 return infoPtr->clrTextBk;
8400 case LVM_GETTEXTCOLOR:
8401 return infoPtr->clrText;
8403 /* case LVN_GETTILEINFO: */
8405 /* case LVN_GETTILEVIEWINFO: */
8407 case LVM_GETTOOLTIPS:
8408 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8409 return FALSE;
8411 case LVM_GETTOPINDEX:
8412 return LISTVIEW_GetTopIndex(infoPtr);
8414 /*case LVM_GETUNICODEFORMAT:
8415 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8416 return FALSE;*/
8418 case LVM_GETVIEWRECT:
8419 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8421 case LVM_GETWORKAREAS:
8422 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8423 return FALSE;
8425 /* case LVN_HASGROUP: */
8427 case LVM_HITTEST:
8428 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8430 case LVM_INSERTCOLUMNA:
8431 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8433 case LVM_INSERTCOLUMNW:
8434 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8436 /* case LVN_INSERTGROUP: */
8438 /* case LVN_INSERTGROUPSORTED: */
8440 case LVM_INSERTITEMA:
8441 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8443 case LVM_INSERTITEMW:
8444 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8446 /* case LVN_INSERTMARKHITTEST: */
8448 /* case LVN_ISGROUPVIEWENABLED: */
8450 /* case LVN_MAPIDTOINDEX: */
8452 /* case LVN_INEDXTOID: */
8454 /* case LVN_MOVEGROUP: */
8456 /* case LVN_MOVEITEMTOGROUP: */
8458 case LVM_REDRAWITEMS:
8459 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8461 /* case LVN_REMOVEALLGROUPS: */
8463 /* case LVN_REMOVEGROUP: */
8465 case LVM_SCROLL:
8466 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8468 case LVM_SETBKCOLOR:
8469 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8471 /* case LVM_SETBKIMAGE: */
8473 case LVM_SETCALLBACKMASK:
8474 infoPtr->uCallbackMask = (UINT)wParam;
8475 return TRUE;
8477 case LVM_SETCOLUMNA:
8478 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8480 case LVM_SETCOLUMNW:
8481 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8483 case LVM_SETCOLUMNORDERARRAY:
8484 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8486 case LVM_SETCOLUMNWIDTH:
8487 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8489 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8490 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8492 /* case LVN_SETGROUPINFO: */
8494 /* case LVN_SETGROUPMETRICS: */
8496 case LVM_SETHOTCURSOR:
8497 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8499 case LVM_SETHOTITEM:
8500 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8502 case LVM_SETHOVERTIME:
8503 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8505 case LVM_SETICONSPACING:
8506 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8508 case LVM_SETIMAGELIST:
8509 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8511 /* case LVN_SETINFOTIP: */
8513 /* case LVN_SETINSERTMARK: */
8515 /* case LVN_SETINSERTMARKCOLOR: */
8517 case LVM_SETITEMA:
8518 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8520 case LVM_SETITEMW:
8521 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8523 case LVM_SETITEMCOUNT:
8524 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8526 case LVM_SETITEMPOSITION:
8528 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8529 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8532 case LVM_SETITEMPOSITION32:
8533 if (lParam == 0) return FALSE;
8534 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8536 case LVM_SETITEMSTATE:
8537 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8539 case LVM_SETITEMTEXTA:
8540 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8542 case LVM_SETITEMTEXTW:
8543 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8545 /* case LVN_SETOUTLINECOLOR: */
8547 /* case LVN_SETSELECTEDCOLUMN: */
8549 case LVM_SETSELECTIONMARK:
8550 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8552 case LVM_SETTEXTBKCOLOR:
8553 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8555 case LVM_SETTEXTCOLOR:
8556 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8558 /* case LVN_SETTILEINFO: */
8560 /* case LVN_SETTILEVIEWINFO: */
8562 /* case LVN_SETTILEWIDTH: */
8564 /* case LVM_SETTOOLTIPS: */
8566 /* case LVM_SETUNICODEFORMAT: */
8568 /* case LVN_SETVIEW: */
8570 /* case LVM_SETWORKAREAS: */
8572 /* case LVN_SORTGROUPS: */
8574 case LVM_SORTITEMS:
8575 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8577 case LVM_SUBITEMHITTEST:
8578 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8580 case LVM_UPDATE:
8581 return LISTVIEW_Update(infoPtr, (INT)wParam);
8583 case WM_CHAR:
8584 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8586 case WM_COMMAND:
8587 return LISTVIEW_Command(infoPtr, wParam, lParam);
8589 case WM_CREATE:
8590 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8592 case WM_ERASEBKGND:
8593 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8595 case WM_GETDLGCODE:
8596 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8598 case WM_GETFONT:
8599 return infoPtr->hFont;
8601 case WM_HSCROLL:
8602 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8604 case WM_KEYDOWN:
8605 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8607 case WM_KILLFOCUS:
8608 return LISTVIEW_KillFocus(infoPtr);
8610 case WM_LBUTTONDBLCLK:
8611 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8613 case WM_LBUTTONDOWN:
8614 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8616 case WM_LBUTTONUP:
8617 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8619 case WM_MOUSEMOVE:
8620 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8622 case WM_MOUSEHOVER:
8623 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8625 case WM_NCDESTROY:
8626 return LISTVIEW_NCDestroy(infoPtr);
8628 case WM_NOTIFY:
8629 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8631 case WM_NOTIFYFORMAT:
8632 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8634 case WM_PAINT:
8635 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8637 case WM_RBUTTONDBLCLK:
8638 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8640 case WM_RBUTTONDOWN:
8641 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8643 case WM_RBUTTONUP:
8644 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8646 case WM_SETCURSOR:
8647 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8648 return TRUE;
8649 goto fwd_msg;
8651 case WM_SETFOCUS:
8652 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8654 case WM_SETFONT:
8655 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8657 case WM_SETREDRAW:
8658 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8660 case WM_SIZE:
8661 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8663 case WM_STYLECHANGED:
8664 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8666 case WM_SYSCOLORCHANGE:
8667 COMCTL32_RefreshSysColors();
8668 return 0;
8670 /* case WM_TIMER: */
8672 case WM_VSCROLL:
8673 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8675 case WM_MOUSEWHEEL:
8676 if (wParam & (MK_SHIFT | MK_CONTROL))
8677 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8678 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8680 case WM_WINDOWPOSCHANGED:
8681 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8682 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8683 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8684 LISTVIEW_UpdateSize(infoPtr);
8685 LISTVIEW_UpdateScroll(infoPtr);
8687 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8689 /* case WM_WININICHANGE: */
8691 default:
8692 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8693 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8695 fwd_msg:
8696 /* call default window procedure */
8697 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8700 return 0;
8703 /***
8704 * DESCRIPTION:
8705 * Registers the window class.
8707 * PARAMETER(S):
8708 * None
8710 * RETURN:
8711 * None
8713 void LISTVIEW_Register(void)
8715 WNDCLASSW wndClass;
8717 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8718 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8719 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8720 wndClass.cbClsExtra = 0;
8721 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8722 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8723 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8724 wndClass.lpszClassName = WC_LISTVIEWW;
8725 RegisterClassW(&wndClass);
8728 /***
8729 * DESCRIPTION:
8730 * Unregisters the window class.
8732 * PARAMETER(S):
8733 * None
8735 * RETURN:
8736 * None
8738 void LISTVIEW_Unregister(void)
8740 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8743 /***
8744 * DESCRIPTION:
8745 * Handle any WM_COMMAND messages
8747 * PARAMETER(S):
8748 * [I] infoPtr : valid pointer to the listview structure
8749 * [I] wParam : the first message parameter
8750 * [I] lParam : the second message parameter
8752 * RETURN:
8754 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8756 switch (HIWORD(wParam))
8758 case EN_UPDATE:
8761 * Adjust the edit window size
8763 WCHAR buffer[1024];
8764 HDC hdc = GetDC(infoPtr->hwndEdit);
8765 HFONT hFont, hOldFont = 0;
8766 RECT rect;
8767 SIZE sz;
8768 int len;
8770 if (!infoPtr->hwndEdit || !hdc) return 0;
8771 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8772 GetWindowRect(infoPtr->hwndEdit, &rect);
8774 /* Select font to get the right dimension of the string */
8775 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8776 if(hFont != 0)
8778 hOldFont = SelectObject(hdc, hFont);
8781 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8783 TEXTMETRICW textMetric;
8785 /* Add Extra spacing for the next character */
8786 GetTextMetricsW(hdc, &textMetric);
8787 sz.cx += (textMetric.tmMaxCharWidth * 2);
8789 SetWindowPos (
8790 infoPtr->hwndEdit,
8791 HWND_TOP,
8794 sz.cx,
8795 rect.bottom - rect.top,
8796 SWP_DRAWFRAME|SWP_NOMOVE);
8798 if(hFont != 0)
8799 SelectObject(hdc, hOldFont);
8801 ReleaseDC(infoPtr->hwndSelf, hdc);
8803 break;
8806 default:
8807 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8810 return 0;
8814 /***
8815 * DESCRIPTION:
8816 * Subclassed edit control windproc function
8818 * PARAMETER(S):
8819 * [I] hwnd : the edit window handle
8820 * [I] uMsg : the message that is to be processed
8821 * [I] wParam : first message parameter
8822 * [I] lParam : second message parameter
8823 * [I] isW : TRUE if input is Unicode
8825 * RETURN:
8827 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
8829 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8830 BOOL cancel = FALSE;
8832 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8833 hwnd, uMsg, wParam, lParam, isW);
8835 switch (uMsg)
8837 case WM_GETDLGCODE:
8838 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8840 case WM_KILLFOCUS:
8841 break;
8843 case WM_DESTROY:
8845 WNDPROC editProc = infoPtr->EditWndProc;
8846 infoPtr->EditWndProc = 0;
8847 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
8848 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
8851 case WM_KEYDOWN:
8852 if (VK_ESCAPE == (INT)wParam)
8854 cancel = TRUE;
8855 break;
8857 else if (VK_RETURN == (INT)wParam)
8858 break;
8860 default:
8861 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
8864 /* kill the edit */
8865 if (infoPtr->hwndEdit)
8867 LPWSTR buffer = NULL;
8869 infoPtr->hwndEdit = 0;
8870 if (!cancel)
8872 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
8874 if (len)
8876 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
8878 if (isW) GetWindowTextW(hwnd, buffer, len+1);
8879 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
8883 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
8885 if (buffer) COMCTL32_Free(buffer);
8889 SendMessageW(hwnd, WM_CLOSE, 0, 0);
8890 return TRUE;
8893 /***
8894 * DESCRIPTION:
8895 * Subclassed edit control Unicode windproc function
8897 * PARAMETER(S):
8898 * [I] hwnd : the edit window handle
8899 * [I] uMsg : the message that is to be processed
8900 * [I] wParam : first message parameter
8901 * [I] lParam : second message parameter
8903 * RETURN:
8905 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8907 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
8910 /***
8911 * DESCRIPTION:
8912 * Subclassed edit control ANSI windproc function
8914 * PARAMETER(S):
8915 * [I] hwnd : the edit window handle
8916 * [I] uMsg : the message that is to be processed
8917 * [I] wParam : first message parameter
8918 * [I] lParam : second message parameter
8920 * RETURN:
8922 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8924 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
8927 /***
8928 * DESCRIPTION:
8929 * Creates a subclassed edit cotrol
8931 * PARAMETER(S):
8932 * [I] infoPtr : valid pointer to the listview structure
8933 * [I] text : initial text for the edit
8934 * [I] style : the window style
8935 * [I] isW : TRUE if input is Unicode
8937 * RETURN:
8939 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
8940 INT x, INT y, INT width, INT height, BOOL isW)
8942 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
8943 HWND hedit;
8944 SIZE sz;
8945 HDC hdc;
8946 HDC hOldFont=0;
8947 TEXTMETRICW textMetric;
8948 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
8950 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
8952 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
8953 hdc = GetDC(infoPtr->hwndSelf);
8955 /* Select the font to get appropriate metric dimensions */
8956 if(infoPtr->hFont != 0)
8957 hOldFont = SelectObject(hdc, infoPtr->hFont);
8959 /*Get String Lenght in pixels */
8960 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
8962 /*Add Extra spacing for the next character */
8963 GetTextMetricsW(hdc, &textMetric);
8964 sz.cx += (textMetric.tmMaxCharWidth * 2);
8966 if(infoPtr->hFont != 0)
8967 SelectObject(hdc, hOldFont);
8969 ReleaseDC(infoPtr->hwndSelf, hdc);
8970 if (isW)
8971 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8972 else
8973 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8975 if (!hedit) return 0;
8977 infoPtr->EditWndProc = (WNDPROC)
8978 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
8979 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
8981 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
8983 return hedit;