Notification code cleanup, more traces, etc.
[wine/wine-kai.git] / dlls / comctl32 / listview.c
blob5639f773788a7d759d356eb5c3c93646089dc05b
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 * -- Drawing optimizations.
29 * -- Hot item handling.
30 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
31 * -- Support CustonDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs)
33 * Notifications:
34 * LISTVIEW_Notify : most notifications from children (editbox and header)
36 * Data structure:
37 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
39 * Advanced functionality:
40 * LISTVIEW_GetNumberOfWorkAreas : not implemented
41 * LISTVIEW_GetISearchString : not implemented
42 * LISTVIEW_GetBkImage : not implemented
43 * LISTVIEW_SetBkImage : not implemented
44 * LISTVIEW_GetColumnOrderArray : simple hack only
45 * LISTVIEW_SetColumnOrderArray : simple hack only
46 * LISTVIEW_Arrange : empty stub
47 * LISTVIEW_ApproximateViewRect : incomplete
48 * LISTVIEW_Update : not completed
50 * Known differences in message stream from native control (not known if
51 * these differences cause problems):
52 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
53 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
54 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
55 * processing for "USEDOUBLECLICKTIME".
59 #include "config.h"
60 #include "wine/port.h"
62 #include <assert.h>
63 #include <ctype.h>
64 #include <string.h>
65 #include <stdlib.h>
66 #include <stdio.h>
68 #include "winbase.h"
69 #include "winnt.h"
70 #include "heap.h"
71 #include "commctrl.h"
72 #include "comctl32.h"
73 #include "wine/debug.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(listview);
77 typedef struct tagCOLUMNCACHE
79 RECT rc;
80 UINT align;
81 } COLUMNCACHE, *LPCOLUMNCACHE;
83 typedef struct tagITEMHDR
85 LPWSTR pszText;
86 INT iImage;
87 } ITEMHDR, *LPITEMHDR;
89 typedef struct tagLISTVIEW_SUBITEM
91 ITEMHDR hdr;
92 INT iSubItem;
93 } LISTVIEW_SUBITEM;
95 typedef struct tagLISTVIEW_ITEM
97 ITEMHDR hdr;
98 UINT state;
99 LPARAM lParam;
100 INT iIndent;
101 BOOL valid;
102 } LISTVIEW_ITEM;
104 typedef struct tagRANGE
106 INT lower;
107 INT upper;
108 } RANGE;
110 typedef struct tagITERATOR
112 INT nItem;
113 INT nSpecial;
114 RANGE range;
115 HDPA ranges;
116 INT index;
117 } ITERATOR;
119 typedef struct tagLISTVIEW_INFO
121 HWND hwndSelf;
122 HBRUSH hBkBrush;
123 COLORREF clrBk;
124 COLORREF clrText;
125 COLORREF clrTextBk;
126 COLORREF clrTextBkDefault;
127 HIMAGELIST himlNormal;
128 HIMAGELIST himlSmall;
129 HIMAGELIST himlState;
130 BOOL bLButtonDown;
131 BOOL bRButtonDown;
132 INT nItemHeight;
133 INT nItemWidth;
134 HDPA hdpaSelectionRanges;
135 INT nSelectionMark;
136 BOOL bRemovingAllSelections;
137 INT nHotItem;
138 SHORT notifyFormat;
139 RECT rcList; /* This rectangle is really the window
140 * client rectangle possibly reduced by the
141 * horizontal scroll bar and/or header - see
142 * LISTVIEW_UpdateSize. This rectangle offset
143 * by the LISTVIEW_GetOrigin value is in
144 * client coordinates */
145 RECT rcView; /* This rectangle contains all items -
146 * contructed in LISTVIEW_AlignTop and
147 * LISTVIEW_AlignLeft */
148 SIZE iconSize;
149 SIZE iconSpacing;
150 SIZE iconStateSize;
151 UINT uCallbackMask;
152 HWND hwndHeader;
153 HFONT hDefaultFont;
154 HCURSOR hHotCursor;
155 HFONT hFont;
156 INT ntmHeight; /* from GetTextMetrics from above font */
157 BOOL bRedraw;
158 BOOL bFocus;
159 INT nFocusedItem;
160 RECT rcFocus;
161 DWORD dwStyle; /* the cached window GWL_STYLE */
162 DWORD dwLvExStyle; /* extended listview style */
163 INT nItemCount;
164 HDPA hdpaItems;
165 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
166 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
167 PFNLVCOMPARE pfnCompare;
168 LPARAM lParamSort;
169 HWND hwndEdit;
170 WNDPROC EditWndProc;
171 INT nEditLabelItem;
172 DWORD dwHoverTime;
174 DWORD lastKeyPressTimestamp;
175 WPARAM charCode;
176 INT nSearchParamLength;
177 WCHAR szSearchParam[ MAX_PATH ];
178 BOOL bIsDrawing;
179 } LISTVIEW_INFO;
182 * constants
184 /* How many we debug buffer to allocate */
185 #define DEBUG_BUFFERS 20
186 /* The size of a single debug bbuffer */
187 #define DEBUG_BUFFER_SIZE 256
189 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
190 #define SB_INTERNAL -1
192 /* maximum size of a label */
193 #define DISP_TEXT_SIZE 512
195 /* padding for items in list and small icon display modes */
196 #define WIDTH_PADDING 12
198 /* padding for items in list, report and small icon display modes */
199 #define HEIGHT_PADDING 1
201 /* offset of items in report display mode */
202 #define REPORT_MARGINX 2
204 /* padding for icon in large icon display mode
205 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
206 * that HITTEST will see.
207 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
208 * ICON_TOP_PADDING - sum of the two above.
209 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
210 * LABEL_VERT_PADDING - between bottom of text and end of box
212 * ICON_LR_PADDING - additional width above icon size.
213 * ICON_LR_HALF - half of the above value
215 #define ICON_TOP_PADDING_NOTHITABLE 2
216 #define ICON_TOP_PADDING_HITABLE 2
217 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
218 #define ICON_BOTTOM_PADDING 4
219 #define LABEL_VERT_PADDING 7
220 #define ICON_LR_PADDING 16
221 #define ICON_LR_HALF (ICON_LR_PADDING/2)
223 /* default label width for items in list and small icon display modes */
224 #define DEFAULT_LABEL_WIDTH 40
226 /* default column width for items in list display mode */
227 #define DEFAULT_COLUMN_WIDTH 128
229 /* Size of "line" scroll for V & H scrolls */
230 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
232 /* Padding betwen image and label */
233 #define IMAGE_PADDING 2
235 /* Padding behind the label */
236 #define TRAILING_PADDING 5
238 /* Border for the icon caption */
239 #define CAPTION_BORDER 2
241 /* Standard DrawText flags */
242 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
243 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
244 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
246 /* The time in milisecods to reset the search in the list */
247 #define KEY_DELAY 450
249 /* Dump the LISTVIEW_INFO structure to the debug channel */
250 #define LISTVIEW_DUMP(iP) do { \
251 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
252 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
253 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
254 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
255 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
256 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
257 (iP->bFocus) ? "true" : "false"); \
258 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
259 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
260 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
261 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
262 iP->hwndSelf, \
263 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
264 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
265 } while(0)
269 * forward declarations
271 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
272 static BOOL LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
273 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
274 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
275 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
276 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *);
277 static BOOL LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
278 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
279 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
280 static INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *);
281 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
282 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *, INT);
283 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
284 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
285 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
286 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
287 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, POINT);
288 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
289 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
290 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
291 static void LISTVIEW_UnsupportedStyles(LONG);
292 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
293 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
294 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
295 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
296 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
297 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
298 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
299 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
300 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
301 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
302 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
303 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
304 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
306 /******** Text handling functions *************************************/
308 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
309 * text string. The string may be ANSI or Unicode, in which case
310 * the boolean isW tells us the type of the string.
312 * The name of the function tell what type of strings it expects:
313 * W: Unicode, T: ANSI/Unicode - function of isW
316 static inline BOOL is_textW(LPCWSTR text)
318 return text != NULL && text != LPSTR_TEXTCALLBACKW;
321 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
323 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
324 return is_textW(text);
327 static inline int textlenT(LPCWSTR text, BOOL isW)
329 return !is_textT(text, isW) ? 0 :
330 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
333 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
335 if (isDestW)
336 if (isSrcW) lstrcpynW(dest, src, max);
337 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
338 else
339 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
340 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
343 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
345 LPWSTR wstr = (LPWSTR)text;
347 if (!isW && is_textT(text, isW))
349 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
350 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
351 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
353 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
354 return wstr;
357 static inline void textfreeT(LPWSTR wstr, BOOL isW)
359 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
363 * dest is a pointer to a Unicode string
364 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
366 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
368 BOOL bResult = TRUE;
370 if (src == LPSTR_TEXTCALLBACKW)
372 if (is_textW(*dest)) COMCTL32_Free(*dest);
373 *dest = LPSTR_TEXTCALLBACKW;
375 else
377 LPWSTR pszText = textdupTtoW(src, isW);
378 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
379 bResult = Str_SetPtrW(dest, pszText);
380 textfreeT(pszText, isW);
382 return bResult;
386 * compares a Unicode to a Unicode/ANSI text string
388 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
390 if (!aw) return bt ? -1 : 0;
391 if (!bt) return aw ? 1 : 0;
392 if (aw == LPSTR_TEXTCALLBACKW)
393 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
394 if (bt != LPSTR_TEXTCALLBACKW)
396 LPWSTR bw = textdupTtoW(bt, isW);
397 int r = bw ? lstrcmpW(aw, bw) : 1;
398 textfreeT(bw, isW);
399 return r;
402 return 1;
405 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
407 int res;
409 n = min(min(n, strlenW(s1)), strlenW(s2));
410 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
411 return res ? res - sizeof(WCHAR) : res;
414 /******** Debugging functions *****************************************/
416 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
418 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
419 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
422 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
424 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
425 n = min(textlenT(text, isW), n);
426 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
429 static char* debug_getbuf()
431 static int index = 0;
432 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
433 return buffers[index++ % DEBUG_BUFFERS];
436 static inline char* debugpoint(const POINT* lppt)
438 if (lppt)
440 char* buf = debug_getbuf();
441 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
442 return buf;
443 } else return "(null)";
446 static inline char* debugrect(const RECT* rect)
448 if (rect)
450 char* buf = debug_getbuf();
451 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
452 rect->left, rect->top, rect->right, rect->bottom);
453 return buf;
454 } else return "(null)";
457 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
459 char* buf = debug_getbuf(), *text = buf;
460 int len, size = DEBUG_BUFFER_SIZE;
462 if (lpLVItem == NULL) return "(null)";
463 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
464 if (len == -1) goto end; buf += len; size -= len;
465 if (lpLVItem->mask & LVIF_STATE)
466 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
467 else len = 0;
468 if (len == -1) goto end; buf += len; size -= len;
469 if (lpLVItem->mask & LVIF_TEXT)
470 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
471 else len = 0;
472 if (len == -1) goto end; buf += len; size -= len;
473 if (lpLVItem->mask & LVIF_IMAGE)
474 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
475 else len = 0;
476 if (len == -1) goto end; buf += len; size -= len;
477 if (lpLVItem->mask & LVIF_PARAM)
478 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
479 else len = 0;
480 if (len == -1) goto end; buf += len; size -= len;
481 if (lpLVItem->mask & LVIF_INDENT)
482 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
483 else len = 0;
484 if (len == -1) goto end; buf += len; size -= len;
485 goto undo;
486 end:
487 buf = text + strlen(text);
488 undo:
489 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
490 return text;
493 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
495 char* buf = debug_getbuf(), *text = buf;
496 int len, size = DEBUG_BUFFER_SIZE;
498 if (lpColumn == NULL) return "(null)";
499 len = snprintf(buf, size, "{");
500 if (len == -1) goto end; buf += len; size -= len;
501 if (lpColumn->mask & LVCF_SUBITEM)
502 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
503 else len = 0;
504 if (len == -1) goto end; buf += len; size -= len;
505 if (lpColumn->mask & LVCF_FMT)
506 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
507 else len = 0;
508 if (len == -1) goto end; buf += len; size -= len;
509 if (lpColumn->mask & LVCF_WIDTH)
510 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
511 else len = 0;
512 if (len == -1) goto end; buf += len; size -= len;
513 if (lpColumn->mask & LVCF_TEXT)
514 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
515 else len = 0;
516 if (len == -1) goto end; buf += len; size -= len;
517 if (lpColumn->mask & LVCF_IMAGE)
518 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
519 else len = 0;
520 if (len == -1) goto end; buf += len; size -= len;
521 if (lpColumn->mask & LVCF_ORDER)
522 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
523 else len = 0;
524 if (len == -1) goto end; buf += len; size -= len;
525 goto undo;
526 end:
527 buf = text + strlen(text);
528 undo:
529 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
530 return text;
534 /******** Notification functions i************************************/
536 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
538 LRESULT result;
540 TRACE("(code=%d)\n", code);
542 pnmh->hwndFrom = infoPtr->hwndSelf;
543 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
544 pnmh->code = code;
545 result = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
546 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
548 TRACE(" <= %ld\n", result);
550 return result;
553 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
555 NMHDR nmh;
556 return notify_hdr(infoPtr, code, &nmh);
559 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
561 notify(infoPtr, LVN_ITEMACTIVATE);
564 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
566 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
569 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
571 NMLISTVIEW nmlv;
573 ZeroMemory(&nmlv, sizeof(nmlv));
574 nmlv.iItem = lvht->iItem;
575 nmlv.iSubItem = lvht->iSubItem;
576 nmlv.ptAction = lvht->pt;
577 return notify_listview(infoPtr, code, &nmlv);
580 static int get_ansi_notification(INT unicodeNotificationCode)
582 switch (unicodeNotificationCode)
584 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
585 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
586 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
587 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
588 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
589 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
591 ERR("unknown notification %x\n", unicodeNotificationCode);
592 return unicodeNotificationCode;
596 Send notification. depends on dispinfoW having same
597 structure as dispinfoA.
598 infoPtr : listview struct
599 notificationCode : *Unicode* notification code
600 pdi : dispinfo structure (can be unicode or ansi)
601 isW : TRUE if dispinfo is Unicode
603 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
605 BOOL bResult = FALSE;
606 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
607 INT realNotifCode;
608 INT cchTempBufMax = 0, savCchTextMax = 0;
609 LPWSTR pszTempBuf = NULL, savPszText = NULL;
611 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
613 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
614 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
617 if (convertToAnsi || convertToUnicode)
619 if (notificationCode != LVN_GETDISPINFOW)
621 cchTempBufMax = convertToUnicode ?
622 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
623 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
625 else
627 cchTempBufMax = pdi->item.cchTextMax;
628 *pdi->item.pszText = 0; /* make sure we don't process garbage */
631 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
632 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
633 if (!pszTempBuf) return FALSE;
634 if (convertToUnicode)
635 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
636 pszTempBuf, cchTempBufMax);
637 else
638 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
639 cchTempBufMax, NULL, NULL);
640 savCchTextMax = pdi->item.cchTextMax;
641 savPszText = pdi->item.pszText;
642 pdi->item.pszText = pszTempBuf;
643 pdi->item.cchTextMax = cchTempBufMax;
646 if (infoPtr->notifyFormat == NFR_ANSI)
647 realNotifCode = get_ansi_notification(notificationCode);
648 else
649 realNotifCode = notificationCode;
650 bResult = notify_hdr(infoPtr, realNotifCode, (LPNMHDR)pdi);
652 if (convertToUnicode || convertToAnsi)
654 if (convertToUnicode) /* note : pointer can be changed by app ! */
655 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
656 savCchTextMax, NULL, NULL);
657 else
658 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
659 savPszText, savCchTextMax);
660 pdi->item.pszText = savPszText; /* restores our buffer */
661 pdi->item.cchTextMax = savCchTextMax;
662 HeapFree(GetProcessHeap(), 0, pszTempBuf);
664 return bResult;
667 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc, LPRECT rcBounds)
669 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
670 lpnmlvcd->nmcd.hdc = hdc;
671 lpnmlvcd->nmcd.rc = *rcBounds;
672 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
673 lpnmlvcd->clrText = infoPtr->clrText;
676 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
678 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
679 return notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
682 /******** Item iterator functions **********************************/
684 static BOOL ranges_add(HDPA ranges, RANGE range);
685 static BOOL ranges_del(HDPA ranges, RANGE range);
686 static void ranges_dump(HDPA ranges);
688 /***
689 * ITERATOR DOCUMENTATION
691 * The iterator functions allow for easy, and convenient iteration
692 * over items of iterest in the list. Typically, you create a
693 * iterator, use it, and destroy it, as such:
694 * ITERATOR i;
696 * iterator_xxxitems(&i, ...);
697 * while (iterator_{prev,next}(&i)
699 * //code which uses i.nItem
701 * iterator_destroy(&i);
703 * where xxx is either: framed, or visible.
704 * Note that it is important that the code destroys the iterator
705 * after it's done with it, as the creation of the iterator may
706 * allocate memory, which thus needs to be freed.
708 * You can iterate both forwards, and backwards through the list,
709 * by using iterator_next or iterator_prev respectively.
711 * Lower numbered items are draw on top of higher number items in
712 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
713 * items may overlap). So, to test items, you should use
714 * iterator_next
715 * which lists the items top to bottom (in Z-order).
716 * For drawing items, you should use
717 * iterator_prev
718 * which lists the items bottom to top (in Z-order).
719 * If you keep iterating over the items after the end-of-items
720 * marker (-1) is returned, the iterator will start from the
721 * beginning. Typically, you don't need to test for -1,
722 * because iterator_{next,prev} will return TRUE if more items
723 * are to be iterated over, or FALSE otherwise.
725 * Note: the iterator is defined to be bidirectional. That is,
726 * any number of prev followed by any number of next, or
727 * five versa, should leave the iterator at the same item:
728 * prev * n, next * n = next * n, prev * n
730 * The iterator has a notion of a out-of-order, special item,
731 * which sits at the start of the list. This is used in
732 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
733 * which needs to be first, as it may overlap other items.
735 * The code is a bit messy because we have:
736 * - a special item to deal with
737 * - simple range, or composite range
738 * - empty range.
739 * If find bugs, or want to add features, please make sure you
740 * always check/modify *both* iterator_prev, and iterator_next.
743 /****
744 * This function iterates through the items in increasing order,
745 * but prefixed by the special item, then -1. That is:
746 * special, 1, 2, 3, ..., n, -1.
747 * Each item is listed only once.
749 static inline BOOL iterator_next(ITERATOR* i)
751 if (i->nItem == -1)
753 i->nItem = i->nSpecial;
754 if (i->nItem != -1) return TRUE;
756 if (i->nItem == i->nSpecial)
758 if (i->ranges) i->index = 0;
759 goto pickarange;
762 i->nItem++;
763 testitem:
764 if (i->nItem == i->nSpecial) i->nItem++;
765 if (i->nItem <= i->range.upper) return TRUE;
767 pickarange:
768 if (i->ranges)
770 if (i->index < i->ranges->nItemCount)
771 i->range = *(RANGE*)DPA_GetPtr(i->ranges, i->index++);
772 else goto end;
774 else if (i->nItem > i->range.upper) goto end;
776 i->nItem = i->range.lower;
777 if (i->nItem >= 0) goto testitem;
778 end:
779 i->nItem = -1;
780 return FALSE;
783 /****
784 * This function iterates through the items in decreasing order,
785 * followed by the special item, then -1. That is:
786 * n, n-1, ..., 3, 2, 1, special, -1.
787 * Each item is listed only once.
789 static inline BOOL iterator_prev(ITERATOR* i)
791 BOOL start = FALSE;
793 if (i->nItem == -1)
795 start = TRUE;
796 if (i->ranges) i->index = i->ranges->nItemCount;
797 goto pickarange;
799 if (i->nItem == i->nSpecial)
801 i->nItem = -1;
802 return FALSE;
805 i->nItem--;
806 testitem:
807 if (i->nItem == i->nSpecial) i->nItem--;
808 if (i->nItem >= i->range.lower) return TRUE;
810 pickarange:
811 if (i->ranges)
813 if (i->index > 0)
814 i->range = *(RANGE*)DPA_GetPtr(i->ranges, --i->index);
815 else goto end;
817 else if (!start && i->nItem < i->range.lower) goto end;
819 i->nItem = i->range.upper;
820 if (i->nItem >= 0) goto testitem;
821 end:
822 return (i->nItem = i->nSpecial) != -1;
825 static RANGE iterator_range(ITERATOR* i)
827 RANGE range;
829 if (!i->ranges) return i->range;
831 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges, 0)).lower;
832 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges, i->ranges->nItemCount - 1)).upper;
833 return range;
836 /***
837 * Releases resources associated with this ierator.
839 static inline void iterator_destroy(ITERATOR* i)
841 if (i->ranges) DPA_Destroy(i->ranges);
844 /***
845 * Create an empty iterator.
847 static inline BOOL iterator_empty(ITERATOR* i)
849 ZeroMemory(i, sizeof(*i));
850 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
851 return TRUE;
854 /***
855 * Creates an iterator over the items which intersect lprc.
857 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
859 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
860 RECT frame = *lprc, rcItem, rcTemp;
861 POINT Origin;
863 /* in case we fail, we want to return an empty iterator */
864 if (!iterator_empty(i)) return FALSE;
866 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
868 OffsetRect(&frame, -Origin.x, -Origin.y);
870 if (uView == LVS_ICON || uView == LVS_SMALLICON)
872 INT nItem;
874 if (uView == LVS_ICON)
876 if (LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem) && IntersectRect(&rcTemp, &rcItem, lprc))
877 i->nSpecial = infoPtr->nFocusedItem;
879 if (!(i->ranges = DPA_Create(50))) return FALSE;
880 /* to do better here, we need to have PosX, and PosY sorted */
881 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
883 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
884 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
885 rcItem.right = rcItem.left + infoPtr->nItemWidth;
886 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
887 if (IntersectRect(&rcTemp, &rcItem, &frame))
889 RANGE item_range = { nItem, nItem };
890 ranges_add(i->ranges, item_range);
891 TRACE(" icon=%d\n", nItem);
894 return TRUE;
896 else if (uView == LVS_REPORT)
898 INT lower, upper;
900 if (frame.left >= infoPtr->nItemWidth) return TRUE;
901 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
903 lower = max(frame.top / infoPtr->nItemHeight, 0);
904 upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1);
905 if (upper < lower) return TRUE;
906 i->range.lower = lower;
907 i->range.upper = upper;
908 TRACE(" report=[%d,%d]\n", lower, upper);
910 else
912 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
913 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
914 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
915 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
916 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
917 INT lower = nFirstCol * nPerCol + nFirstRow;
918 RANGE item_range;
919 INT nCol;
921 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
922 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
924 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
926 if (!(i->ranges = DPA_Create(nLastCol - nFirstCol + 1))) return FALSE;
927 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
929 item_range.lower = nCol * nPerCol + nFirstRow;
930 if(item_range.lower >= infoPtr->nItemCount) break;
931 item_range.upper = min(nCol * nPerCol + nLastRow, infoPtr->nItemCount - 1);
932 TRACE(" list=[%d,%d]\n", item_range.lower, item_range.upper);
933 ranges_add(i->ranges, item_range);
937 return TRUE;
940 /***
941 * Creates an iterator over the items which intersect the visible region of hdc.
943 static BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
945 POINT Origin, Position;
946 RECT rcItem, rcClip;
947 INT rgntype;
949 rgntype = GetClipBox(hdc, &rcClip);
950 if (rgntype == NULLREGION) return iterator_empty(i);
951 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
952 if (rgntype == SIMPLEREGION) return TRUE;
954 /* first deal with the special item */
955 if (LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem) && !RectVisible(hdc, &rcItem))
956 i->nSpecial = -1;
958 /* if we can't deal with the region, we'll just go with the simple range */
959 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return TRUE;
960 if (!i->ranges)
962 if (!(i->ranges = DPA_Create(50))) return TRUE;
963 if (!ranges_add(i->ranges, i->range))
965 DPA_Destroy(i->ranges);
966 i->ranges = 0;
967 return TRUE;
971 /* now delete the invisible items from the list */
972 while(iterator_next(i))
974 if (!LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position)) continue;
975 rcItem.left = Position.x + Origin.x;
976 rcItem.top = Position.y + Origin.y;
977 rcItem.right = rcItem.left + infoPtr->nItemWidth;
978 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
979 if (!RectVisible(hdc, &rcItem))
981 RANGE item_range = { i->nItem, i->nItem };
982 ranges_del(i->ranges, item_range);
985 /* the iterator should restart on the next iterator_next */
987 return TRUE;
990 /******** Misc helper functions ************************************/
992 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
993 WPARAM wParam, LPARAM lParam, BOOL isW)
995 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
996 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
999 /******** Internal API functions ************************************/
1001 /* The Invalidate* are macros, so we preserve the caller location */
1002 #define LISTVIEW_InvalidateRect(infoPtr, rect) while(infoPtr->bRedraw) { \
1003 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
1004 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
1005 break;\
1008 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
1009 RECT rcBox; \
1010 if(LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox)) \
1011 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
1012 } while (0)
1014 #define LISTVIEW_InvalidateSubItem(infoPtr, nItem, nSubItem) do { \
1015 POINT Origin, Position; \
1016 RECT rcBox; \
1017 if (LISTVIEW_GetOrigin(infoPtr, &Origin) && \
1018 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position) && \
1019 Header_GetItemRect(infoPtr->hwndHeader, nSubItem, &rcBox)) { \
1020 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y); \
1021 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
1023 } while (0)
1025 #define LISTVIEW_InvalidateList(infoPtr)\
1026 LISTVIEW_InvalidateRect(infoPtr, NULL)
1028 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1030 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1033 /***
1034 * DESCRIPTION:
1035 * Retrieves the number of items that can fit vertically in the client area.
1037 * PARAMETER(S):
1038 * [I] infoPtr : valid pointer to the listview structure
1040 * RETURN:
1041 * Number of items per row.
1043 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1045 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1047 return max(nListWidth/infoPtr->nItemWidth, 1);
1050 /***
1051 * DESCRIPTION:
1052 * Retrieves the number of items that can fit horizontally in the client
1053 * area.
1055 * PARAMETER(S):
1056 * [I] infoPtr : valid pointer to the listview structure
1058 * RETURN:
1059 * Number of items per column.
1061 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1063 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1065 return max(nListHeight / infoPtr->nItemHeight, 1);
1069 /*************************************************************************
1070 * LISTVIEW_ProcessLetterKeys
1072 * Processes keyboard messages generated by pressing the letter keys
1073 * on the keyboard.
1074 * What this does is perform a case insensitive search from the
1075 * current position with the following quirks:
1076 * - If two chars or more are pressed in quick succession we search
1077 * for the corresponding string (e.g. 'abc').
1078 * - If there is a delay we wipe away the current search string and
1079 * restart with just that char.
1080 * - If the user keeps pressing the same character, whether slowly or
1081 * fast, so that the search string is entirely composed of this
1082 * character ('aaaaa' for instance), then we search for first item
1083 * that starting with that character.
1084 * - If the user types the above character in quick succession, then
1085 * we must also search for the corresponding string ('aaaaa'), and
1086 * go to that string if there is a match.
1088 * PARAMETERS
1089 * [I] hwnd : handle to the window
1090 * [I] charCode : the character code, the actual character
1091 * [I] keyData : key data
1093 * RETURNS
1095 * Zero.
1097 * BUGS
1099 * - The current implementation has a list of characters it will
1100 * accept and it ignores averything else. In particular it will
1101 * ignore accentuated characters which seems to match what
1102 * Windows does. But I'm not sure it makes sense to follow
1103 * Windows there.
1104 * - We don't sound a beep when the search fails.
1106 * SEE ALSO
1108 * TREEVIEW_ProcessLetterKeys
1110 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1112 INT nItem;
1113 INT endidx,idx;
1114 LVITEMW item;
1115 WCHAR buffer[MAX_PATH];
1116 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1118 /* simple parameter checking */
1119 if (!charCode || !keyData) return 0;
1121 /* only allow the valid WM_CHARs through */
1122 if (!isalnum(charCode) &&
1123 charCode != '.' && charCode != '`' && charCode != '!' &&
1124 charCode != '@' && charCode != '#' && charCode != '$' &&
1125 charCode != '%' && charCode != '^' && charCode != '&' &&
1126 charCode != '*' && charCode != '(' && charCode != ')' &&
1127 charCode != '-' && charCode != '_' && charCode != '+' &&
1128 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1129 charCode != '}' && charCode != '[' && charCode != '{' &&
1130 charCode != '/' && charCode != '?' && charCode != '>' &&
1131 charCode != '<' && charCode != ',' && charCode != '~')
1132 return 0;
1134 /* if there's one item or less, there is no where to go */
1135 if (infoPtr->nItemCount <= 1) return 0;
1137 /* update the search parameters */
1138 infoPtr->lastKeyPressTimestamp = GetTickCount();
1139 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1140 if (infoPtr->nSearchParamLength < MAX_PATH)
1141 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1142 if (infoPtr->charCode != charCode)
1143 infoPtr->charCode = charCode = 0;
1144 } else {
1145 infoPtr->charCode=charCode;
1146 infoPtr->szSearchParam[0]=charCode;
1147 infoPtr->nSearchParamLength=1;
1148 /* Redundant with the 1 char string */
1149 charCode=0;
1152 /* and search from the current position */
1153 nItem=-1;
1154 if (infoPtr->nFocusedItem >= 0) {
1155 endidx=infoPtr->nFocusedItem;
1156 idx=endidx;
1157 /* if looking for single character match,
1158 * then we must always move forward
1160 if (infoPtr->nSearchParamLength == 1)
1161 idx++;
1162 } else {
1163 endidx=infoPtr->nItemCount;
1164 idx=0;
1166 do {
1167 if (idx == infoPtr->nItemCount) {
1168 if (endidx == infoPtr->nItemCount || endidx == 0)
1169 break;
1170 idx=0;
1173 /* get item */
1174 item.mask = LVIF_TEXT;
1175 item.iItem = idx;
1176 item.iSubItem = 0;
1177 item.pszText = buffer;
1178 item.cchTextMax = MAX_PATH;
1179 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1181 /* check for a match */
1182 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1183 nItem=idx;
1184 break;
1185 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1186 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1187 /* This would work but we must keep looking for a longer match */
1188 nItem=idx;
1190 idx++;
1191 } while (idx != endidx);
1193 if (nItem != -1)
1194 LISTVIEW_KeySelection(infoPtr, nItem);
1196 return 0;
1199 /*************************************************************************
1200 * LISTVIEW_UpdateHeaderSize [Internal]
1202 * Function to resize the header control
1204 * PARAMS
1205 * hwnd [I] handle to a window
1206 * nNewScrollPos [I] Scroll Pos to Set
1208 * RETURNS
1209 * nothing
1211 * NOTES
1213 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1215 RECT winRect;
1216 POINT point[2];
1218 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1220 GetWindowRect(infoPtr->hwndHeader, &winRect);
1221 point[0].x = winRect.left;
1222 point[0].y = winRect.top;
1223 point[1].x = winRect.right;
1224 point[1].y = winRect.bottom;
1226 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1227 point[0].x = -nNewScrollPos;
1228 point[1].x += nNewScrollPos;
1230 SetWindowPos(infoPtr->hwndHeader,0,
1231 point[0].x,point[0].y,point[1].x,point[1].y,
1232 SWP_NOZORDER | SWP_NOACTIVATE);
1235 /***
1236 * DESCRIPTION:
1237 * Update the scrollbars. This functions should be called whenever
1238 * the content, size or view changes.
1240 * PARAMETER(S):
1241 * [I] infoPtr : valid pointer to the listview structure
1243 * RETURN:
1244 * None
1246 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1248 LONG lStyle = infoPtr->dwStyle;
1249 UINT uView = lStyle & LVS_TYPEMASK;
1250 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1251 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1252 SCROLLINFO scrollInfo;
1254 if (lStyle & LVS_NOSCROLL) return;
1256 scrollInfo.cbSize = sizeof(SCROLLINFO);
1258 if (uView == LVS_LIST)
1260 /* update horizontal scrollbar */
1261 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1262 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1264 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1265 infoPtr->nItemCount, nCountPerColumn, nCountPerRow);
1267 scrollInfo.nMin = 0;
1268 scrollInfo.nMax = infoPtr->nItemCount / nCountPerColumn;
1269 if((infoPtr->nItemCount % nCountPerColumn) == 0)
1270 scrollInfo.nMax--;
1271 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1272 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1273 scrollInfo.nPage = nCountPerRow;
1274 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1275 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1276 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1278 else if (uView == LVS_REPORT)
1280 BOOL test;
1282 /* update vertical scrollbar */
1283 scrollInfo.nMin = 0;
1284 scrollInfo.nMax = infoPtr->nItemCount - 1;
1285 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1286 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1287 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1288 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1289 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1290 scrollInfo.nMax, scrollInfo.nPage, test);
1291 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1292 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1294 /* update horizontal scrollbar */
1295 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1296 scrollInfo.fMask = SIF_POS;
1297 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1298 || infoPtr->nItemCount == 0)
1300 scrollInfo.nPos = 0;
1302 scrollInfo.nMin = 0;
1303 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1304 scrollInfo.nPage = nListWidth;
1305 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1306 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1307 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1308 scrollInfo.nMax, scrollInfo.nPage, test);
1309 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1310 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1312 /* Update the Header Control */
1313 scrollInfo.fMask = SIF_POS;
1314 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1315 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1318 else
1320 RECT rcView;
1322 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1324 INT nViewWidth = rcView.right - rcView.left;
1325 INT nViewHeight = rcView.bottom - rcView.top;
1327 /* Update Horizontal Scrollbar */
1328 scrollInfo.fMask = SIF_POS;
1329 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1330 || infoPtr->nItemCount == 0)
1332 scrollInfo.nPos = 0;
1334 scrollInfo.nMin = 0;
1335 scrollInfo.nMax = max(nViewWidth, 0)-1;
1336 scrollInfo.nPage = nListWidth;
1337 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1338 TRACE("LVS_ICON/SMALLICON Horz.\n");
1339 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1341 /* Update Vertical Scrollbar */
1342 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1343 scrollInfo.fMask = SIF_POS;
1344 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1345 || infoPtr->nItemCount == 0)
1347 scrollInfo.nPos = 0;
1349 scrollInfo.nMin = 0;
1350 scrollInfo.nMax = max(nViewHeight,0)-1;
1351 scrollInfo.nPage = nListHeight;
1352 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1353 TRACE("LVS_ICON/SMALLICON Vert.\n");
1354 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1360 /***
1361 * DESCRIPTION:
1362 * Shows/hides the focus rectangle.
1364 * PARAMETER(S):
1365 * [I] infoPtr : valid pointer to the listview structure
1366 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1368 * RETURN:
1369 * None
1371 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1373 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1374 HDC hdc;
1376 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1378 if (infoPtr->nFocusedItem < 0) return;
1380 /* we need some gymnastics in ICON mode to handle large items */
1381 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1383 RECT rcBox;
1385 if (!LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox))
1386 return;
1387 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1389 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1390 return;
1394 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1396 /* for some reason, owner draw should work only in report mode */
1397 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1399 DRAWITEMSTRUCT dis;
1400 LVITEMW item;
1402 item.iItem = infoPtr->nFocusedItem;
1403 item.iSubItem = 0;
1404 item.mask = LVIF_PARAM;
1405 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1407 ZeroMemory(&dis, sizeof(dis));
1408 dis.CtlType = ODT_LISTVIEW;
1409 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1410 dis.itemID = item.iItem;
1411 dis.itemAction = ODA_FOCUS;
1412 if (fShow) dis.itemState |= ODS_FOCUS;
1413 dis.hwndItem = infoPtr->hwndSelf;
1414 dis.hDC = hdc;
1415 if (!LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem)) goto done;
1416 dis.itemData = item.lParam;
1418 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1420 else
1422 DrawFocusRect(hdc, &infoPtr->rcFocus);
1424 done:
1425 ReleaseDC(infoPtr->hwndSelf, hdc);
1428 /***
1429 * Invalidates all visible selected items.
1431 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1433 ITERATOR i;
1435 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1436 while(iterator_next(&i))
1438 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1439 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1441 iterator_destroy(&i);
1445 /***
1446 * DESCRIPTION:
1447 * Prints a message for unsupported window styles.
1448 * A kind of TODO list for window styles.
1450 * PARAMETER(S):
1451 * [I] LONG : window style
1453 * RETURN:
1454 * None
1456 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1458 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1459 FIXME(" LVS_NOSCROLL\n");
1461 if (lStyle & LVS_NOLABELWRAP)
1462 FIXME(" LVS_NOLABELWRAP\n");
1464 if (lStyle & LVS_SORTASCENDING)
1465 FIXME(" LVS_SORTASCENDING\n");
1467 if (lStyle & LVS_SORTDESCENDING)
1468 FIXME(" LVS_SORTDESCENDING\n");
1472 /***
1473 * DESCRIPTION: [INTERNAL]
1474 * Computes an item's (left,top) corner, relative to rcView.
1475 * That is, the position has NOT been made relative to the Origin.
1476 * This is deliberate, to avoid computing the Origin over, and
1477 * over again, when this function is call in a loop. Instead,
1478 * one ca factor the computation of the Origin before the loop,
1479 * and offset the value retured by this function, on every iteration.
1481 * PARAMETER(S):
1482 * [I] infoPtr : valid pointer to the listview structure
1483 * [I] nItem : item number
1484 * [O] lpptOrig : item top, left corner
1486 * RETURN:
1487 * TRUE if computations OK
1488 * FALSE otherwise
1490 static BOOL LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1492 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1494 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
1496 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1498 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1499 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1501 else if (uView == LVS_LIST)
1503 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1504 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1505 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1507 else /* LVS_REPORT */
1509 lpptPosition->x = REPORT_MARGINX;
1510 lpptPosition->y = nItem * infoPtr->nItemHeight;
1513 return TRUE;
1516 /***
1517 * DESCRIPTION: [INTERNAL]
1518 * Compute the rectangles of an item. This is to localize all
1519 * the computations in one place. If you are not interested in some
1520 * of these values, simply pass in a NULL -- the fucntion is smart
1521 * enough to compute only what's necessary. The function computes
1522 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1523 * one, the BOX rectangle. This rectangle is very cheap to compute,
1524 * and is guaranteed to contain all the other rectangles. Computing
1525 * the ICON rect is also cheap, but all the others are potentaily
1526 * expensive. This gives an easy and effective optimization when
1527 * searching (like point inclusion, or rectangle intersection):
1528 * first test against the BOX, and if TRUE, test agains the desired
1529 * rectangle.
1530 * If the function does not have all the necessary information
1531 * to computed the requested rectangles, will crash with a
1532 * failed assertion. This is done so we catch all programming
1533 * errors, given that the function is called only from our code.
1535 * We have the following 'special' meanings for a few fields:
1536 * * If LVIS_FOCUSED is set, we assume the item has the focus
1537 * This is important in ICON mode, where it might get a larger
1538 * then usual rectange
1540 * Please note that subitem support works only in REPORT mode.
1542 * PARAMETER(S):
1543 * [I] infoPtr : valid pointer to the listview structure
1544 * [I] lpLVItem : item to compute the measures for
1545 * [O] lprcBox : ptr to Box rectangle
1546 * The internal LVIR_BOX rectangle
1547 * [0] lprcState : ptr to State icon rectangle
1548 * The internal LVIR_STATE rectangle
1549 * [O] lprcIcon : ptr to Icon rectangle
1550 * Same as LVM_GETITEMRECT with LVIR_ICON
1551 * [O] lprcLabel : ptr to Label rectangle
1552 * Same as LVM_GETITEMRECT with LVIR_LABEL
1554 * RETURN:
1555 * TRUE if computations OK
1556 * FALSE otherwise
1558 static BOOL LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem,
1559 LPRECT lprcBox, LPRECT lprcState,
1560 LPRECT lprcIcon, LPRECT lprcLabel)
1562 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1563 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1564 RECT Box, State, Icon, Label;
1566 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1568 /* Be smart and try to figure out the minimum we have to do */
1569 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1570 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1572 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1573 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1575 if (lprcLabel) doLabel = TRUE;
1576 if (doLabel || lprcIcon) doIcon = TRUE;
1577 if (doIcon || lprcState) doState = TRUE;
1579 /************************************************************/
1580 /* compute the box rectangle (it should be cheap to do) */
1581 /************************************************************/
1582 if (lpLVItem->iSubItem)
1584 if (!Header_GetItemRect(infoPtr->hwndHeader, lpLVItem->iSubItem, &Box)) return FALSE;
1586 else
1588 Box.left = 0;
1589 Box.right = infoPtr->nItemWidth;
1591 Box.top = 0;
1592 Box.bottom = infoPtr->nItemHeight;
1594 /************************************************************/
1595 /* compute STATEICON bounding box */
1596 /************************************************************/
1597 if (doState)
1599 if (uView == LVS_ICON)
1601 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1602 if (infoPtr->himlNormal)
1603 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1604 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1606 else
1608 /* we need the ident in report mode, if we don't have it, we fail */
1609 State.left = Box.left;
1610 if (uView == LVS_REPORT)
1612 State.left += REPORT_MARGINX;
1613 if (lpLVItem->iSubItem == 0)
1615 assert(lpLVItem->mask & LVIF_INDENT);
1616 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1619 State.top = Box.top;
1621 State.right = State.left;
1622 State.bottom = State.top;
1623 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1625 State.right += infoPtr->iconStateSize.cx;
1626 State.bottom += infoPtr->iconStateSize.cy;
1628 if (lprcState) *lprcState = State;
1629 TRACE(" - state=%s\n", debugrect(&State));
1632 /************************************************************/
1633 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1634 /************************************************************/
1635 if (doIcon)
1637 if (uView == LVS_ICON)
1639 Icon.left = Box.left;
1640 if (infoPtr->himlNormal)
1641 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1642 Icon.top = Box.top + ICON_TOP_PADDING;
1643 Icon.right = Icon.left;
1644 Icon.bottom = Icon.top;
1645 if (infoPtr->himlNormal)
1647 Icon.right += infoPtr->iconSize.cx;
1648 Icon.bottom += infoPtr->iconSize.cy;
1651 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1653 Icon.left = State.right;
1654 if (!IsRectEmpty(&State)) Icon.left += IMAGE_PADDING;
1655 Icon.top = Box.top;
1656 Icon.right = Icon.left;
1657 /* FIXME: add suport for icons for subitems */
1658 if (infoPtr->himlSmall && lpLVItem->iSubItem == 0) Icon.right += infoPtr->iconSize.cx;
1659 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1661 if(lprcIcon) *lprcIcon = Icon;
1662 TRACE(" - icon=%s\n", debugrect(&Icon));
1665 /************************************************************/
1666 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1667 /************************************************************/
1668 if (doLabel)
1670 SIZE labelSize = { 0, 0 };
1672 /* calculate how far to the right can the label strech */
1673 Label.right = Box.right;
1674 if (uView == LVS_REPORT)
1676 if (lpLVItem->iSubItem == 0 && !Header_GetItemRect(infoPtr->hwndHeader, 0, &Label)) return FALSE;
1677 Label.right -= REPORT_MARGINX;
1680 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1682 labelSize.cx = infoPtr->nItemWidth;
1683 labelSize.cy = infoPtr->nItemHeight;
1684 goto calc_label;
1687 /* we need the text in non owner draw mode */
1688 assert(lpLVItem->mask & LVIF_TEXT);
1689 if (is_textT(lpLVItem->pszText, TRUE))
1691 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1692 HDC hdc = GetDC(infoPtr->hwndSelf);
1693 HFONT hOldFont = SelectObject(hdc, hFont);
1694 UINT uFormat;
1695 RECT rcText;
1697 /* compute rough rectangle where the label will go */
1698 SetRectEmpty(&rcText);
1699 rcText.right = infoPtr->nItemWidth - TRAILING_PADDING;
1700 rcText.bottom = infoPtr->nItemHeight;
1701 if (uView == LVS_ICON)
1702 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1704 /* now figure out the flags */
1705 if (uView == LVS_ICON)
1706 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1707 else
1708 uFormat = LV_SL_DT_FLAGS;
1710 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1712 labelSize.cx = min(rcText.right - rcText.left + TRAILING_PADDING, infoPtr->nItemWidth);
1713 labelSize.cy = rcText.bottom - rcText.top;
1715 SelectObject(hdc, hOldFont);
1716 ReleaseDC(infoPtr->hwndSelf, hdc);
1719 calc_label:
1720 if (uView == LVS_ICON)
1722 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1723 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1724 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1725 Label.right = Label.left + labelSize.cx;
1726 Label.bottom = Label.top + infoPtr->nItemHeight;
1727 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1729 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1730 labelSize.cy /= infoPtr->ntmHeight;
1731 labelSize.cy = max(labelSize.cy, 1);
1732 labelSize.cy *= infoPtr->ntmHeight;
1734 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1736 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1738 Label.left = Icon.right;
1739 if (!IsRectEmpty(&Icon) || !IsRectEmpty(&State)) Label.left += IMAGE_PADDING;
1740 Label.top = Box.top;
1741 Label.right = min(Label.left + labelSize.cx, Label.right);
1742 Label.bottom = Label.top + infoPtr->nItemHeight;
1745 if (lprcLabel) *lprcLabel = Label;
1746 TRACE(" - label=%s\n", debugrect(&Label));
1749 /* Fix the Box if necessary */
1750 if (lprcBox)
1752 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1753 else *lprcBox = Box;
1755 TRACE(" - box=%s\n", debugrect(&Box));
1757 return TRUE;
1760 /***
1761 * DESCRIPTION: [INTERNAL]
1763 * PARAMETER(S):
1764 * [I] infoPtr : valid pointer to the listview structure
1765 * [I] nItem : item number
1766 * [O] lprcBox : ptr to Box rectangle
1768 * RETURN:
1769 * TRUE if computations OK
1770 * FALSE otherwise
1772 static BOOL LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
1774 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1775 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1776 POINT Position, Origin;
1777 LVITEMW lvItem;
1779 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) return FALSE;
1780 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
1782 /* Be smart and try to figure out the minimum we have to do */
1783 lvItem.mask = 0;
1784 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1785 lvItem.mask |= LVIF_TEXT;
1786 lvItem.iItem = nItem;
1787 lvItem.iSubItem = 0;
1788 lvItem.pszText = szDispText;
1789 lvItem.cchTextMax = DISP_TEXT_SIZE;
1790 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
1791 if (uView == LVS_ICON)
1793 lvItem.mask |= LVIF_STATE;
1794 lvItem.stateMask = LVIS_FOCUSED;
1795 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
1797 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0)) return FALSE;
1799 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
1801 return TRUE;
1804 /***
1805 * DESCRIPTION:
1806 * Aligns the items with the top edge of the window.
1808 * PARAMETER(S):
1809 * [I] infoPtr : valid pointer to the listview structure
1811 * RETURN:
1812 * None
1814 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1816 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1817 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1818 POINT ptItem;
1819 RECT rcView;
1820 INT i, off_x=0, off_y=0;
1822 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1824 /* Since SetItemPosition uses upper-left of icon, and for
1825 style=LVS_ICON the icon is not left adjusted, get the offset */
1826 if (uView == LVS_ICON)
1828 off_y = ICON_TOP_PADDING;
1829 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1831 ptItem.x = off_x;
1832 ptItem.y = off_y;
1833 ZeroMemory(&rcView, sizeof(RECT));
1834 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1835 off_x, off_y,
1836 infoPtr->rcList.left, infoPtr->rcList.right);
1838 if (nListWidth > infoPtr->nItemWidth)
1840 for (i = 0; i < infoPtr->nItemCount; i++)
1842 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1844 ptItem.x = off_x;
1845 ptItem.y += infoPtr->nItemHeight;
1848 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1849 ptItem.x += infoPtr->nItemWidth;
1850 rcView.right = max(rcView.right, ptItem.x);
1853 rcView.right -= off_x;
1854 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1856 else
1858 for (i = 0; i < infoPtr->nItemCount; i++)
1860 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1861 ptItem.y += infoPtr->nItemHeight;
1864 rcView.right = infoPtr->nItemWidth;
1865 rcView.bottom = ptItem.y-off_y;
1868 infoPtr->rcView = rcView;
1872 /***
1873 * DESCRIPTION:
1874 * Aligns the items with the left edge of the window.
1876 * PARAMETER(S):
1877 * [I] infoPtr : valid pointer to the listview structure
1879 * RETURN:
1880 * None
1882 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1884 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1885 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1886 POINT ptItem;
1887 RECT rcView;
1888 INT i, off_x=0, off_y=0;
1890 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1892 /* Since SetItemPosition uses upper-left of icon, and for
1893 style=LVS_ICON the icon is not left adjusted, get the offset */
1894 if (uView == LVS_ICON)
1896 off_y = ICON_TOP_PADDING;
1897 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1899 ptItem.x = off_x;
1900 ptItem.y = off_y;
1901 ZeroMemory(&rcView, sizeof(RECT));
1902 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1904 if (nListHeight > infoPtr->nItemHeight)
1906 for (i = 0; i < infoPtr->nItemCount; i++)
1908 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1910 ptItem.y = off_y;
1911 ptItem.x += infoPtr->nItemWidth;
1914 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1915 ptItem.y += infoPtr->nItemHeight;
1916 rcView.bottom = max(rcView.bottom, ptItem.y);
1919 rcView.right = ptItem.x + infoPtr->nItemWidth;
1921 else
1923 for (i = 0; i < infoPtr->nItemCount; i++)
1925 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1926 ptItem.x += infoPtr->nItemWidth;
1929 rcView.bottom = infoPtr->nItemHeight;
1930 rcView.right = ptItem.x;
1933 infoPtr->rcView = rcView;
1938 /***
1939 * DESCRIPTION:
1940 * Retrieves the bounding rectangle of all the items.
1942 * PARAMETER(S):
1943 * [I] infoPtr : valid pointer to the listview structure
1944 * [O] lprcView : bounding rectangle
1946 * RETURN:
1947 * SUCCESS : TRUE
1948 * FAILURE : FALSE
1950 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1952 POINT ptOrigin;
1954 TRACE("(lprcView=%p)\n", lprcView);
1956 if (!lprcView) return FALSE;
1958 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return FALSE;
1960 *lprcView = infoPtr->rcView;
1961 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
1963 TRACE("lprcView=%s\n", debugrect(lprcView));
1965 return TRUE;
1968 /***
1969 * DESCRIPTION:
1970 * Retrieves the subitem pointer associated with the subitem index.
1972 * PARAMETER(S):
1973 * [I] HDPA : DPA handle for a specific item
1974 * [I] INT : index of subitem
1976 * RETURN:
1977 * SUCCESS : subitem pointer
1978 * FAILURE : NULL
1980 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
1982 LISTVIEW_SUBITEM *lpSubItem;
1983 INT i;
1985 /* we should binary search here if need be */
1986 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1988 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1989 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
1990 return lpSubItem;
1993 return NULL;
1997 /***
1998 * DESCRIPTION:
1999 * Calculates the width of a specific item.
2001 * PARAMETER(S):
2002 * [I] infoPtr : valid pointer to the listview structure
2003 * [I] nItem : item to calculate width, or -1 for max of all
2005 * RETURN:
2006 * Returns the width of an item width an item.
2008 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr, INT nItem)
2010 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2011 INT nItemWidth = 0, i;
2013 if (uView == LVS_ICON)
2014 nItemWidth = infoPtr->iconSpacing.cx;
2015 else if (uView == LVS_REPORT)
2017 INT nHeaderItemCount;
2018 RECT rcHeaderItem;
2020 /* calculate width of header */
2021 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
2022 for (i = 0; i < nHeaderItemCount; i++)
2023 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem))
2024 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
2026 else
2028 INT nLabelWidth;
2030 if (infoPtr->nItemCount == 0) return DEFAULT_COLUMN_WIDTH;
2032 /* get width of string */
2033 if (nItem == -1)
2035 for (i = 0; i < infoPtr->nItemCount; i++)
2037 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
2038 nItemWidth = max(nItemWidth, nLabelWidth);
2041 else
2042 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
2043 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
2044 nItemWidth += WIDTH_PADDING;
2045 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2046 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2047 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
2050 return max(nItemWidth, 1);
2053 /***
2054 * DESCRIPTION:
2055 * Calculates the max width of any item in the list.
2057 * PARAMETER(S):
2058 * [I] infoPtr : valid pointer to the listview structure
2059 * [I] LONG : window style
2061 * RETURN:
2062 * Returns item width.
2064 static inline INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *infoPtr)
2066 return LISTVIEW_CalculateItemWidth(infoPtr, -1);
2069 /***
2070 * DESCRIPTION:
2071 * Retrieves and saves important text metrics info for the current
2072 * Listview font.
2074 * PARAMETER(S):
2075 * [I] infoPtr : valid pointer to the listview structure
2078 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2080 HDC hdc = GetDC(infoPtr->hwndSelf);
2081 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2082 HFONT hOldFont = SelectObject(hdc, hFont);
2083 TEXTMETRICW tm;
2085 if (GetTextMetricsW(hdc, &tm))
2086 infoPtr->ntmHeight = tm.tmHeight;
2087 SelectObject(hdc, hOldFont);
2088 ReleaseDC(infoPtr->hwndSelf, hdc);
2090 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2094 /***
2095 * DESCRIPTION:
2096 * Calculates the height of an item.
2098 * PARAMETER(S):
2099 * [I] infoPtr : valid pointer to the listview structure
2101 * RETURN:
2102 * Returns item height.
2104 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *infoPtr)
2106 INT nItemHeight;
2108 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
2109 nItemHeight = infoPtr->iconSpacing.cy;
2110 else
2112 nItemHeight = infoPtr->ntmHeight;
2113 if (infoPtr->himlState)
2114 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2115 if (infoPtr->himlSmall)
2116 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2117 if (infoPtr->himlState || infoPtr->himlSmall)
2118 nItemHeight += HEIGHT_PADDING;
2120 return nItemHeight;
2123 #if 0
2124 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
2126 ERR("Selections are:\n");
2127 ranges_dump(infoPtr->hdpaSelectionRanges);
2129 #endif
2131 /***
2132 * DESCRIPTION:
2133 * A compare function for ranges
2135 * PARAMETER(S)
2136 * [I] range1 : pointer to range 1;
2137 * [I] range2 : pointer to range 2;
2138 * [I] flags : flags
2140 * RETURNS:
2141 * >0 : if Item 1 > Item 2
2142 * <0 : if Item 2 > Item 1
2143 * 0 : if Item 1 == Item 2
2145 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2147 if (((RANGE*)range1)->upper < ((RANGE*)range2)->lower)
2148 return -1;
2149 if (((RANGE*)range2)->upper < ((RANGE*)range1)->lower)
2150 return 1;
2151 return 0;
2154 static void ranges_dump(HDPA ranges)
2156 INT i;
2158 for (i = 0; i < ranges->nItemCount; i++)
2160 RANGE *selection = DPA_GetPtr(ranges, i);
2161 TRACE(" [%d - %d]\n", selection->lower, selection->upper);
2165 static inline BOOL ranges_contain(HDPA ranges, INT nItem)
2167 RANGE srchrng = { nItem, nItem };
2169 TRACE("(nItem=%d)\n", nItem);
2170 if (TRACE_ON(listview)) ranges_dump(ranges);
2171 return DPA_Search(ranges, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2174 static BOOL ranges_shift(HDPA ranges, INT nItem, INT delta, INT nUpper)
2176 RANGE srchrng, *chkrng;
2177 INT index;
2179 srchrng.upper = nItem;
2180 srchrng.lower = nItem;
2182 index = DPA_Search(ranges, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2183 if (index == -1) return TRUE;
2185 for (;index < ranges->nItemCount; index++)
2187 chkrng = DPA_GetPtr(ranges, index);
2188 if (chkrng->lower >= nItem)
2189 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2190 if (chkrng->upper >= nItem)
2191 chkrng->upper = max(min(chkrng->upper + delta, nUpper - 1), 0);
2193 return TRUE;
2196 static BOOL ranges_add(HDPA ranges, RANGE range)
2198 RANGE srchrgn;
2199 INT index;
2201 TRACE("range=(%i - %i)\n", range.lower, range.upper);
2202 if (TRACE_ON(listview)) ranges_dump(ranges);
2204 /* try find overlapping regions first */
2205 srchrgn.lower = range.lower - 1;
2206 srchrgn.upper = range.upper + 1;
2207 index = DPA_Search(ranges, &srchrgn, 0, ranges_cmp, 0, 0);
2209 if (index == -1)
2211 RANGE *newrgn;
2213 TRACE("Adding new range\n");
2215 /* create the brand new range to insert */
2216 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2217 if(!newrgn) return FALSE;
2218 *newrgn = range;
2220 /* figure out where to insert it */
2221 index = DPA_Search(ranges, newrgn, 0, ranges_cmp, 0, DPAS_INSERTAFTER);
2222 if (index == -1) index = 0;
2224 /* and get it over with */
2225 DPA_InsertPtr(ranges, index, newrgn);
2227 else
2229 RANGE *chkrgn, *mrgrgn;
2230 INT fromindex, mergeindex;
2232 chkrgn = DPA_GetPtr(ranges, index);
2233 if (!chkrgn) return FALSE;
2234 TRACE("Merge with index %i (%d - %d)\n",
2235 index, chkrgn->lower, chkrgn->upper);
2237 chkrgn->lower = min(range.lower, chkrgn->lower);
2238 chkrgn->upper = max(range.upper, chkrgn->upper);
2240 TRACE("New range %i (%d - %d)\n",
2241 index, chkrgn->lower, chkrgn->upper);
2243 /* merge now common anges */
2244 fromindex = 0;
2245 srchrgn.lower = chkrgn->lower - 1;
2246 srchrgn.upper = chkrgn->upper + 1;
2250 mergeindex = DPA_Search(ranges, &srchrgn, fromindex, ranges_cmp, 0, 0);
2251 if (mergeindex == -1) break;
2252 if (mergeindex == index)
2254 fromindex = index + 1;
2255 continue;
2258 TRACE("Merge with index %i\n", mergeindex);
2260 mrgrgn = DPA_GetPtr(ranges, mergeindex);
2261 if (!mrgrgn) return FALSE;
2263 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2264 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2265 COMCTL32_Free(mrgrgn);
2266 DPA_DeletePtr(ranges, mergeindex);
2267 if (mergeindex < index) index --;
2268 } while(1);
2271 if (TRACE_ON(listview)) ranges_dump(ranges);
2272 return TRUE;
2275 static BOOL ranges_del(HDPA ranges, RANGE range)
2277 RANGE remrgn, tmprgn, *chkrgn;
2278 BOOL done = FALSE;
2279 INT index;
2281 TRACE("range: (%d - %d)\n", range.lower, range.upper);
2283 remrgn = range;
2286 index = DPA_Search(ranges, &remrgn, 0, ranges_cmp, 0, 0);
2287 if (index == -1) return TRUE;
2289 chkrgn = DPA_GetPtr(ranges, index);
2290 if (!chkrgn) return FALSE;
2292 TRACE("Matches range index %i (%d - %d)\n",
2293 index, chkrgn->lower, chkrgn->upper);
2295 /* case 1: Same range */
2296 if ( (chkrgn->upper == remrgn.upper) &&
2297 (chkrgn->lower == remrgn.lower) )
2299 DPA_DeletePtr(ranges, index);
2300 done = TRUE;
2302 /* case 2: engulf */
2303 else if ( (chkrgn->upper <= remrgn.upper) &&
2304 (chkrgn->lower >= remrgn.lower) )
2306 DPA_DeletePtr(ranges, index);
2308 /* case 3: overlap upper */
2309 else if ( (chkrgn->upper <= remrgn.upper) &&
2310 (chkrgn->lower < remrgn.lower) )
2312 chkrgn->upper = remrgn.lower - 1;
2314 /* case 4: overlap lower */
2315 else if ( (chkrgn->upper > remrgn.upper) &&
2316 (chkrgn->lower >= remrgn.lower) )
2318 chkrgn->lower = remrgn.upper + 1;
2320 /* case 5: fully internal */
2321 else
2323 RANGE *newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2324 if (!newrgn) return FALSE;
2325 tmprgn = *chkrgn;
2326 newrgn->lower = chkrgn->lower;
2327 newrgn->upper = remrgn.lower - 1;
2328 chkrgn->lower = remrgn.upper + 1;
2329 DPA_InsertPtr(ranges, index, newrgn);
2330 chkrgn = &tmprgn;
2333 while(!done);
2335 return TRUE;
2338 /***
2339 * DESCRIPTION:
2340 * Removes all selection ranges
2342 * Parameters(s):
2343 * [I] infoPtr : valid pointer to the listview structure
2344 * [I] nSkipItem : item to skip removing the selection
2346 * RETURNS:
2347 * SUCCESS : TRUE
2348 * FAILURE : TRUE
2350 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr, INT nSkipItem)
2352 LVITEMW lvItem;
2353 RANGE *sel;
2354 INT i, pos;
2355 HDPA clone;
2357 if (infoPtr->bRemovingAllSelections) return TRUE;
2359 infoPtr->bRemovingAllSelections = TRUE;
2361 TRACE("()\n");
2363 lvItem.state = 0;
2364 lvItem.stateMask = LVIS_SELECTED;
2366 /* need to clone the DPA because callbacks can change it */
2367 clone = DPA_Clone(infoPtr->hdpaSelectionRanges, NULL);
2368 for ( pos = 0; (sel = DPA_GetPtr(clone, pos)); pos++ )
2370 for(i = sel->lower; i <= sel->upper; i++)
2372 if (i != nSkipItem)
2373 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
2376 DPA_Destroy(clone);
2378 infoPtr->bRemovingAllSelections = FALSE;
2380 return TRUE;
2383 /***
2384 * DESCRIPTION:
2385 * Retrieves the number of items that are marked as selected.
2387 * PARAMETER(S):
2388 * [I] infoPtr : valid pointer to the listview structure
2390 * RETURN:
2391 * Number of items selected.
2393 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2395 INT i, nSelectedCount = 0;
2397 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2399 INT i;
2400 for (i = 0; i < infoPtr->nItemCount; i++)
2402 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2403 nSelectedCount++;
2406 else
2408 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
2410 RANGE *sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
2411 nSelectedCount += sel->upper - sel->lower + 1;
2415 TRACE("nSelectedCount=%d\n", nSelectedCount);
2416 return nSelectedCount;
2419 /***
2420 * DESCRIPTION:
2421 * Manages the item focus.
2423 * PARAMETER(S):
2424 * [I] infoPtr : valid pointer to the listview structure
2425 * [I] INT : item index
2427 * RETURN:
2428 * TRUE : focused item changed
2429 * FALSE : focused item has NOT changed
2431 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2433 INT oldFocus = infoPtr->nFocusedItem;
2434 LVITEMW lvItem;
2436 lvItem.state = LVIS_FOCUSED;
2437 lvItem.stateMask = LVIS_FOCUSED;
2438 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2440 return oldFocus != infoPtr->nFocusedItem;
2443 /* Helper function for LISTVIEW_ShiftIndices *only* */
2444 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2446 if (nShiftItem < nItem) return nShiftItem;
2448 if (nShiftItem > nItem) return nShiftItem + direction;
2450 if (direction > 0) return nShiftItem + direction;
2452 return min(nShiftItem, infoPtr->nItemCount - 1);
2456 * DESCRIPTION:
2457 * Updates the various indices after an item has been inserted or deleted.
2459 * PARAMETER(S):
2460 * [I] infoPtr : valid pointer to the listview structure
2461 * [I] nItem : item index
2462 * [I] direction : Direction of shift, +1 or -1.
2464 * RETURN:
2465 * None
2467 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2469 INT nNewFocus;
2471 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2473 ranges_shift(infoPtr->hdpaSelectionRanges, nItem, direction, infoPtr->nItemCount);
2475 assert(abs(direction) == 1);
2477 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2479 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2480 if (nNewFocus != infoPtr->nFocusedItem)
2481 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2483 /* But we are not supposed to modify nHotItem! */
2488 * DESCRIPTION:
2489 * Adds a block of selections.
2491 * PARAMETER(S):
2492 * [I] infoPtr : valid pointer to the listview structure
2493 * [I] INT : item index
2495 * RETURN:
2496 * None
2498 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2500 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2501 INT nLast = max(infoPtr->nSelectionMark, nItem);
2502 INT i;
2503 LVITEMW item;
2505 if (nFirst == -1)
2506 nFirst = nItem;
2508 item.state = LVIS_SELECTED;
2509 item.stateMask = LVIS_SELECTED;
2511 /* FIXME: this is not correct LVS_OWNERDATA
2512 * See docu for LVN_ITEMCHANGED. Is there something similar for
2513 * RemoveGroupSelection (is there such a thing?)?
2515 for (i = nFirst; i <= nLast; i++)
2516 LISTVIEW_SetItemState(infoPtr,i,&item);
2518 LISTVIEW_SetItemFocus(infoPtr, nItem);
2519 infoPtr->nSelectionMark = nItem;
2523 /***
2524 * DESCRIPTION:
2525 * Sets a single group selection.
2527 * PARAMETER(S):
2528 * [I] infoPtr : valid pointer to the listview structure
2529 * [I] INT : item index
2531 * RETURN:
2532 * None
2534 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2536 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2537 INT i;
2538 LVITEMW item;
2539 POINT ptItem;
2540 RECT rcSel;
2542 LISTVIEW_RemoveAllSelections(infoPtr, -1);
2544 item.state = LVIS_SELECTED;
2545 item.stateMask = LVIS_SELECTED;
2547 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2549 INT nFirst, nLast;
2551 if (infoPtr->nSelectionMark == -1)
2552 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2553 else
2555 nFirst = min(infoPtr->nSelectionMark, nItem);
2556 nLast = max(infoPtr->nSelectionMark, nItem);
2558 for (i = nFirst; i <= nLast; i++)
2559 LISTVIEW_SetItemState(infoPtr, i, &item);
2561 else
2563 RECT rcItem, rcSelMark;
2564 ITERATOR i;
2566 rcItem.left = LVIR_BOUNDS;
2567 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2568 rcSelMark.left = LVIR_BOUNDS;
2569 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2570 UnionRect(&rcSel, &rcItem, &rcSelMark);
2571 iterator_frameditems(&i, infoPtr, &rcSel);
2572 while(iterator_next(&i))
2574 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
2575 if (PtInRect(&rcSel, ptItem))
2576 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
2578 iterator_destroy(&i);
2581 LISTVIEW_SetItemFocus(infoPtr, nItem);
2584 /***
2585 * DESCRIPTION:
2586 * Sets a single selection.
2588 * PARAMETER(S):
2589 * [I] infoPtr : valid pointer to the listview structure
2590 * [I] INT : item index
2592 * RETURN:
2593 * None
2595 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2597 LVITEMW lvItem;
2599 TRACE("nItem=%d\n", nItem);
2601 LISTVIEW_RemoveAllSelections(infoPtr, nItem);
2603 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2604 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2605 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2607 infoPtr->nSelectionMark = nItem;
2610 /***
2611 * DESCRIPTION:
2612 * Set selection(s) with keyboard.
2614 * PARAMETER(S):
2615 * [I] infoPtr : valid pointer to the listview structure
2616 * [I] INT : item index
2618 * RETURN:
2619 * SUCCESS : TRUE (needs to be repainted)
2620 * FAILURE : FALSE (nothing has changed)
2622 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2624 /* FIXME: pass in the state */
2625 LONG lStyle = infoPtr->dwStyle;
2626 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2627 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2628 BOOL bResult = FALSE;
2630 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2632 if (lStyle & LVS_SINGLESEL)
2634 bResult = TRUE;
2635 LISTVIEW_SetSelection(infoPtr, nItem);
2637 else
2639 if (wShift)
2641 bResult = TRUE;
2642 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2644 else if (wCtrl)
2646 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2648 else
2650 bResult = TRUE;
2651 LISTVIEW_SetSelection(infoPtr, nItem);
2654 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2657 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2658 return bResult;
2662 /***
2663 * DESCRIPTION:
2664 * Called when the mouse is being actively tracked and has hovered for a specified
2665 * amount of time
2667 * PARAMETER(S):
2668 * [I] infoPtr : valid pointer to the listview structure
2669 * [I] fwKeys : key indicator
2670 * [I] pts : mouse position
2672 * RETURN:
2673 * 0 if the message was processed, non-zero if there was an error
2675 * INFO:
2676 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2677 * over the item for a certain period of time.
2680 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2682 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2683 /* FIXME: select the item!!! */
2684 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2686 return 0;
2689 /***
2690 * DESCRIPTION:
2691 * Called whenever WM_MOUSEMOVE is received.
2693 * PARAMETER(S):
2694 * [I] infoPtr : valid pointer to the listview structure
2695 * [I] fwKeys : key indicator
2696 * [I] pts : mouse position
2698 * RETURN:
2699 * 0 if the message is processed, non-zero if there was an error
2701 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2703 TRACKMOUSEEVENT trackinfo;
2705 /* see if we are supposed to be tracking mouse hovering */
2706 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2707 /* fill in the trackinfo struct */
2708 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2709 trackinfo.dwFlags = TME_QUERY;
2710 trackinfo.hwndTrack = infoPtr->hwndSelf;
2711 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2713 /* see if we are already tracking this hwnd */
2714 _TrackMouseEvent(&trackinfo);
2716 if(!(trackinfo.dwFlags & TME_HOVER)) {
2717 trackinfo.dwFlags = TME_HOVER;
2719 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2720 _TrackMouseEvent(&trackinfo);
2724 return 0;
2728 /***
2729 * Tests wheather the item is assignable to a list with style lStyle
2731 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2733 if ( (lpLVItem->mask & LVIF_TEXT) &&
2734 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2735 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2737 return TRUE;
2740 /***
2741 * DESCRIPTION:
2742 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2744 * PARAMETER(S):
2745 * [I] infoPtr : valid pointer to the listview structure
2746 * [I] lpLVItem : valid pointer to new item atttributes
2747 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2748 * [O] bChanged : will be set to TRUE if the item really changed
2750 * RETURN:
2751 * SUCCESS : TRUE
2752 * FAILURE : FALSE
2754 static BOOL set_owner_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged)
2756 LONG lStyle = infoPtr->dwStyle;
2757 NMLISTVIEW nmlv;
2758 INT oldState;
2760 /* a virtual listview stores only the state for the main item */
2761 if (lpLVItem->iSubItem || !(lpLVItem->mask & LVIF_STATE)) return FALSE;
2763 oldState = (LVIS_FOCUSED | LVIS_SELECTED) & ~infoPtr->uCallbackMask;
2764 if (oldState) oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, oldState);
2765 TRACE("oldState=%x, newState=%x\n", oldState, lpLVItem->state);
2767 /* we're done if we don't need to change anything we handle */
2768 if ( !((oldState ^ lpLVItem->state) & lpLVItem->stateMask &
2769 ~infoPtr->uCallbackMask & (LVIS_FOCUSED | LVIS_SELECTED))) return TRUE;
2771 *bChanged = TRUE;
2774 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2775 * by LVS_OWERNDATA list controls
2778 /* if we handle the focus, and we're asked to change it, do it now */
2779 if ( lpLVItem->stateMask & LVIS_FOCUSED )
2781 if (lpLVItem->state & LVIS_FOCUSED)
2782 infoPtr->nFocusedItem = lpLVItem->iItem;
2783 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2784 infoPtr->nFocusedItem = -1;
2787 /* and the selection is the only other state a virtual list may hold */
2788 if (lpLVItem->stateMask & LVIS_SELECTED)
2790 RANGE range = { lpLVItem->iItem, lpLVItem->iItem };
2792 if (lpLVItem->state & LVIS_SELECTED)
2794 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr, lpLVItem->iItem);
2795 ranges_add(infoPtr->hdpaSelectionRanges, range);
2797 else
2798 ranges_del(infoPtr->hdpaSelectionRanges, range);
2801 /* notify the parent now that things have changed */
2802 ZeroMemory(&nmlv, sizeof(nmlv));
2803 nmlv.iItem = lpLVItem->iItem;
2804 nmlv.uNewState = lpLVItem->state;
2805 nmlv.uOldState = oldState;
2806 nmlv.uChanged = LVIF_STATE;
2807 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2809 return TRUE;
2812 /***
2813 * DESCRIPTION:
2814 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2816 * PARAMETER(S):
2817 * [I] infoPtr : valid pointer to the listview structure
2818 * [I] lpLVItem : valid pointer to new item atttributes
2819 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2820 * [O] bChanged : will be set to TRUE if the item really changed
2822 * RETURN:
2823 * SUCCESS : TRUE
2824 * FAILURE : FALSE
2826 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged)
2828 LONG lStyle = infoPtr->dwStyle;
2829 HDPA hdpaSubItems;
2830 LISTVIEW_ITEM *lpItem;
2831 NMLISTVIEW nmlv;
2832 UINT uChanged = 0, oldState;
2834 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2835 if (!hdpaSubItems && hdpaSubItems != (HDPA)-1) return FALSE;
2837 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2838 if (!lpItem) return FALSE;
2840 /* we need to handle the focus, and selection differently */
2841 oldState = (LVIS_FOCUSED | LVIS_SELECTED) & ~infoPtr->uCallbackMask;
2842 if (oldState) oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, oldState);
2844 TRACE("oldState=0x%x, state=0x%x\n", oldState, lpItem->state);
2845 /* determine what fields will change */
2846 if ((lpLVItem->mask & LVIF_STATE) && ((oldState ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
2847 uChanged |= LVIF_STATE;
2849 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2850 uChanged |= LVIF_IMAGE;
2852 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2853 uChanged |= LVIF_PARAM;
2855 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2856 uChanged |= LVIF_INDENT;
2858 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2859 uChanged |= LVIF_TEXT;
2861 TRACE("uChanged=0x%x\n", uChanged);
2862 if (!uChanged) return TRUE;
2863 *bChanged = TRUE;
2865 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2866 nmlv.iItem = lpLVItem->iItem;
2867 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2868 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2869 nmlv.uChanged = uChanged;
2870 nmlv.lParam = lpItem->lParam;
2872 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2873 if(lpItem->valid && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
2874 return FALSE;
2876 /* copy information */
2877 if (lpLVItem->mask & LVIF_TEXT)
2878 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
2880 if (lpLVItem->mask & LVIF_IMAGE)
2881 lpItem->hdr.iImage = lpLVItem->iImage;
2883 if (lpLVItem->mask & LVIF_PARAM)
2884 lpItem->lParam = lpLVItem->lParam;
2886 if (lpLVItem->mask & LVIF_INDENT)
2887 lpItem->iIndent = lpLVItem->iIndent;
2889 if (uChanged & LVIF_STATE)
2891 RANGE range = { lpLVItem->iItem, lpLVItem->iItem };
2893 lpItem->state &= ~lpLVItem->stateMask;
2894 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2895 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
2897 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr, lpLVItem->iItem);
2898 ranges_add(infoPtr->hdpaSelectionRanges, range);
2900 else if (lpLVItem->stateMask & LVIS_SELECTED)
2901 ranges_del(infoPtr->hdpaSelectionRanges, range);
2903 /* if we are asked to change focus, and we manage it, do it */
2904 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
2906 if (lpLVItem->state & LVIS_FOCUSED)
2908 infoPtr->nFocusedItem = lpLVItem->iItem;
2909 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
2911 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2912 infoPtr->nFocusedItem = -1;
2916 /* if we're inserting the item, we're done */
2917 if (!lpItem->valid) return TRUE;
2919 /* send LVN_ITEMCHANGED notification */
2920 nmlv.lParam = lpItem->lParam;
2921 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2923 return TRUE;
2926 /***
2927 * DESCRIPTION:
2928 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2930 * PARAMETER(S):
2931 * [I] infoPtr : valid pointer to the listview structure
2932 * [I] lpLVItem : valid pointer to new subitem atttributes
2933 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2934 * [O] bChanged : will be set to TRUE if the item really changed
2936 * RETURN:
2937 * SUCCESS : TRUE
2938 * FAILURE : FALSE
2940 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged)
2942 HDPA hdpaSubItems;
2943 LISTVIEW_SUBITEM *lpSubItem;
2945 /* set subitem only if column is present */
2946 if (Header_GetItemCount(infoPtr->hwndHeader) <= lpLVItem->iSubItem)
2947 return FALSE;
2949 /* First do some sanity checks */
2950 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
2951 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
2953 /* get the subitem structure, and create it if not there */
2954 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2955 if (!hdpaSubItems) return FALSE;
2957 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
2958 if (!lpSubItem)
2960 LISTVIEW_SUBITEM *tmpSubItem;
2961 INT i;
2963 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2964 if (!lpSubItem) return FALSE;
2965 /* we could binary search here, if need be...*/
2966 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2968 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2969 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
2971 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
2973 COMCTL32_Free(lpSubItem);
2974 return FALSE;
2976 lpSubItem->iSubItem = lpLVItem->iSubItem;
2977 *bChanged = TRUE;
2980 if (lpLVItem->mask & LVIF_IMAGE)
2981 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
2983 lpSubItem->hdr.iImage = lpLVItem->iImage;
2984 *bChanged = TRUE;
2987 if (lpLVItem->mask & LVIF_TEXT)
2988 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
2990 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
2991 *bChanged = TRUE;
2994 return TRUE;
2997 /***
2998 * DESCRIPTION:
2999 * Sets item attributes.
3001 * PARAMETER(S):
3002 * [I] infoPtr : valid pointer to the listview structure
3003 * [I] LPLVITEM : new item atttributes
3004 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3006 * RETURN:
3007 * SUCCESS : TRUE
3008 * FAILURE : FALSE
3010 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
3012 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3013 INT nOldFocus = infoPtr->nFocusedItem;
3014 LPWSTR pszText = NULL;
3015 BOOL bResult, bChanged = FALSE;
3017 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3019 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3020 return FALSE;
3022 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3023 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3025 pszText = lpLVItem->pszText;
3026 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3029 /* actually set the fields */
3030 if (infoPtr->dwStyle & LVS_OWNERDATA)
3031 bResult = set_owner_item(infoPtr, lpLVItem, TRUE, &bChanged);
3032 else
3034 /* sanity checks first */
3035 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3037 if (lpLVItem->iSubItem)
3038 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3039 else
3040 bResult = set_main_item(infoPtr, lpLVItem, TRUE, &bChanged);
3043 /* redraw item, if necessary */
3044 if (bChanged && !infoPtr->bIsDrawing)
3046 if (nOldFocus != infoPtr->nFocusedItem && infoPtr->bFocus)
3047 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->rcFocus);
3049 /* this little optimization eliminates some nasty flicker */
3050 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3051 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3052 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3053 else
3054 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3056 /* restore text */
3057 if (pszText)
3059 textfreeT(lpLVItem->pszText, isW);
3060 lpLVItem->pszText = pszText;
3063 return bResult;
3066 /***
3067 * DESCRIPTION:
3068 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3070 * PARAMETER(S):
3071 * [I] infoPtr : valid pointer to the listview structure
3073 * RETURN:
3074 * item index
3076 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3078 LONG lStyle = infoPtr->dwStyle;
3079 UINT uView = lStyle & LVS_TYPEMASK;
3080 INT nItem = 0;
3081 SCROLLINFO scrollInfo;
3083 scrollInfo.cbSize = sizeof(SCROLLINFO);
3084 scrollInfo.fMask = SIF_POS;
3086 if (uView == LVS_LIST)
3088 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3089 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3091 else if (uView == LVS_REPORT)
3093 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3094 nItem = scrollInfo.nPos;
3096 else
3098 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3099 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3102 TRACE("nItem=%d\n", nItem);
3104 return nItem;
3108 /***
3109 * DESCRIPTION:
3110 * Erases the background of the given rectangle
3112 * PARAMETER(S):
3113 * [I] infoPtr : valid pointer to the listview structure
3114 * [I] hdc : device context handle
3115 * [I] lprcBox : clipping rectangle
3117 * RETURN:
3118 * Success: TRUE
3119 * Failure: FALSE
3121 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
3123 if (!infoPtr->hBkBrush) return FALSE;
3125 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3127 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3130 /***
3131 * DESCRIPTION:
3132 * Draws an item.
3134 * PARAMETER(S):
3135 * [I] infoPtr : valid pointer to the listview structure
3136 * [I] hdc : device context handle
3137 * [I] nItem : item index
3138 * [I] nSubItem : subitem index
3139 * [I] pos : item position in client coordinates
3140 * [I] cdmode : custom draw mode
3142 * RETURN:
3143 * Success: TRUE
3144 * Failure: FALSE
3146 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3148 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3149 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3150 DWORD cditemmode = CDRF_DODEFAULT;
3151 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3152 NMLVCUSTOMDRAW nmlvcd;
3153 HIMAGELIST himl;
3154 LVITEMW lvItem;
3156 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3158 /* get information needed for drawing the item */
3159 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3160 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3161 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3162 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3163 lvItem.iItem = nItem;
3164 lvItem.iSubItem = nSubItem;
3165 lvItem.cchTextMax = DISP_TEXT_SIZE;
3166 lvItem.pszText = szDispText;
3167 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3168 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3170 /* now check if we need to update the focus rectangle */
3171 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3173 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3174 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel)) return FALSE;
3175 OffsetRect(&rcBox, pos.x, pos.y);
3176 OffsetRect(&rcState, pos.x, pos.y);
3177 OffsetRect(&rcIcon, pos.x, pos.y);
3178 OffsetRect(&rcLabel, pos.x, pos.y);
3179 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3180 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3182 /* fill in the custom draw structure */
3183 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox);
3184 nmlvcd.nmcd.dwItemSpec = lvItem.iItem;
3185 nmlvcd.iSubItem = lvItem.iSubItem;
3186 if (lvItem.state & LVIS_SELECTED) nmlvcd.nmcd.uItemState |= CDIS_SELECTED;
3187 if (lvItem.state & LVIS_FOCUSED) nmlvcd.nmcd.uItemState |= CDIS_FOCUS;
3188 if (lvItem.iItem == infoPtr->nHotItem) nmlvcd.nmcd.uItemState |= CDIS_HOT;
3189 nmlvcd.nmcd.lItemlParam = lvItem.lParam;
3190 if ((lvItem.state & LVIS_SELECTED) &&
3191 (lvItem.iSubItem == 0 ||
3192 (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))))
3194 if (infoPtr->bFocus)
3196 nmlvcd.clrTextBk = comctl32_color.clrHighlight;
3197 nmlvcd.clrText = comctl32_color.clrHighlightText;
3199 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
3201 nmlvcd.clrTextBk = comctl32_color.clr3dFace;
3202 nmlvcd.clrText = comctl32_color.clrBtnText;
3206 if (cdmode & CDRF_NOTIFYITEMDRAW)
3207 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3208 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3210 /* state icons */
3211 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3213 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3214 if (uStateImage)
3215 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3218 /* small icons */
3219 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3220 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3221 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3222 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3224 /* Don't bother painting item being edited */
3225 if (infoPtr->hwndEdit && lprcFocus && nSubItem == 0) goto postpaint;
3227 /* Set the text attributes */
3228 SetBkMode(hdc, nmlvcd.clrTextBk == CLR_NONE ? TRANSPARENT : OPAQUE);
3229 if (nmlvcd.clrTextBk != CLR_NONE)
3230 SetBkColor(hdc, nmlvcd.clrTextBk == CLR_DEFAULT ? infoPtr->clrTextBkDefault : nmlvcd.clrTextBk);
3231 SetTextColor(hdc, nmlvcd.clrText);
3233 /* draw the selection background, if we're drawing the main item */
3234 if (nSubItem == 0)
3236 rcSelect = rcLabel;
3237 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3238 rcSelect.right = rcBox.right;
3240 if (lvItem.state & LVIS_SELECTED)
3241 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3242 if(lprcFocus) *lprcFocus = rcSelect;
3245 /* figure out the text drawing flags */
3246 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3247 if (uView == LVS_ICON)
3248 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3249 else
3251 INT align = DT_LEFT;
3253 if (nSubItem)
3255 LVCOLUMNW lvColumn;
3256 lvColumn.mask = LVCF_FMT;
3257 LISTVIEW_GetColumnT(infoPtr, nSubItem, &lvColumn, TRUE);
3258 TRACE("lvColumn=%s\n", debuglvcolumn_t(&lvColumn, TRUE));
3259 if (lvColumn.fmt & LVCFMT_RIGHT) align = DT_RIGHT;
3260 else if (lvColumn.fmt & LVCFMT_CENTER) align = DT_CENTER;
3262 uFormat |= align;
3264 if (!(uFormat & (DT_RIGHT | DT_CENTER))) rcLabel.left += 2;
3265 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3267 postpaint:
3268 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3269 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3270 return TRUE;
3273 /***
3274 * DESCRIPTION:
3275 * Draws listview items when in owner draw mode.
3277 * PARAMETER(S):
3278 * [I] infoPtr : valid pointer to the listview structure
3279 * [I] hdc : device context handle
3281 * RETURN:
3282 * None
3284 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
3286 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3287 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3288 POINT Origin, Position;
3289 DRAWITEMSTRUCT dis;
3290 LVITEMW item;
3291 ITERATOR i;
3293 TRACE("()\n");
3295 ZeroMemory(&dis, sizeof(dis));
3297 /* Get scroll info once before loop */
3298 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3300 /* figure out what we need to draw */
3301 iterator_visibleitems(&i, infoPtr, hdc);
3303 /* send cache hint notification */
3304 if (infoPtr->dwStyle & LVS_OWNERDATA)
3306 RANGE range = iterator_range(&i);
3307 NMLVCACHEHINT nmlv;
3309 nmlv.iFrom = range.lower;
3310 nmlv.iTo = range.upper;
3311 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3314 /* iterate through the invalidated rows */
3315 while(iterator_prev(&i))
3317 item.iItem = i.nItem;
3318 item.iSubItem = 0;
3319 item.mask = LVIF_PARAM | LVIF_STATE;
3320 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3321 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3323 dis.CtlType = ODT_LISTVIEW;
3324 dis.CtlID = uID;
3325 dis.itemID = item.iItem;
3326 dis.itemAction = ODA_DRAWENTIRE;
3327 dis.itemState = 0;
3328 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3329 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3330 dis.hwndItem = infoPtr->hwndSelf;
3331 dis.hDC = hdc;
3332 if (!LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position)) continue;
3333 dis.rcItem.left = Position.x + Origin.x;
3334 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3335 dis.rcItem.top = Position.y + Origin.y;
3336 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3337 dis.itemData = item.lParam;
3339 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3340 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3342 iterator_destroy(&i);
3345 /***
3346 * DESCRIPTION:
3347 * Draws listview items when in report display mode.
3349 * PARAMETER(S):
3350 * [I] infoPtr : valid pointer to the listview structure
3351 * [I] hdc : device context handle
3352 * [I] cdmode : custom draw mode
3354 * RETURN:
3355 * None
3357 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3359 INT rgntype, nColumnCount, nFirstCol, nLastCol, nCol;
3360 RECT rcClip;
3361 COLUMNCACHE *lpCols;
3362 POINT Origin, Position;
3363 ITERATOR i;
3365 TRACE("()\n");
3367 /* figure out what to draw */
3368 rgntype = GetClipBox(hdc, &rcClip);
3369 if (rgntype == NULLREGION) return;
3371 /* cache column info */
3372 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3373 lpCols = COMCTL32_Alloc(nColumnCount * sizeof(COLUMNCACHE));
3374 if (!lpCols) return;
3375 for (nCol = 0; nCol < nColumnCount; nCol++)
3377 Header_GetItemRect(infoPtr->hwndHeader, nCol, &lpCols[nCol].rc);
3378 TRACE("lpCols[%d].rc=%s\n", nCol, debugrect(&lpCols[nCol].rc));
3381 /* Get scroll info once before loop */
3382 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3384 /* we now narrow the columns as well */
3385 nLastCol = nColumnCount - 1;
3386 for(nFirstCol = 0; nFirstCol < nColumnCount; nFirstCol++)
3387 if (lpCols[nFirstCol].rc.right + Origin.x >= rcClip.left) break;
3388 for(nLastCol = nColumnCount - 1; nLastCol >= 0; nLastCol--)
3389 if (lpCols[nLastCol].rc.left + Origin.x < rcClip.right) break;
3391 /* figure out what we need to draw */
3392 iterator_visibleitems(&i, infoPtr, hdc);
3394 /* a last few bits before we start drawing */
3395 TRACE("Colums=(%di - %d)\n", nFirstCol, nLastCol);
3397 /* iterate through the invalidated rows */
3398 while(iterator_prev(&i))
3400 /* iterate through the invalidated columns */
3401 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
3403 if (!LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position)) continue;
3404 Position.x += Origin.x;
3405 Position.y += Origin.y;
3407 if (rgntype == COMPLEXREGION)
3409 RECT rcItem;
3410 rcItem.left = Position.x + lpCols[nCol].rc.left;
3411 rcItem.right = rcItem.left + (lpCols[nCol].rc.right - lpCols[nCol].rc.left);
3412 rcItem.top = Position.y;
3413 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3414 if (!RectVisible(hdc, &rcItem)) continue;
3417 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, nCol, Position, cdmode);
3420 iterator_destroy(&i);
3422 /* cleanup the mess */
3423 COMCTL32_Free(lpCols);
3426 /***
3427 * DESCRIPTION:
3428 * Draws listview items when in list display mode.
3430 * PARAMETER(S):
3431 * [I] infoPtr : valid pointer to the listview structure
3432 * [I] hdc : device context handle
3433 * [I] cdmode : custom draw mode
3435 * RETURN:
3436 * None
3438 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3440 POINT Origin, Position;
3441 ITERATOR i;
3443 /* Get scroll info once before loop */
3444 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3446 /* figure out what we need to draw */
3447 iterator_visibleitems(&i, infoPtr, hdc);
3449 while(iterator_prev(&i))
3451 if (!LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position)) continue;
3452 Position.x += Origin.x;
3453 Position.y += Origin.y;
3455 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, 0, Position, cdmode);
3457 iterator_destroy(&i);
3461 /***
3462 * DESCRIPTION:
3463 * Draws listview items.
3465 * PARAMETER(S):
3466 * [I] infoPtr : valid pointer to the listview structure
3467 * [I] HDC : device context handle
3469 * RETURN:
3470 * NoneX
3472 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3474 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3475 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3476 NMLVCUSTOMDRAW nmlvcd;
3477 HFONT hOldFont;
3478 DWORD cdmode;
3479 INT oldBkMode;
3480 RECT rcClient;
3482 LISTVIEW_DUMP(infoPtr);
3484 infoPtr->bIsDrawing = TRUE;
3486 /* save dc values we're gonna trash while drawing */
3487 hOldFont = SelectObject(hdc, infoPtr->hFont);
3488 oldBkMode = GetBkMode(hdc);
3489 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3490 oldTextColor = GetTextColor(hdc);
3492 oldClrTextBk = infoPtr->clrTextBk;
3493 oldClrText = infoPtr->clrText;
3495 GetClientRect(infoPtr->hwndSelf, &rcClient);
3496 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient);
3497 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3498 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3500 /* Use these colors to draw the items */
3501 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3502 infoPtr->clrText = nmlvcd.clrText;
3504 /* nothing to draw */
3505 if(infoPtr->nItemCount == 0) goto enddraw;
3507 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3508 LISTVIEW_RefreshOwnerDraw(infoPtr, hdc);
3509 else
3511 if (uView == LVS_REPORT)
3512 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3513 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3514 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3516 /* if we have a focus rect, draw it */
3517 if (infoPtr->bFocus)
3518 DrawFocusRect(hdc, &infoPtr->rcFocus);
3521 enddraw:
3522 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3523 notify_customdraw(infoPtr, CDDS_POSTPAINT, &nmlvcd);
3525 infoPtr->clrTextBk = oldClrTextBk;
3526 infoPtr->clrText = oldClrText;
3528 SelectObject(hdc, hOldFont);
3529 SetBkMode(hdc, oldBkMode);
3530 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3531 SetTextColor(hdc, oldTextColor);
3532 infoPtr->bIsDrawing = FALSE;
3536 /***
3537 * DESCRIPTION:
3538 * Calculates the approximate width and height of a given number of items.
3540 * PARAMETER(S):
3541 * [I] infoPtr : valid pointer to the listview structure
3542 * [I] INT : number of items
3543 * [I] INT : width
3544 * [I] INT : height
3546 * RETURN:
3547 * Returns a DWORD. The width in the low word and the height in high word.
3549 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3550 WORD wWidth, WORD wHeight)
3552 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3553 INT nItemCountPerColumn = 1;
3554 INT nColumnCount = 0;
3555 DWORD dwViewRect = 0;
3557 if (nItemCount == -1)
3558 nItemCount = infoPtr->nItemCount;
3560 if (uView == LVS_LIST)
3562 if (wHeight == 0xFFFF)
3564 /* use current height */
3565 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3568 if (wHeight < infoPtr->nItemHeight)
3569 wHeight = infoPtr->nItemHeight;
3571 if (nItemCount > 0)
3573 if (infoPtr->nItemHeight > 0)
3575 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3576 if (nItemCountPerColumn == 0)
3577 nItemCountPerColumn = 1;
3579 if (nItemCount % nItemCountPerColumn != 0)
3580 nColumnCount = nItemCount / nItemCountPerColumn;
3581 else
3582 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3586 /* Microsoft padding magic */
3587 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3588 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3590 dwViewRect = MAKELONG(wWidth, wHeight);
3592 else if (uView == LVS_REPORT)
3593 FIXME("uView == LVS_REPORT: not implemented\n");
3594 else if (uView == LVS_SMALLICON)
3595 FIXME("uView == LVS_SMALLICON: not implemented\n");
3596 else if (uView == LVS_ICON)
3597 FIXME("uView == LVS_ICON: not implemented\n");
3599 return dwViewRect;
3602 /***
3603 * DESCRIPTION:
3604 * Arranges listview items in icon display mode.
3606 * PARAMETER(S):
3607 * [I] infoPtr : valid pointer to the listview structure
3608 * [I] INT : alignment code
3610 * RETURN:
3611 * SUCCESS : TRUE
3612 * FAILURE : FALSE
3614 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3616 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3617 BOOL bResult = FALSE;
3619 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3621 switch (nAlignCode)
3623 case LVA_ALIGNLEFT:
3624 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3625 break;
3626 case LVA_ALIGNTOP:
3627 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3628 break;
3629 case LVA_DEFAULT:
3630 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3631 break;
3632 case LVA_SNAPTOGRID:
3633 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3634 break;
3638 return bResult;
3641 /* << LISTVIEW_CreateDragImage >> */
3644 /***
3645 * DESCRIPTION:
3646 * Removes all listview items and subitems.
3648 * PARAMETER(S):
3649 * [I] infoPtr : valid pointer to the listview structure
3651 * RETURN:
3652 * SUCCESS : TRUE
3653 * FAILURE : FALSE
3655 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3657 LONG lStyle = infoPtr->dwStyle;
3658 UINT uView = lStyle & LVS_TYPEMASK;
3659 LISTVIEW_ITEM *lpItem;
3660 LISTVIEW_SUBITEM *lpSubItem;
3661 NMLISTVIEW nmlv;
3662 BOOL bSuppress;
3663 BOOL bResult = FALSE;
3664 HDPA hdpaSubItems;
3666 TRACE("()\n");
3668 LISTVIEW_RemoveAllSelections(infoPtr, -1);
3669 infoPtr->nSelectionMark=-1;
3670 infoPtr->nFocusedItem=-1;
3671 SetRectEmpty(&infoPtr->rcFocus);
3672 /* But we are supposed to leave nHotItem as is! */
3674 if (lStyle & LVS_OWNERDATA)
3676 infoPtr->nItemCount = 0;
3677 LISTVIEW_InvalidateList(infoPtr);
3678 return TRUE;
3681 if (infoPtr->nItemCount > 0)
3683 INT i, j;
3685 /* send LVN_DELETEALLITEMS notification */
3686 /* verify if subsequent LVN_DELETEITEM notifications should be
3687 suppressed */
3688 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3689 nmlv.iItem = -1;
3690 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3692 for (i = 0; i < infoPtr->nItemCount; i++)
3694 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3695 if (hdpaSubItems != NULL)
3697 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3699 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3700 if (lpSubItem != NULL)
3702 /* free subitem string */
3703 if (is_textW(lpSubItem->hdr.pszText))
3704 COMCTL32_Free(lpSubItem->hdr.pszText);
3706 /* free subitem */
3707 COMCTL32_Free(lpSubItem);
3711 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3712 if (lpItem != NULL)
3714 if (!bSuppress)
3716 /* send LVN_DELETEITEM notification */
3717 nmlv.iItem = i;
3718 nmlv.lParam = lpItem->lParam;
3719 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3722 /* free item string */
3723 if (is_textW(lpItem->hdr.pszText))
3724 COMCTL32_Free(lpItem->hdr.pszText);
3726 /* free item */
3727 COMCTL32_Free(lpItem);
3730 DPA_Destroy(hdpaSubItems);
3734 /* reinitialize listview memory */
3735 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3736 infoPtr->nItemCount = 0;
3737 DPA_DeleteAllPtrs(infoPtr->hdpaPosX);
3738 DPA_DeleteAllPtrs(infoPtr->hdpaPosY);
3740 /* align items (set position of each item) */
3741 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3743 if (lStyle & LVS_ALIGNLEFT)
3745 LISTVIEW_AlignLeft(infoPtr);
3747 else
3749 LISTVIEW_AlignTop(infoPtr);
3753 LISTVIEW_UpdateScroll(infoPtr);
3755 LISTVIEW_InvalidateList(infoPtr);
3758 return bResult;
3761 /***
3762 * DESCRIPTION:
3763 * Removes a column from the listview control.
3765 * PARAMETER(S):
3766 * [I] infoPtr : valid pointer to the listview structure
3767 * [I] INT : column index
3769 * RETURN:
3770 * SUCCESS : TRUE
3771 * FAILURE : FALSE
3773 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3775 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3776 RECT rcCol, rcOld;
3778 TRACE("nColumn=%d\n", nColumn);
3780 if (nColumn <= 0) return FALSE;
3782 if (uView == LVS_REPORT)
3784 if (!Header_GetItemRect(infoPtr->hwndHeader, nColumn, &rcCol))
3785 return FALSE;
3787 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3788 return FALSE;
3791 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3793 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3794 HDPA hdpaSubItems;
3795 INT nItem, nSubItem, i;
3797 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3799 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3800 if (!hdpaSubItems) continue;
3801 nSubItem = 0;
3802 lpDelItem = 0;
3803 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3805 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3806 if (!lpSubItem) break;
3807 if (lpSubItem->iSubItem == nColumn)
3809 nSubItem = i;
3810 lpDelItem = lpSubItem;
3812 else if (lpSubItem->iSubItem > nColumn)
3814 lpSubItem->iSubItem--;
3818 /* if we found our subitem, zapp it */
3819 if (nSubItem > 0)
3821 /* free string */
3822 if (is_textW(lpDelItem->hdr.pszText))
3823 COMCTL32_Free(lpDelItem->hdr.pszText);
3825 /* free item */
3826 COMCTL32_Free(lpDelItem);
3828 /* free dpa memory */
3829 DPA_DeletePtr(hdpaSubItems, nSubItem);
3834 /* we need to worry about display issues in report mode only */
3835 if (uView != LVS_REPORT) return TRUE;
3837 /* if we have a focus, must first erase the focus rect */
3838 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
3840 /* Need to reset the item width when deleting a column */
3841 infoPtr->nItemWidth -= rcCol.right - rcCol.left;
3843 /* update scrollbar(s) */
3844 LISTVIEW_UpdateScroll(infoPtr);
3846 /* scroll to cover the deleted column, and invalidate for redraw */
3847 rcOld = infoPtr->rcList;
3848 rcOld.left = rcCol.left;
3849 ScrollWindowEx(infoPtr->hwndSelf, -(rcCol.right - rcCol.left), 0,
3850 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3852 /* we can restore focus now */
3853 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
3855 return TRUE;
3858 /***
3859 * DESCRIPTION:
3860 * Removes an item from the listview control.
3862 * PARAMETER(S):
3863 * [I] infoPtr : valid pointer to the listview structure
3864 * [I] INT : item index
3866 * RETURN:
3867 * SUCCESS : TRUE
3868 * FAILURE : FALSE
3870 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
3872 LONG lStyle = infoPtr->dwStyle;
3873 UINT uView = lStyle & LVS_TYPEMASK;
3874 NMLISTVIEW nmlv;
3875 BOOL bResult = FALSE;
3876 HDPA hdpaSubItems;
3877 LISTVIEW_ITEM *lpItem;
3878 LISTVIEW_SUBITEM *lpSubItem;
3879 LVITEMW item;
3880 INT i;
3882 TRACE("(nItem=%d)\n", nItem);
3885 /* First, send LVN_DELETEITEM notification. */
3886 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
3887 nmlv.iItem = nItem;
3888 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3890 if (nItem == infoPtr->nFocusedItem)
3892 infoPtr->nFocusedItem = -1;
3893 SetRectEmpty(&infoPtr->rcFocus);
3896 /* remove it from the selection range */
3897 item.state = LVIS_SELECTED;
3898 item.stateMask = LVIS_SELECTED;
3899 LISTVIEW_SetItemState(infoPtr,nItem,&item);
3901 if (lStyle & LVS_OWNERDATA)
3903 infoPtr->nItemCount--;
3904 LISTVIEW_InvalidateList(infoPtr); /*FIXME: optimize */
3905 return TRUE;
3908 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3910 /* initialize memory */
3911 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3913 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
3914 if (hdpaSubItems != NULL)
3916 infoPtr->nItemCount--;
3917 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3919 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3920 if (lpSubItem != NULL)
3922 /* free item string */
3923 if (is_textW(lpSubItem->hdr.pszText))
3924 COMCTL32_Free(lpSubItem->hdr.pszText);
3926 /* free item */
3927 COMCTL32_Free(lpSubItem);
3931 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3932 if (lpItem != NULL)
3934 /* free item string */
3935 if (is_textW(lpItem->hdr.pszText))
3936 COMCTL32_Free(lpItem->hdr.pszText);
3938 /* free item */
3939 COMCTL32_Free(lpItem);
3942 bResult = DPA_Destroy(hdpaSubItems);
3943 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
3944 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
3947 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
3949 /* align items (set position of each item) */
3950 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
3952 if (lStyle & LVS_ALIGNLEFT)
3953 LISTVIEW_AlignLeft(infoPtr);
3954 else
3955 LISTVIEW_AlignTop(infoPtr);
3958 LISTVIEW_UpdateScroll(infoPtr);
3960 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
3963 return bResult;
3967 /***
3968 * DESCRIPTION:
3969 * Callback implementation for editlabel control
3971 * PARAMETER(S):
3972 * [I] infoPtr : valid pointer to the listview structure
3973 * [I] pszText : modified text
3974 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
3976 * RETURN:
3977 * SUCCESS : TRUE
3978 * FAILURE : FALSE
3980 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
3982 NMLVDISPINFOW dispInfo;
3984 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
3986 ZeroMemory(&dispInfo, sizeof(dispInfo));
3987 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
3988 dispInfo.item.iItem = infoPtr->nEditLabelItem;
3989 dispInfo.item.iSubItem = 0;
3990 dispInfo.item.stateMask = ~0;
3991 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
3992 /* add the text from the edit in */
3993 dispInfo.item.mask |= LVIF_TEXT;
3994 dispInfo.item.pszText = pszText;
3995 dispInfo.item.cchTextMax = textlenT(pszText, isW);
3997 /* Do we need to update the Item Text */
3998 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
3999 if (!pszText) return TRUE;
4001 ZeroMemory(&dispInfo, sizeof(dispInfo));
4002 dispInfo.item.mask = LVIF_TEXT;
4003 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4004 dispInfo.item.iSubItem = 0;
4005 dispInfo.item.pszText = pszText;
4006 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4007 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4010 /***
4011 * DESCRIPTION:
4012 * Begin in place editing of specified list view item
4014 * PARAMETER(S):
4015 * [I] infoPtr : valid pointer to the listview structure
4016 * [I] INT : item index
4017 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4019 * RETURN:
4020 * SUCCESS : TRUE
4021 * FAILURE : FALSE
4023 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4025 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4026 NMLVDISPINFOW dispInfo;
4027 RECT rect;
4029 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4031 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4032 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4034 infoPtr->nEditLabelItem = nItem;
4036 /* Is the EditBox still there, if so remove it */
4037 if(infoPtr->hwndEdit != 0)
4039 SetFocus(infoPtr->hwndSelf);
4040 infoPtr->hwndEdit = 0;
4043 LISTVIEW_SetSelection(infoPtr, nItem);
4044 LISTVIEW_SetItemFocus(infoPtr, nItem);
4046 rect.left = LVIR_LABEL;
4047 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4049 ZeroMemory(&dispInfo, sizeof(dispInfo));
4050 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4051 dispInfo.item.iItem = nItem;
4052 dispInfo.item.iSubItem = 0;
4053 dispInfo.item.stateMask = ~0;
4054 dispInfo.item.pszText = szDispText;
4055 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4056 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4058 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4059 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4060 if (!infoPtr->hwndEdit) return 0;
4062 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4064 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4065 infoPtr->hwndEdit = 0;
4066 return 0;
4069 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4070 SetFocus(infoPtr->hwndEdit);
4071 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4072 return infoPtr->hwndEdit;
4076 /***
4077 * DESCRIPTION:
4078 * Ensures the specified item is visible, scrolling into view if necessary.
4080 * PARAMETER(S):
4081 * [I] infoPtr : valid pointer to the listview structure
4082 * [I] nItem : item index
4083 * [I] bPartial : partially or entirely visible
4085 * RETURN:
4086 * SUCCESS : TRUE
4087 * FAILURE : FALSE
4089 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4091 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4092 INT nScrollPosHeight = 0;
4093 INT nScrollPosWidth = 0;
4094 INT nHorzAdjust = 0;
4095 INT nVertAdjust = 0;
4096 INT nHorzDiff = 0;
4097 INT nVertDiff = 0;
4098 RECT rcItem, rcTemp;
4100 rcItem.left = LVIR_BOUNDS;
4101 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4103 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4105 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4107 /* scroll left/right, but in LVS_REPORT mode */
4108 if (uView == LVS_LIST)
4109 nScrollPosWidth = infoPtr->nItemWidth;
4110 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4111 nScrollPosWidth = 1;
4113 if (rcItem.left < infoPtr->rcList.left)
4115 nHorzAdjust = -1;
4116 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4118 else
4120 nHorzAdjust = 1;
4121 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4125 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4127 /* scroll up/down, but not in LVS_LIST mode */
4128 if (uView == LVS_REPORT)
4129 nScrollPosHeight = infoPtr->nItemHeight;
4130 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4131 nScrollPosHeight = 1;
4133 if (rcItem.top < infoPtr->rcList.top)
4135 nVertAdjust = -1;
4136 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4138 else
4140 nVertAdjust = 1;
4141 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4145 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4147 if (nScrollPosWidth)
4149 INT diff = nHorzDiff / nScrollPosWidth;
4150 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4151 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4154 if (nScrollPosHeight)
4156 INT diff = nVertDiff / nScrollPosHeight;
4157 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4158 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4161 return TRUE;
4164 /***
4165 * DESCRIPTION:
4166 * Searches for an item with specific characteristics.
4168 * PARAMETER(S):
4169 * [I] hwnd : window handle
4170 * [I] nStart : base item index
4171 * [I] lpFindInfo : item information to look for
4173 * RETURN:
4174 * SUCCESS : index of item
4175 * FAILURE : -1
4177 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4178 LPLVFINDINFOW lpFindInfo)
4180 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4181 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4182 BOOL bWrap = FALSE, bNearest = FALSE;
4183 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4184 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4185 POINT Position, Destination;
4186 LVITEMW lvItem;
4188 if (!lpFindInfo || nItem < 0) return -1;
4190 lvItem.mask = 0;
4191 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4193 lvItem.mask |= LVIF_TEXT;
4194 lvItem.pszText = szDispText;
4195 lvItem.cchTextMax = DISP_TEXT_SIZE;
4198 if (lpFindInfo->flags & LVFI_WRAP)
4199 bWrap = TRUE;
4201 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4202 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4204 POINT Origin;
4206 FIXME("LVFI_NEARESTXY is slow.\n");
4207 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
4208 Destination.x = lpFindInfo->pt.x - Origin.x;
4209 Destination.y = lpFindInfo->pt.y - Origin.y;
4210 switch(lpFindInfo->vkDirection)
4212 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4213 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4214 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4215 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4216 case VK_HOME: Destination.x = Destination.y = 0; break;
4217 case VK_END: Destination.x = infoPtr->rcView.right; Destination.y = infoPtr->rcView.bottom; break;
4218 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4219 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4220 default: FIXME("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4222 bNearest = TRUE;
4225 /* if LVFI_PARAM is specified, all other flags are ignored */
4226 if (lpFindInfo->flags & LVFI_PARAM)
4228 lvItem.mask |= LVIF_PARAM;
4229 bNearest = FALSE;
4230 lvItem.mask &= ~LVIF_TEXT;
4233 again:
4234 for (; nItem < nLast; nItem++)
4236 lvItem.iItem = nItem;
4237 lvItem.iSubItem = 0;
4238 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4240 if (lvItem.mask & LVIF_PARAM && lpFindInfo->lParam == lvItem.lParam)
4241 return nItem;
4243 if (lvItem.mask & LVIF_TEXT)
4245 if (lpFindInfo->flags & LVFI_PARTIAL)
4247 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4249 else
4251 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4255 if (!bNearest) return nItem;
4257 /* This is very inefficient. To do a good job here,
4258 * we need a sorted array of (x,y) item positions */
4259 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) continue;
4261 /* compute the distance^2 to the destination */
4262 xdist = Destination.x - Position.x;
4263 ydist = Destination.y - Position.y;
4264 dist = xdist * xdist + ydist * ydist;
4266 /* remember the distance, and item if it's closer */
4267 if (dist < mindist)
4269 mindist = dist;
4270 nNearestItem = nItem;
4274 if (bWrap)
4276 nItem = 0;
4277 nLast = min(nStart + 1, infoPtr->nItemCount);
4278 bWrap = FALSE;
4279 goto again;
4282 return nNearestItem;
4285 /***
4286 * DESCRIPTION:
4287 * Searches for an item with specific characteristics.
4289 * PARAMETER(S):
4290 * [I] hwnd : window handle
4291 * [I] nStart : base item index
4292 * [I] lpFindInfo : item information to look for
4294 * RETURN:
4295 * SUCCESS : index of item
4296 * FAILURE : -1
4298 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4299 LPLVFINDINFOA lpFindInfo)
4301 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4302 LVFINDINFOW fiw;
4303 LRESULT res;
4305 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4306 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4307 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4308 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4309 return res;
4312 /***
4313 * DESCRIPTION:
4314 * Retrieves the background image of the listview control.
4316 * PARAMETER(S):
4317 * [I] infoPtr : valid pointer to the listview structure
4318 * [O] LPLVMKBIMAGE : background image attributes
4320 * RETURN:
4321 * SUCCESS : TRUE
4322 * FAILURE : FALSE
4324 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4325 /* { */
4326 /* FIXME (listview, "empty stub!\n"); */
4327 /* return FALSE; */
4328 /* } */
4330 /***
4331 * DESCRIPTION:
4332 * Retrieves column attributes.
4334 * PARAMETER(S):
4335 * [I] infoPtr : valid pointer to the listview structure
4336 * [I] INT : column index
4337 * [IO] LPLVCOLUMNW : column information
4338 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4339 * otherwise it is in fact a LPLVCOLUMNA
4341 * RETURN:
4342 * SUCCESS : TRUE
4343 * FAILURE : FALSE
4345 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4347 HDITEMW hdi;
4348 BOOL bResult = FALSE;
4350 if (lpColumn != NULL)
4353 /* initialize memory */
4354 ZeroMemory(&hdi, sizeof(hdi));
4356 if (lpColumn->mask & LVCF_FMT)
4357 hdi.mask |= HDI_FORMAT;
4359 if (lpColumn->mask & LVCF_WIDTH)
4360 hdi.mask |= HDI_WIDTH;
4362 if (lpColumn->mask & LVCF_TEXT)
4364 hdi.mask |= HDI_TEXT;
4365 hdi.cchTextMax = lpColumn->cchTextMax;
4366 hdi.pszText = lpColumn->pszText;
4369 if (lpColumn->mask & LVCF_IMAGE)
4370 hdi.mask |= HDI_IMAGE;
4372 if (lpColumn->mask & LVCF_ORDER)
4373 hdi.mask |= HDI_ORDER;
4375 if (isW)
4376 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4377 else
4378 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4380 if (bResult)
4382 if (lpColumn->mask & LVCF_FMT)
4384 lpColumn->fmt = 0;
4386 if (hdi.fmt & HDF_LEFT)
4387 lpColumn->fmt |= LVCFMT_LEFT;
4388 else if (hdi.fmt & HDF_RIGHT)
4389 lpColumn->fmt |= LVCFMT_RIGHT;
4390 else if (hdi.fmt & HDF_CENTER)
4391 lpColumn->fmt |= LVCFMT_CENTER;
4393 if (hdi.fmt & HDF_IMAGE)
4394 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4396 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4397 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4400 if (lpColumn->mask & LVCF_WIDTH)
4401 lpColumn->cx = hdi.cxy;
4403 if (lpColumn->mask & LVCF_IMAGE)
4404 lpColumn->iImage = hdi.iImage;
4406 if (lpColumn->mask & LVCF_ORDER)
4407 lpColumn->iOrder = hdi.iOrder;
4409 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4410 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4415 return bResult;
4419 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4421 INT i;
4423 if (!lpiArray)
4424 return FALSE;
4426 /* FIXME: little hack */
4427 for (i = 0; i < iCount; i++)
4428 lpiArray[i] = i;
4430 return TRUE;
4433 /***
4434 * DESCRIPTION:
4435 * Retrieves the column width.
4437 * PARAMETER(S):
4438 * [I] infoPtr : valid pointer to the listview structure
4439 * [I] int : column index
4441 * RETURN:
4442 * SUCCESS : column width
4443 * FAILURE : zero
4445 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4447 INT nColumnWidth = 0;
4448 HDITEMW hdi;
4450 TRACE("nColumn=%d\n", nColumn);
4452 /* we have a 'column' in LIST and REPORT mode only */
4453 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4455 case LVS_LIST:
4456 nColumnWidth = infoPtr->nItemWidth;
4457 break;
4458 case LVS_REPORT:
4459 hdi.mask = HDI_WIDTH;
4460 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4461 nColumnWidth = hdi.cxy;
4462 break;
4465 TRACE("nColumnWidth=%d\n", nColumnWidth);
4466 return nColumnWidth;
4469 /***
4470 * DESCRIPTION:
4471 * In list or report display mode, retrieves the number of items that can fit
4472 * vertically in the visible area. In icon or small icon display mode,
4473 * retrieves the total number of visible items.
4475 * PARAMETER(S):
4476 * [I] infoPtr : valid pointer to the listview structure
4478 * RETURN:
4479 * Number of fully visible items.
4481 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4483 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4484 INT nItemCount = 0;
4486 if (uView == LVS_LIST)
4488 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4490 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4491 LISTVIEW_GetCountPerColumn(infoPtr);
4494 else if (uView == LVS_REPORT)
4496 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4498 else
4500 nItemCount = infoPtr->nItemCount;
4503 return nItemCount;
4507 /***
4508 * DESCRIPTION:
4509 * Retrieves an image list handle.
4511 * PARAMETER(S):
4512 * [I] infoPtr : valid pointer to the listview structure
4513 * [I] nImageList : image list identifier
4515 * RETURN:
4516 * SUCCESS : image list handle
4517 * FAILURE : NULL
4519 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4521 HIMAGELIST himl = NULL;
4523 switch (nImageList)
4525 case LVSIL_NORMAL:
4526 himl = infoPtr->himlNormal;
4527 break;
4528 case LVSIL_SMALL:
4529 himl = infoPtr->himlSmall;
4530 break;
4531 case LVSIL_STATE:
4532 himl = infoPtr->himlState;
4533 break;
4536 return (LRESULT)himl;
4539 /* LISTVIEW_GetISearchString */
4541 /***
4542 * DESCRIPTION:
4543 * Retrieves item attributes.
4545 * PARAMETER(S):
4546 * [I] hwnd : window handle
4547 * [IO] lpLVItem : item info
4548 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4549 * if FALSE, the lpLVItem is a LPLVITEMA.
4551 * NOTE:
4552 * This is the internal 'GetItem' interface -- it tries to
4553 * be smart, and avoids text copies, if possible, by modifing
4554 * lpLVItem->pszText to point to the text string. Please note
4555 * that this is not always possible (e.g. OWNERDATA), so on
4556 * entry you *must* supply valid values for pszText, and cchTextMax.
4557 * The only difference to the documented interface is that upon
4558 * return, you should use *only* the lpLVItem->pszText, rather than
4559 * the buffer pointer you provided on input. Most code already does
4560 * that, so it's not a problem.
4561 * For the two cases when the text must be copied (that is,
4562 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4564 * RETURN:
4565 * SUCCESS : TRUE
4566 * FAILURE : FALSE
4568 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4570 NMLVDISPINFOW dispInfo;
4571 LISTVIEW_ITEM *lpItem;
4572 ITEMHDR* pItemHdr;
4573 HDPA hdpaSubItems;
4575 /* In the following:
4576 * lpLVItem describes the information requested by the user
4577 * lpItem is what we have
4578 * dispInfo is a structure we use to request the missing
4579 * information from the application
4582 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4584 if (!lpLVItem || (lpLVItem->iItem < 0) ||
4585 (lpLVItem->iItem >= infoPtr->nItemCount))
4586 return FALSE;
4588 /* a quick optimization if all we're asked is the focus state
4589 * these queries are worth optimising since they are common,
4590 * and can be answered in constant time, without the heavy accesses */
4591 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIF_STATE) &&
4592 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4594 lpLVItem->state = 0;
4595 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4596 lpLVItem->state |= LVIS_FOCUSED;
4597 return TRUE;
4600 ZeroMemory(&dispInfo, sizeof(dispInfo));
4602 /* if the app stores all the data, handle it separately */
4603 if (infoPtr->dwStyle & LVS_OWNERDATA)
4605 dispInfo.item.state = 0;
4607 /* if we need to callback, do it now */
4608 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4610 /* NOTE: copy only fields which we _know_ are initialized, some apps
4611 * depend on the uninitialized fields being 0 */
4612 dispInfo.item.mask = lpLVItem->mask;
4613 dispInfo.item.iItem = lpLVItem->iItem;
4614 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4615 if (lpLVItem->mask & LVIF_TEXT)
4617 dispInfo.item.pszText = lpLVItem->pszText;
4618 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4620 if (lpLVItem->mask & LVIF_STATE)
4621 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4622 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4623 dispInfo.item.stateMask = lpLVItem->stateMask;
4624 *lpLVItem = dispInfo.item;
4625 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4628 /* we store only a little state, so if we're not asked, we're done */
4629 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4631 /* if focus is handled by us, report it */
4632 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4634 lpLVItem->state &= ~LVIS_FOCUSED;
4635 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4636 lpLVItem->state |= LVIS_FOCUSED;
4639 /* and do the same for selection, if we handle it */
4640 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4642 lpLVItem->state &= ~LVIS_SELECTED;
4643 if (ranges_contain(infoPtr->hdpaSelectionRanges, lpLVItem->iItem))
4644 lpLVItem->state |= LVIS_SELECTED;
4647 return TRUE;
4650 /* find the item and subitem structures before we proceed */
4651 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4652 if (hdpaSubItems == NULL) return FALSE;
4654 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4655 return FALSE;
4657 if (lpLVItem->iSubItem)
4659 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4660 if(!lpSubItem) return FALSE;
4661 pItemHdr = &lpSubItem->hdr;
4663 else
4664 pItemHdr = &lpItem->hdr;
4666 /* Do we need to query the state from the app? */
4667 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4669 dispInfo.item.mask |= LVIF_STATE;
4670 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4673 /* Do we need to enquire about the image? */
4674 if ((lpLVItem->mask & LVIF_IMAGE) && (pItemHdr->iImage==I_IMAGECALLBACK))
4675 dispInfo.item.mask |= LVIF_IMAGE;
4677 /* Do we need to enquire about the text? */
4678 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4680 dispInfo.item.mask |= LVIF_TEXT;
4681 dispInfo.item.pszText = lpLVItem->pszText;
4682 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4683 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4684 *dispInfo.item.pszText = '\0';
4687 /* If we don't have all the requested info, query the application */
4688 if (dispInfo.item.mask != 0)
4690 dispInfo.item.iItem = lpLVItem->iItem;
4691 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4692 dispInfo.item.lParam = lpItem->lParam;
4693 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4694 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4697 /* Now, handle the iImage field */
4698 if (dispInfo.item.mask & LVIF_IMAGE)
4700 lpLVItem->iImage = dispInfo.item.iImage;
4701 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (pItemHdr->iImage==I_IMAGECALLBACK))
4702 pItemHdr->iImage = dispInfo.item.iImage;
4704 else if (lpLVItem->mask & LVIF_IMAGE)
4705 lpLVItem->iImage = pItemHdr->iImage;
4707 /* The pszText field */
4708 if (dispInfo.item.mask & LVIF_TEXT)
4710 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4711 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4713 lpLVItem->pszText = dispInfo.item.pszText;
4715 else if (lpLVItem->mask & LVIF_TEXT)
4717 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4718 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4721 /* if this is a subitem, we're done*/
4722 if (lpLVItem->iSubItem) return TRUE;
4724 /* Next is the lParam field */
4725 if (dispInfo.item.mask & LVIF_PARAM)
4727 lpLVItem->lParam = dispInfo.item.lParam;
4728 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4729 lpItem->lParam = dispInfo.item.lParam;
4731 else if (lpLVItem->mask & LVIF_PARAM)
4732 lpLVItem->lParam = lpItem->lParam;
4734 /* ... the state field (this one is different due to uCallbackmask) */
4735 if (lpLVItem->mask & LVIF_STATE)
4737 lpLVItem->state = lpItem->state;
4738 if (dispInfo.item.mask & LVIF_STATE)
4740 lpLVItem->state &= ~dispInfo.item.stateMask;
4741 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4743 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4745 lpLVItem->state &= ~LVIS_FOCUSED;
4746 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4747 lpLVItem->state |= LVIS_FOCUSED;
4749 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4751 lpLVItem->state &= ~LVIS_SELECTED;
4752 if (ranges_contain(infoPtr->hdpaSelectionRanges, lpLVItem->iItem))
4753 lpLVItem->state |= LVIS_SELECTED;
4757 /* and last, but not least, the indent field */
4758 if (lpLVItem->mask & LVIF_INDENT)
4759 lpLVItem->iIndent = lpItem->iIndent;
4761 return TRUE;
4764 /***
4765 * DESCRIPTION:
4766 * Retrieves item attributes.
4768 * PARAMETER(S):
4769 * [I] hwnd : window handle
4770 * [IO] lpLVItem : item info
4771 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4772 * if FALSE, the lpLVItem is a LPLVITEMA.
4774 * NOTE:
4775 * This is the external 'GetItem' interface -- it properly copies
4776 * the text in the provided buffer.
4778 * RETURN:
4779 * SUCCESS : TRUE
4780 * FAILURE : FALSE
4782 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4784 LPWSTR pszText;
4785 BOOL bResult;
4787 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4788 return FALSE;
4790 pszText = lpLVItem->pszText;
4791 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4792 if (bResult && lpLVItem->pszText != pszText)
4793 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4794 lpLVItem->pszText = pszText;
4796 return bResult;
4800 /***
4801 * DESCRIPTION:
4802 * Retrieves the position (upper-left) of the listview control item.
4803 * Note that for LVS_ICON style, the upper-left is that of the icon
4804 * and not the bounding box.
4806 * PARAMETER(S):
4807 * [I] infoPtr : valid pointer to the listview structure
4808 * [I] nItem : item index
4809 * [O] lpptPosition : coordinate information
4811 * RETURN:
4812 * SUCCESS : TRUE
4813 * FAILURE : FALSE
4815 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4817 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4818 POINT Origin;
4820 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4822 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4823 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition)) return FALSE;
4824 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
4826 if (uView == LVS_ICON)
4828 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
4829 lpptPosition->y += ICON_TOP_PADDING;
4831 lpptPosition->x += Origin.x;
4832 lpptPosition->y += Origin.y;
4834 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
4835 return TRUE;
4839 /***
4840 * DESCRIPTION:
4841 * Retrieves the bounding rectangle for a listview control item.
4843 * PARAMETER(S):
4844 * [I] infoPtr : valid pointer to the listview structure
4845 * [I] nItem : item index
4846 * [IO] lprc : bounding rectangle coordinates
4847 * lprc->left specifies the portion of the item for which the bounding
4848 * rectangle will be retrieved.
4850 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
4851 * including the icon and label.
4853 * * For LVS_ICON
4854 * * Experiment shows that native control returns:
4855 * * width = min (48, length of text line)
4856 * * .left = position.x - (width - iconsize.cx)/2
4857 * * .right = .left + width
4858 * * height = #lines of text * ntmHeight + icon height + 8
4859 * * .top = position.y - 2
4860 * * .bottom = .top + height
4861 * * separation between items .y = itemSpacing.cy - height
4862 * * .x = itemSpacing.cx - width
4863 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
4865 * * For LVS_ICON
4866 * * Experiment shows that native control returns:
4867 * * width = iconSize.cx + 16
4868 * * .left = position.x - (width - iconsize.cx)/2
4869 * * .right = .left + width
4870 * * height = iconSize.cy + 4
4871 * * .top = position.y - 2
4872 * * .bottom = .top + height
4873 * * separation between items .y = itemSpacing.cy - height
4874 * * .x = itemSpacing.cx - width
4875 * LVIR_LABEL Returns the bounding rectangle of the item text.
4877 * * For LVS_ICON
4878 * * Experiment shows that native control returns:
4879 * * width = text length
4880 * * .left = position.x - width/2
4881 * * .right = .left + width
4882 * * height = ntmH * linecount + 2
4883 * * .top = position.y + iconSize.cy + 6
4884 * * .bottom = .top + height
4885 * * separation between items .y = itemSpacing.cy - height
4886 * * .x = itemSpacing.cx - width
4887 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
4888 * rectangles, but excludes columns in report view.
4890 * RETURN:
4891 * SUCCESS : TRUE
4892 * FAILURE : FALSE
4894 * NOTES
4895 * Note that the bounding rectangle of the label in the LVS_ICON view depends
4896 * upon whether the window has the focus currently and on whether the item
4897 * is the one with the focus. Ensure that the control's record of which
4898 * item has the focus agrees with the items' records.
4900 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
4902 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4903 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4904 BOOL doLabel = TRUE, oversizedBox = FALSE;
4905 POINT Position, Origin;
4906 LVITEMW lvItem;
4907 RECT label_rect;
4909 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
4911 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4912 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
4913 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) return FALSE;
4915 /* Be smart and try to figure out the minimum we have to do */
4916 if (lprc->left == LVIR_ICON) doLabel = FALSE;
4917 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
4918 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
4919 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
4920 oversizedBox = TRUE;
4922 /* get what we need from the item before hand, so we make
4923 * only one request. This can speed up things, if data
4924 * is stored on the app side */
4925 lvItem.mask = 0;
4926 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
4927 if (doLabel) lvItem.mask |= LVIF_TEXT;
4928 lvItem.iItem = nItem;
4929 lvItem.iSubItem = 0;
4930 lvItem.pszText = szDispText;
4931 lvItem.cchTextMax = DISP_TEXT_SIZE;
4932 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
4933 /* we got the state already up, simulate it here, to avoid a reget */
4934 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
4936 lvItem.mask |= LVIF_STATE;
4937 lvItem.stateMask = LVIS_FOCUSED;
4938 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
4941 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
4942 lprc->left = LVIR_BOUNDS;
4943 switch(lprc->left)
4945 case LVIR_ICON:
4946 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL)) return FALSE;
4947 break;
4949 case LVIR_LABEL:
4950 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc)) return FALSE;
4951 break;
4953 case LVIR_BOUNDS:
4954 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL)) return FALSE;
4955 break;
4957 case LVIR_SELECTBOUNDS:
4958 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect)) return FALSE;
4959 UnionRect(lprc, lprc, &label_rect);
4960 break;
4962 default:
4963 WARN("Unknown value: %d\n", lprc->left);
4964 return FALSE;
4967 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
4969 TRACE(" rect=%s\n", debugrect(lprc));
4971 return TRUE;
4974 /***
4975 * DESCRIPTION:
4976 * Retrieves the spacing between listview control items.
4978 * PARAMETER(S):
4979 * [I] infoPtr : valid pointer to the listview structure
4980 * [IO] lprc : rectangle to receive the output
4981 * on input, lprc->top = nSubItem
4982 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
4984 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
4985 * not only those of the first column.
4986 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
4988 * RETURN:
4989 * TRUE: success
4990 * FALSE: failure
4992 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
4994 POINT Position, Origin;
4995 LVITEMW lvItem;
4997 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
4999 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5001 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
5002 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5004 lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5005 lvItem.iItem = nItem;
5006 lvItem.iSubItem = lprc->top;
5008 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5009 switch(lprc->left)
5011 case LVIR_ICON:
5012 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL)) return FALSE;
5013 break;
5015 case LVIR_LABEL:
5016 case LVIR_BOUNDS:
5017 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL)) return FALSE;
5018 break;
5020 default:
5021 ERR("Unknown bounds=%d\n", lprc->left);
5022 return FALSE;
5025 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5026 return TRUE;
5030 /***
5031 * DESCRIPTION:
5032 * Retrieves the width of a label.
5034 * PARAMETER(S):
5035 * [I] infoPtr : valid pointer to the listview structure
5037 * RETURN:
5038 * SUCCESS : string width (in pixels)
5039 * FAILURE : zero
5041 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5043 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5044 LVITEMW lvItem;
5046 TRACE("(nItem=%d)\n", nItem);
5048 lvItem.mask = LVIF_TEXT;
5049 lvItem.iItem = nItem;
5050 lvItem.iSubItem = 0;
5051 lvItem.pszText = szDispText;
5052 lvItem.cchTextMax = DISP_TEXT_SIZE;
5053 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5055 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5058 /***
5059 * DESCRIPTION:
5060 * Retrieves the spacing between listview control items.
5062 * PARAMETER(S):
5063 * [I] infoPtr : valid pointer to the listview structure
5064 * [I] BOOL : flag for small or large icon
5066 * RETURN:
5067 * Horizontal + vertical spacing
5069 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5071 LONG lResult;
5073 if (!bSmall)
5075 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5077 else
5079 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5080 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5081 else
5082 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5084 return lResult;
5087 /***
5088 * DESCRIPTION:
5089 * Retrieves the state of a listview control item.
5091 * PARAMETER(S):
5092 * [I] infoPtr : valid pointer to the listview structure
5093 * [I] nItem : item index
5094 * [I] uMask : state mask
5096 * RETURN:
5097 * State specified by the mask.
5099 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5101 LVITEMW lvItem;
5103 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5105 lvItem.iItem = nItem;
5106 lvItem.iSubItem = 0;
5107 lvItem.mask = LVIF_STATE;
5108 lvItem.stateMask = uMask;
5109 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5111 return lvItem.state & uMask;
5114 /***
5115 * DESCRIPTION:
5116 * Retrieves the text of a listview control item or subitem.
5118 * PARAMETER(S):
5119 * [I] hwnd : window handle
5120 * [I] nItem : item index
5121 * [IO] lpLVItem : item information
5122 * [I] isW : TRUE if lpLVItem is Unicode
5124 * RETURN:
5125 * SUCCESS : string length
5126 * FAILURE : 0
5128 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5130 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5132 lpLVItem->mask = LVIF_TEXT;
5133 lpLVItem->iItem = nItem;
5134 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5136 return textlenT(lpLVItem->pszText, isW);
5139 /***
5140 * DESCRIPTION:
5141 * Searches for an item based on properties + relationships.
5143 * PARAMETER(S):
5144 * [I] infoPtr : valid pointer to the listview structure
5145 * [I] nItem : item index
5146 * [I] uFlags : relationship flag
5148 * FIXME:
5149 * This function is very, very inefficient! Needs work.
5151 * RETURN:
5152 * SUCCESS : item index
5153 * FAILURE : -1
5155 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5157 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5158 UINT uMask = 0;
5159 LVFINDINFOW lvFindInfo;
5160 INT nCountPerColumn;
5161 INT i;
5163 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5164 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5166 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5168 if (uFlags & LVNI_CUT)
5169 uMask |= LVIS_CUT;
5171 if (uFlags & LVNI_DROPHILITED)
5172 uMask |= LVIS_DROPHILITED;
5174 if (uFlags & LVNI_FOCUSED)
5175 uMask |= LVIS_FOCUSED;
5177 if (uFlags & LVNI_SELECTED)
5178 uMask |= LVIS_SELECTED;
5180 /* if we're asked for the focused item, that's only one,
5181 * so it's worth optimizing */
5182 if (uFlags & LVNI_FOCUSED)
5184 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5185 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5188 if (uFlags & LVNI_ABOVE)
5190 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5192 while (nItem >= 0)
5194 nItem--;
5195 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5196 return nItem;
5199 else
5201 lvFindInfo.flags = LVFI_NEARESTXY;
5202 lvFindInfo.vkDirection = VK_UP;
5203 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5204 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5206 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5207 return nItem;
5211 else if (uFlags & LVNI_BELOW)
5213 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5215 while (nItem < infoPtr->nItemCount)
5217 nItem++;
5218 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5219 return nItem;
5222 else
5224 lvFindInfo.flags = LVFI_NEARESTXY;
5225 lvFindInfo.vkDirection = VK_DOWN;
5226 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5227 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5229 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5230 return nItem;
5234 else if (uFlags & LVNI_TOLEFT)
5236 if (uView == LVS_LIST)
5238 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5239 while (nItem - nCountPerColumn >= 0)
5241 nItem -= nCountPerColumn;
5242 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5243 return nItem;
5246 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5248 lvFindInfo.flags = LVFI_NEARESTXY;
5249 lvFindInfo.vkDirection = VK_LEFT;
5250 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5251 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5253 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5254 return nItem;
5258 else if (uFlags & LVNI_TORIGHT)
5260 if (uView == LVS_LIST)
5262 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5263 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5265 nItem += nCountPerColumn;
5266 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5267 return nItem;
5270 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5272 lvFindInfo.flags = LVFI_NEARESTXY;
5273 lvFindInfo.vkDirection = VK_RIGHT;
5274 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5275 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5277 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5278 return nItem;
5282 else
5284 nItem++;
5286 /* search by index */
5287 for (i = nItem; i < infoPtr->nItemCount; i++)
5289 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5290 return i;
5294 return -1;
5297 /* LISTVIEW_GetNumberOfWorkAreas */
5299 /***
5300 * DESCRIPTION:
5301 * Retrieves the origin coordinates when in icon or small icon display mode.
5303 * PARAMETER(S):
5304 * [I] infoPtr : valid pointer to the listview structure
5305 * [O] lpptOrigin : coordinate information
5307 * RETURN:
5308 * SUCCESS : TRUE
5309 * FAILURE : FALSE
5311 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5313 DWORD lStyle = infoPtr->dwStyle;
5314 UINT uView = lStyle & LVS_TYPEMASK;
5315 INT nHorzPos = 0, nVertPos = 0;
5316 SCROLLINFO scrollInfo;
5318 if (!lpptOrigin) return FALSE;
5320 scrollInfo.cbSize = sizeof(SCROLLINFO);
5321 scrollInfo.fMask = SIF_POS;
5323 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5324 nHorzPos = scrollInfo.nPos;
5325 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5326 nVertPos = scrollInfo.nPos;
5328 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5330 lpptOrigin->x = infoPtr->rcList.left;
5331 lpptOrigin->y = infoPtr->rcList.top;
5332 if (uView == LVS_LIST)
5333 nHorzPos *= infoPtr->nItemWidth;
5334 else if (uView == LVS_REPORT)
5335 nVertPos *= infoPtr->nItemHeight;
5337 lpptOrigin->x -= nHorzPos;
5338 lpptOrigin->y -= nVertPos;
5340 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5342 return TRUE;
5345 /***
5346 * DESCRIPTION:
5347 * Retrieves the width of a string.
5349 * PARAMETER(S):
5350 * [I] hwnd : window handle
5351 * [I] lpszText : text string to process
5352 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5354 * RETURN:
5355 * SUCCESS : string width (in pixels)
5356 * FAILURE : zero
5358 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5360 SIZE stringSize;
5362 stringSize.cx = 0;
5363 if (is_textT(lpszText, isW))
5365 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5366 HDC hdc = GetDC(infoPtr->hwndSelf);
5367 HFONT hOldFont = SelectObject(hdc, hFont);
5369 if (isW)
5370 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5371 else
5372 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5373 SelectObject(hdc, hOldFont);
5374 ReleaseDC(infoPtr->hwndSelf, hdc);
5376 return stringSize.cx;
5379 /***
5380 * DESCRIPTION:
5381 * Determines which listview item is located at the specified position.
5383 * PARAMETER(S):
5384 * [I] infoPtr : valid pointer to the listview structure
5385 * [IO] lpht : hit test information
5386 * [I] subitem : fill out iSubItem.
5387 * [I] select : return the index only if the hit selects the item
5389 * NOTE:
5390 * (mm 20001022): We must not allow iSubItem to be touched, for
5391 * an app might pass only a structure with space up to iItem!
5392 * (MS Office 97 does that for instance in the file open dialog)
5394 * RETURN:
5395 * SUCCESS : item index
5396 * FAILURE : -1
5398 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5400 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5401 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5402 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5403 POINT Origin, Position, opt;
5404 LVITEMW lvItem;
5405 ITERATOR i;
5407 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5409 lpht->flags = 0;
5410 lpht->iItem = -1;
5411 if (subitem) lpht->iSubItem = 0;
5413 if (infoPtr->rcList.left > lpht->pt.x)
5414 lpht->flags |= LVHT_TOLEFT;
5415 else if (infoPtr->rcList.right < lpht->pt.x)
5416 lpht->flags |= LVHT_TORIGHT;
5418 if (infoPtr->rcList.top > lpht->pt.y)
5419 lpht->flags |= LVHT_ABOVE;
5420 else if (infoPtr->rcList.bottom < lpht->pt.y)
5421 lpht->flags |= LVHT_BELOW;
5423 TRACE("lpht->flags=0x%x\n", lpht->flags);
5424 if (lpht->flags) return -1;
5426 lpht->flags |= LVHT_NOWHERE;
5428 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
5430 /* first deal with the large items */
5431 rcSearch.left = lpht->pt.x;
5432 rcSearch.top = lpht->pt.y;
5433 rcSearch.right = rcSearch.left + 1;
5434 rcSearch.bottom = rcSearch.top + 1;
5436 iterator_frameditems(&i, infoPtr, &rcSearch);
5437 iterator_next(&i); /* go to first item in the sequence */
5438 lpht->iItem = i.nItem;
5439 iterator_destroy(&i);
5441 TRACE("lpht->iItem=%d\n", lpht->iItem);
5442 if (lpht->iItem == -1) return -1;
5444 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5445 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5446 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5447 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5448 lvItem.iItem = lpht->iItem;
5449 lvItem.iSubItem = 0;
5450 lvItem.pszText = szDispText;
5451 lvItem.cchTextMax = DISP_TEXT_SIZE;
5452 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5453 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5455 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel)) return -1;
5456 if (!LISTVIEW_GetItemOrigin(infoPtr, lpht->iItem, &Position)) return -1;
5457 opt.x = lpht->pt.x - Position.x - Origin.x;
5458 opt.y = lpht->pt.y - Position.y - Origin.y;
5460 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
5461 rcBounds = rcBox;
5462 else
5463 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5464 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5465 if (!PtInRect(&rcBounds, opt)) return -1;
5467 if (PtInRect(&rcIcon, opt))
5468 lpht->flags |= LVHT_ONITEMICON;
5469 else if (PtInRect(&rcLabel, opt))
5470 lpht->flags |= LVHT_ONITEMLABEL;
5471 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5472 lpht->flags |= LVHT_ONITEMSTATEICON;
5473 if (lpht->flags & LVHT_ONITEM)
5474 lpht->flags &= ~LVHT_NOWHERE;
5476 TRACE("lpht->flags=0x%x\n", lpht->flags);
5477 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5479 INT j, nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5480 rcBounds.right = rcBounds.left;
5481 for (j = 0; j < nColumnCount; j++)
5483 rcBounds.left = rcBounds.right;
5484 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5485 if (PtInRect(&rcBounds, opt))
5487 lpht->iSubItem = j;
5488 break;
5493 if (!select || lpht->iItem == -1) return lpht->iItem;
5495 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5497 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5498 return PtInRect(&rcBounds, opt) ? lpht->iItem : -1;
5502 /***
5503 * DESCRIPTION:
5504 * Inserts a new column.
5506 * PARAMETER(S):
5507 * [I] infoPtr : valid pointer to the listview structure
5508 * [I] INT : column index
5509 * [I] LPLVCOLUMNW : column information
5511 * RETURN:
5512 * SUCCESS : new column index
5513 * FAILURE : -1
5515 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5516 LPLVCOLUMNW lpColumn, BOOL isW)
5518 RECT rcOld, rcCol;
5519 INT nNewColumn;
5520 HDITEMW hdi;
5522 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5524 if (!lpColumn) return -1;
5526 hdi.mask = hdi.fmt = 0;
5527 if (lpColumn->mask & LVCF_FMT)
5529 /* format member is valid */
5530 hdi.mask |= HDI_FORMAT;
5532 /* set text alignment (leftmost column must be left-aligned) */
5533 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5534 hdi.fmt |= HDF_LEFT;
5535 else if (lpColumn->fmt & LVCFMT_RIGHT)
5536 hdi.fmt |= HDF_RIGHT;
5537 else if (lpColumn->fmt & LVCFMT_CENTER)
5538 hdi.fmt |= HDF_CENTER;
5540 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5541 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5543 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5545 hdi.fmt |= HDF_IMAGE;
5546 hdi.iImage = I_IMAGECALLBACK;
5549 if (lpColumn->fmt & LVCFMT_IMAGE)
5550 ; /* FIXME: enable images for *(sub)items* this column */
5553 if (lpColumn->mask & LVCF_WIDTH)
5555 hdi.mask |= HDI_WIDTH;
5556 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5558 /* make it fill the remainder of the controls width */
5559 HDITEMW hdit;
5560 RECT rcHeader;
5561 INT item_index;
5563 /* get the width of every item except the current one */
5564 hdit.mask = HDI_WIDTH;
5565 hdi.cxy = 0;
5567 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5568 if (Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit)))
5569 hdi.cxy += hdit.cxy;
5571 /* retrieve the layout of the header */
5572 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5573 TRACE("start cxy=%d rcHeader=%s\n", hdi.cxy, debugrect(&rcHeader));
5575 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
5577 else
5578 hdi.cxy = lpColumn->cx;
5581 if (lpColumn->mask & LVCF_TEXT)
5583 hdi.mask |= HDI_TEXT | HDI_FORMAT;
5584 hdi.fmt |= HDF_STRING;
5585 hdi.pszText = lpColumn->pszText;
5586 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
5589 if (lpColumn->mask & LVCF_IMAGE)
5591 hdi.mask |= HDI_IMAGE;
5592 hdi.iImage = lpColumn->iImage;
5595 if (lpColumn->mask & LVCF_ORDER)
5597 hdi.mask |= HDI_ORDER;
5598 hdi.iOrder = lpColumn->iOrder;
5601 /* insert item in header control */
5602 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5603 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5604 (WPARAM)nColumn, (LPARAM)&hdi);
5605 if (nNewColumn == -1) return -1;
5606 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &rcCol)) return -1;
5608 /* now we have to actually adjust the data */
5609 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
5611 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
5612 HDPA hdpaSubItems;
5613 INT nItem, i;
5615 /* preallocate memory, so we can fail gracefully */
5616 if (nNewColumn == 0)
5618 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->nItemCount);
5619 if (!lpNewItems) return -1;
5620 for (i = 0; i < infoPtr->nItemCount; i++)
5621 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
5622 if (i != infoPtr->nItemCount)
5624 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5625 COMCTL32_Free(lpNewItems);
5626 return -1;
5630 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5632 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5633 if (!hdpaSubItems) continue;
5634 for (i = 1; i < hdpaSubItems->nItemCount; i++)
5636 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
5637 if (!lpSubItem) break;
5638 if (lpSubItem->iSubItem >= nNewColumn)
5639 lpSubItem->iSubItem++;
5642 /* if we found our subitem, zapp it */
5643 if (nNewColumn == 0)
5645 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
5646 lpSubItem = lpNewItems[nItem];
5647 lpSubItem->hdr = lpMainItem->hdr;
5648 lpSubItem->iSubItem = 1;
5649 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
5650 lpMainItem->iSubItem = 0;
5651 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
5655 COMCTL32_Free(lpNewItems);
5658 /* we don't have to worry abiut display issues in non-report mode */
5659 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return nNewColumn;
5661 /* if we have a focus, must first erase the focus rect */
5662 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
5664 /* Need to reset the item width when inserting a new column */
5665 infoPtr->nItemWidth += rcCol.right - rcCol.left;
5667 LISTVIEW_UpdateScroll(infoPtr);
5669 /* scroll to cover the deleted column, and invalidate for redraw */
5670 rcOld = infoPtr->rcList;
5671 rcOld.left = rcCol.left;
5672 ScrollWindowEx(infoPtr->hwndSelf, rcCol.right - rcCol.left, 0,
5673 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5675 /* we can restore focus now */
5676 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
5678 return nNewColumn;
5681 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5682 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5683 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5684 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5685 their own sort proc. when sending LVM_SORTITEMS.
5687 /* Platform SDK:
5688 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5690 LVS_SORTXXX must be specified,
5691 LVS_OWNERDRAW is not set,
5692 <item>.pszText is not LPSTR_TEXTCALLBACK.
5694 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5695 are sorted based on item text..."
5697 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5699 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5700 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5701 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5703 /* if we're sorting descending, negate the return value */
5704 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5707 /***
5708 * nESCRIPTION:
5709 * Inserts a new item in the listview control.
5711 * PARAMETER(S):
5712 * [I] infoPtr : valid pointer to the listview structure
5713 * [I] lpLVItem : item information
5714 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5716 * RETURN:
5717 * SUCCESS : new item index
5718 * FAILURE : -1
5720 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5722 LONG lStyle = infoPtr->dwStyle;
5723 UINT uView = lStyle & LVS_TYPEMASK;
5724 INT nItem = -1;
5725 HDPA hdpaSubItems;
5726 NMLISTVIEW nmlv;
5727 LISTVIEW_ITEM *lpItem;
5728 BOOL is_sorted;
5730 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5732 if (lStyle & LVS_OWNERDATA)
5734 nItem = infoPtr->nItemCount;
5735 infoPtr->nItemCount++;
5736 return nItem;
5739 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5740 if (!lpLVItem || lpLVItem->iSubItem) return -1;
5742 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5744 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5745 return -1;
5747 /* insert item in listview control data structure */
5748 if ( (hdpaSubItems = DPA_Create(8)) )
5749 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5750 if (nItem == -1) goto fail;
5752 /* FIXME: is the handling of this LVS_OWNERDRAWFIXED correct? */
5753 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5754 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5756 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
5757 is_sorted ? infoPtr->nItemCount + 1 : lpLVItem->iItem,
5758 hdpaSubItems );
5759 if (nItem == -1) goto fail;
5760 infoPtr->nItemCount++;
5762 if (!LISTVIEW_SetItemT(infoPtr, lpLVItem, isW))
5763 goto undo;
5765 /* if we're sorted, sort the list, and update the index */
5766 if (is_sorted)
5768 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5769 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5770 if (nItem == -1)
5772 ERR("We can't find the item we just inserted, possible memory corruption.");
5773 /* we can't remove it from the list if we can't find it, so just fail */
5774 /* we don't deallocate memory here, as it will probably cause more problems */
5775 return -1;
5779 /* make room for the position, if we are in the right mode */
5780 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5782 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5783 goto undo;
5784 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5786 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5787 goto undo;
5791 /* Add the subitem list to the items array. Do this last in case we go to
5792 * fail during the above.
5794 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5796 lpItem->valid = TRUE;
5798 /* send LVN_INSERTITEM notification */
5799 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5800 nmlv.iItem = nItem;
5801 nmlv.lParam = lpItem->lParam;
5802 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5804 /* align items (set position of each item) */
5805 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5807 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
5808 else LISTVIEW_AlignTop(infoPtr);
5811 LISTVIEW_UpdateScroll(infoPtr);
5813 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
5815 TRACE(" <- %d\n", nItem);
5816 return nItem;
5818 undo:
5819 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5820 infoPtr->nItemCount--;
5821 fail:
5822 DPA_DeletePtr(hdpaSubItems, 0);
5823 DPA_Destroy (hdpaSubItems);
5824 COMCTL32_Free (lpItem);
5825 return -1;
5828 /***
5829 * DESCRIPTION:
5830 * Redraws a range of items.
5832 * PARAMETER(S):
5833 * [I] infoPtr : valid pointer to the listview structure
5834 * [I] INT : first item
5835 * [I] INT : last item
5837 * RETURN:
5838 * SUCCESS : TRUE
5839 * FAILURE : FALSE
5841 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
5843 INT i;
5845 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
5846 max(nFirst, nLast) >= infoPtr->nItemCount)
5847 return FALSE;
5849 for (i = nFirst; i <= nLast; i++)
5850 LISTVIEW_InvalidateItem(infoPtr, i);
5852 return TRUE;
5855 /***
5856 * DESCRIPTION:
5857 * Scroll the content of a listview.
5859 * PARAMETER(S):
5860 * [I] infoPtr : valid pointer to the listview structure
5861 * [I] INT : horizontal scroll amount in pixels
5862 * [I] INT : vertical scroll amount in pixels
5864 * RETURN:
5865 * SUCCESS : TRUE
5866 * FAILURE : FALSE
5868 * COMMENTS:
5869 * If the control is in report mode (LVS_REPORT) the control can
5870 * be scrolled only in line increments. "dy" will be rounded to the
5871 * nearest number of pixels that are a whole line. Ex: if line height
5872 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
5873 * is passed the the scroll will be 0. (per MSDN 7/2002)
5875 * For: (per experimentaion with native control and CSpy ListView)
5876 * LVS_ICON dy=1 = 1 pixel (vertical only)
5877 * dx ignored
5878 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
5879 * dx ignored
5880 * LVS_LIST dx=1 = 1 column (horizontal only)
5881 * but will only scroll 1 column per message
5882 * no matter what the value.
5883 * dy must be 0 or FALSE returned.
5884 * LVS_REPORT dx=1 = 1 pixel
5885 * dy= see above
5888 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
5890 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
5891 case LVS_REPORT:
5892 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
5893 dy /= infoPtr->nItemHeight;
5894 break;
5895 case LVS_LIST:
5896 if (dy != 0) return FALSE;
5897 break;
5898 default: /* icon */
5899 dx = 0;
5900 break;
5903 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
5904 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
5906 return TRUE;
5909 /***
5910 * DESCRIPTION:
5911 * Sets the background color.
5913 * PARAMETER(S):
5914 * [I] infoPtr : valid pointer to the listview structure
5915 * [I] COLORREF : background color
5917 * RETURN:
5918 * SUCCESS : TRUE
5919 * FAILURE : FALSE
5921 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
5923 TRACE("(clrBk=%lx)\n", clrBk);
5925 if(infoPtr->clrBk != clrBk) {
5926 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
5927 infoPtr->clrBk = clrBk;
5928 if (clrBk == CLR_NONE)
5929 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
5930 else
5931 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
5932 LISTVIEW_InvalidateList(infoPtr);
5935 return TRUE;
5938 /* LISTVIEW_SetBkImage */
5940 /***
5941 * DESCRIPTION:
5942 * Sets the attributes of a header item.
5944 * PARAMETER(S):
5945 * [I] infoPtr : valid pointer to the listview structure
5946 * [I] INT : column index
5947 * [I] LPLVCOLUMNW : column attributes
5948 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
5949 * otherwise it is in fact a LPLVCOLUMNA
5951 * RETURN:
5952 * SUCCESS : TRUE
5953 * FAILURE : FALSE
5955 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5956 LPLVCOLUMNW lpColumn, BOOL isW)
5958 BOOL bResult = FALSE;
5959 HDITEMW hdi, hdiget;
5961 if ((lpColumn != NULL) && (nColumn >= 0) &&
5962 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
5964 /* initialize memory */
5965 ZeroMemory(&hdi, sizeof(hdi));
5967 if (lpColumn->mask & LVCF_FMT)
5969 /* format member is valid */
5970 hdi.mask |= HDI_FORMAT;
5972 /* get current format first */
5973 hdiget.mask = HDI_FORMAT;
5974 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
5975 /* preserve HDF_STRING if present */
5976 hdi.fmt = hdiget.fmt & HDF_STRING;
5978 /* set text alignment (leftmost column must be left-aligned) */
5979 if (nColumn == 0)
5981 hdi.fmt |= HDF_LEFT;
5983 else
5985 if (lpColumn->fmt & LVCFMT_LEFT)
5986 hdi.fmt |= HDF_LEFT;
5987 else if (lpColumn->fmt & LVCFMT_RIGHT)
5988 hdi.fmt |= HDF_RIGHT;
5989 else if (lpColumn->fmt & LVCFMT_CENTER)
5990 hdi.fmt |= HDF_CENTER;
5993 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5994 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5996 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5997 hdi.fmt |= HDF_IMAGE;
5999 if (lpColumn->fmt & LVCFMT_IMAGE)
6001 hdi.fmt |= HDF_IMAGE;
6002 hdi.iImage = I_IMAGECALLBACK;
6006 if (lpColumn->mask & LVCF_WIDTH)
6008 hdi.mask |= HDI_WIDTH;
6009 hdi.cxy = lpColumn->cx;
6012 if (lpColumn->mask & LVCF_TEXT)
6014 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6015 hdi.pszText = lpColumn->pszText;
6016 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6017 hdi.fmt |= HDF_STRING;
6020 if (lpColumn->mask & LVCF_IMAGE)
6022 hdi.mask |= HDI_IMAGE;
6023 hdi.iImage = lpColumn->iImage;
6026 if (lpColumn->mask & LVCF_ORDER)
6028 hdi.mask |= HDI_ORDER;
6029 hdi.iOrder = lpColumn->iOrder;
6032 /* set header item attributes */
6033 if (isW)
6034 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6035 else
6036 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6039 return bResult;
6042 /***
6043 * DESCRIPTION:
6044 * Sets the column order array
6046 * PARAMETERS:
6047 * [I] infoPtr : valid pointer to the listview structure
6048 * [I] INT : number of elements in column order array
6049 * [I] INT : pointer to column order array
6051 * RETURN:
6052 * SUCCESS : TRUE
6053 * FAILURE : FALSE
6055 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6057 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6059 if (!lpiArray)
6060 return FALSE;
6062 return TRUE;
6066 /***
6067 * DESCRIPTION:
6068 * Sets the width of a column
6070 * PARAMETERS:
6071 * [I] infoPtr : valid pointer to the listview structure
6072 * [I] INT : column index
6073 * [I] INT : column width
6075 * RETURN:
6076 * SUCCESS : TRUE
6077 * FAILURE : FALSE
6079 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6081 HDITEMW hdi;
6082 LRESULT lret;
6083 LONG lStyle = infoPtr->dwStyle;
6084 UINT uView = lStyle & LVS_TYPEMASK;
6085 HDC hdc;
6086 HFONT header_font;
6087 HFONT old_font;
6088 SIZE size;
6089 WCHAR text_buffer[DISP_TEXT_SIZE];
6090 INT header_item_count;
6091 INT item_index;
6092 INT nLabelWidth;
6093 RECT rcHeader;
6094 LVITEMW lvItem;
6095 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6097 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6099 /* set column width only if in report or list mode */
6100 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6102 /* take care of invalid cx values */
6103 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6104 else if (uView == LVS_LIST && cx < 1) return FALSE;
6106 /* resize all columns if in LVS_LIST mode */
6107 if(uView == LVS_LIST)
6109 infoPtr->nItemWidth = cx;
6110 LISTVIEW_InvalidateList(infoPtr);
6111 return TRUE;
6114 /* autosize based on listview items width */
6115 if(cx == LVSCW_AUTOSIZE)
6117 /* set the width of the column to the width of the widest item */
6118 if (iCol == 0 || uView == LVS_LIST)
6120 cx = 0;
6121 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6123 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6124 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6126 if (infoPtr->himlSmall)
6127 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6129 else
6131 lvItem.iSubItem = iCol;
6132 lvItem.mask = LVIF_TEXT;
6133 lvItem.pszText = szDispText;
6134 lvItem.cchTextMax = DISP_TEXT_SIZE;
6135 cx = 0;
6136 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6138 lvItem.iItem = item_index;
6139 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6140 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6141 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6144 cx += TRAILING_PADDING;
6145 } /* autosize based on listview header width */
6146 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6148 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6150 /* if iCol is the last column make it fill the remainder of the controls width */
6151 if(iCol == (header_item_count - 1)) {
6152 /* get the width of every item except the current one */
6153 hdi.mask = HDI_WIDTH;
6154 cx = 0;
6156 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6157 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6158 cx+=hdi.cxy;
6161 /* retrieve the layout of the header */
6162 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6164 cx = (rcHeader.right - rcHeader.left) - cx;
6166 else
6168 /* Despite what the MS docs say, if this is not the last
6169 column, then MS resizes the column to the width of the
6170 largest text string in the column, including headers
6171 and items. This is different from LVSCW_AUTOSIZE in that
6172 LVSCW_AUTOSIZE ignores the header string length.
6175 /* retrieve header font */
6176 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6178 /* retrieve header text */
6179 hdi.mask = HDI_TEXT;
6180 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6181 hdi.pszText = text_buffer;
6183 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6185 /* determine the width of the text in the header */
6186 hdc = GetDC(infoPtr->hwndSelf);
6187 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6189 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6191 SelectObject(hdc, old_font); /* restore the old font */
6192 ReleaseDC(infoPtr->hwndSelf, hdc);
6194 lvItem.iSubItem = iCol;
6195 lvItem.mask = LVIF_TEXT;
6196 lvItem.pszText = szDispText;
6197 lvItem.cchTextMax = DISP_TEXT_SIZE;
6198 cx = size.cx;
6199 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6201 lvItem.iItem = item_index;
6202 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6203 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6204 nLabelWidth += TRAILING_PADDING;
6205 /* While it is possible for subitems to have icons, even MS messes
6206 up the positioning, so I suspect no applications actually use
6207 them. */
6208 if (item_index == 0 && infoPtr->himlSmall)
6209 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6210 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6215 /* call header to update the column change */
6216 hdi.mask = HDI_WIDTH;
6218 hdi.cxy = cx;
6219 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6221 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6223 return lret;
6226 /***
6227 * DESCRIPTION:
6228 * Sets the extended listview style.
6230 * PARAMETERS:
6231 * [I] infoPtr : valid pointer to the listview structure
6232 * [I] DWORD : mask
6233 * [I] DWORD : style
6235 * RETURN:
6236 * SUCCESS : previous style
6237 * FAILURE : 0
6239 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6241 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6243 /* set new style */
6244 if (dwMask)
6245 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6246 else
6247 infoPtr->dwLvExStyle = dwStyle;
6249 return dwOldStyle;
6252 /***
6253 * DESCRIPTION:
6254 * Sets the new hot cursor used during hot tracking and hover selection.
6256 * PARAMETER(S):
6257 * [I] infoPtr : valid pointer to the listview structure
6258 * [I} hCurosr : the new hot cursor handle
6260 * RETURN:
6261 * Returns the previous hot cursor
6263 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6265 HCURSOR oldCursor = infoPtr->hHotCursor;
6266 infoPtr->hHotCursor = hCursor;
6267 return oldCursor;
6271 /***
6272 * DESCRIPTION:
6273 * Sets the hot item index.
6275 * PARAMETERS:
6276 * [I] infoPtr : valid pointer to the listview structure
6277 * [I] INT : index
6279 * RETURN:
6280 * SUCCESS : previous hot item index
6281 * FAILURE : -1 (no hot item)
6283 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6285 INT iOldIndex = infoPtr->nHotItem;
6286 infoPtr->nHotItem = iIndex;
6287 return iOldIndex;
6291 /***
6292 * DESCRIPTION:
6293 * Sets the amount of time the cursor must hover over an item before it is selected.
6295 * PARAMETER(S):
6296 * [I] infoPtr : valid pointer to the listview structure
6297 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6299 * RETURN:
6300 * Returns the previous hover time
6302 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6304 DWORD oldHoverTime = infoPtr->dwHoverTime;
6305 infoPtr->dwHoverTime = dwHoverTime;
6306 return oldHoverTime;
6309 /***
6310 * DESCRIPTION:
6311 * Sets spacing for icons of LVS_ICON style.
6313 * PARAMETER(S):
6314 * [I] infoPtr : valid pointer to the listview structure
6315 * [I] DWORD : MAKELONG(cx, cy)
6317 * RETURN:
6318 * MAKELONG(oldcx, oldcy)
6320 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6322 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6323 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6324 LONG lStyle = infoPtr->dwStyle;
6325 UINT uView = lStyle & LVS_TYPEMASK;
6327 TRACE("requested=(%d,%d)\n", cx, cy);
6329 /* this is supported only for LVS_ICON style */
6330 if (uView != LVS_ICON) return oldspacing;
6332 /* set to defaults, if instructed to */
6333 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6334 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6336 /* if 0 then compute width
6337 * FIXME: Should scan each item and determine max width of
6338 * icon or label, then make that the width */
6339 if (cx == 0)
6340 cx = infoPtr->iconSpacing.cx;
6342 /* if 0 then compute height */
6343 if (cy == 0)
6344 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6345 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6348 infoPtr->iconSpacing.cx = cx;
6349 infoPtr->iconSpacing.cy = cy;
6351 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6352 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6353 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6354 infoPtr->ntmHeight);
6356 /* these depend on the iconSpacing */
6357 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6358 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6360 return oldspacing;
6363 inline void update_icon_size(HIMAGELIST himl, SIZE *size)
6365 INT cx, cy;
6367 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6369 size->cx = cx;
6370 size->cy = cy;
6372 else
6373 size->cx = size->cy = 0;
6376 /***
6377 * DESCRIPTION:
6378 * Sets image lists.
6380 * PARAMETER(S):
6381 * [I] infoPtr : valid pointer to the listview structure
6382 * [I] INT : image list type
6383 * [I] HIMAGELIST : image list handle
6385 * RETURN:
6386 * SUCCESS : old image list
6387 * FAILURE : NULL
6389 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6391 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6392 INT oldHeight = infoPtr->nItemHeight;
6393 HIMAGELIST himlOld = 0;
6395 switch (nType)
6397 case LVSIL_NORMAL:
6398 himlOld = infoPtr->himlNormal;
6399 infoPtr->himlNormal = himl;
6400 if (uView == LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6401 LISTVIEW_SetIconSpacing(infoPtr, 0);
6402 break;
6404 case LVSIL_SMALL:
6405 himlOld = infoPtr->himlSmall;
6406 infoPtr->himlSmall = himl;
6407 if (uView != LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6408 break;
6410 case LVSIL_STATE:
6411 himlOld = infoPtr->himlState;
6412 infoPtr->himlState = himl;
6413 update_icon_size(himl, &infoPtr->iconStateSize);
6414 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6415 break;
6417 default:
6418 ERR("Unknown icon type=%d\n", nType);
6419 return NULL;
6422 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6423 if (infoPtr->nItemHeight != oldHeight)
6424 LISTVIEW_UpdateScroll(infoPtr);
6426 return himlOld;
6429 /***
6430 * DESCRIPTION:
6431 * Preallocates memory (does *not* set the actual count of items !)
6433 * PARAMETER(S):
6434 * [I] infoPtr : valid pointer to the listview structure
6435 * [I] INT : item count (projected number of items to allocate)
6436 * [I] DWORD : update flags
6438 * RETURN:
6439 * SUCCESS : TRUE
6440 * FAILURE : FALSE
6442 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6444 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6446 if (infoPtr->dwStyle & LVS_OWNERDATA)
6448 int precount,topvisible;
6450 TRACE("LVS_OWNERDATA is set!\n");
6451 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6452 FIXME("flags %s %s not implemented\n",
6453 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6454 : "",
6455 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6457 LISTVIEW_RemoveAllSelections(infoPtr, -1);
6459 precount = infoPtr->nItemCount;
6460 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6461 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6463 infoPtr->nItemCount = nItems;
6464 infoPtr->nItemWidth = max(LISTVIEW_CalculateMaxWidth(infoPtr),
6465 DEFAULT_COLUMN_WIDTH);
6467 LISTVIEW_UpdateSize(infoPtr);
6468 LISTVIEW_UpdateScroll(infoPtr);
6470 if (min(precount,infoPtr->nItemCount) < topvisible)
6471 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6473 else
6475 /* According to MSDN for non-LVS_OWNERDATA this is just
6476 * a performance issue. The control allocates its internal
6477 * data structures for the number of items specified. It
6478 * cuts down on the number of memory allocations. Therefore
6479 * we will just issue a WARN here
6481 WARN("for non-ownerdata performance option not implemented.\n");
6484 return TRUE;
6487 /***
6488 * DESCRIPTION:
6489 * Sets the position of an item.
6491 * PARAMETER(S):
6492 * [I] infoPtr : valid pointer to the listview structure
6493 * [I] nItem : item index
6494 * [I] pt : coordinate
6496 * RETURN:
6497 * SUCCESS : TRUE
6498 * FAILURE : FALSE
6500 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6502 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6503 POINT old;
6505 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6507 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6508 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6510 /* This point value seems to be an undocumented feature.
6511 * The best guess is that it means either at the origin,
6512 * or at true beginning of the list. I will assume the origin. */
6513 if ((pt.x == -1) && (pt.y == -1))
6514 LISTVIEW_GetOrigin(infoPtr, &pt);
6515 else if (uView == LVS_ICON)
6517 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6518 pt.y -= ICON_TOP_PADDING;
6521 /* save the old position */
6522 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
6523 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
6525 /* Is the position changing? */
6526 if (pt.x == old.x && pt.y == old.y) return TRUE;
6528 /* FIXME: shouldn't we invalidate, as the item moved? */
6530 /* Allocating a POINTER for every item is too resource intensive,
6531 * so we'll keep the (x,y) in different arrays */
6532 if (DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)pt.x) &&
6533 DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)pt.y) )
6534 return TRUE;
6536 ERR("We should never fail here (nItem=%d, pt=%s), please report.\n",
6537 nItem, debugpoint(&pt));
6538 return FALSE;
6541 /***
6542 * DESCRIPTION:
6543 * Sets the state of one or many items.
6545 * PARAMETER(S):
6546 * [I] infoPtr : valid pointer to the listview structure
6547 * [I]INT : item index
6548 * [I] LPLVITEM : item or subitem info
6550 * RETURN:
6551 * SUCCESS : TRUE
6552 * FAILURE : FALSE
6554 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6556 BOOL bResult = TRUE;
6557 LVITEMW lvItem;
6559 lvItem.iItem = nItem;
6560 lvItem.iSubItem = 0;
6561 lvItem.mask = LVIF_STATE;
6562 lvItem.state = lpLVItem->state;
6563 lvItem.stateMask = lpLVItem->stateMask;
6564 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6566 if (nItem == -1)
6568 /* apply to all items */
6569 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6570 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6572 else
6573 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6575 return bResult;
6578 /***
6579 * DESCRIPTION:
6580 * Sets the text of an item or subitem.
6582 * PARAMETER(S):
6583 * [I] hwnd : window handle
6584 * [I] nItem : item index
6585 * [I] lpLVItem : item or subitem info
6586 * [I] isW : TRUE if input is Unicode
6588 * RETURN:
6589 * SUCCESS : TRUE
6590 * FAILURE : FALSE
6592 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6594 LVITEMW lvItem;
6596 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6598 lvItem.iItem = nItem;
6599 lvItem.iSubItem = lpLVItem->iSubItem;
6600 lvItem.mask = LVIF_TEXT;
6601 lvItem.pszText = lpLVItem->pszText;
6602 lvItem.cchTextMax = lpLVItem->cchTextMax;
6604 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6606 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6609 /***
6610 * DESCRIPTION:
6611 * Set item index that marks the start of a multiple selection.
6613 * PARAMETER(S):
6614 * [I] infoPtr : valid pointer to the listview structure
6615 * [I] INT : index
6617 * RETURN:
6618 * Index number or -1 if there is no selection mark.
6620 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6622 INT nOldIndex = infoPtr->nSelectionMark;
6624 TRACE("(nIndex=%d)\n", nIndex);
6626 infoPtr->nSelectionMark = nIndex;
6628 return nOldIndex;
6631 /***
6632 * DESCRIPTION:
6633 * Sets the text background color.
6635 * PARAMETER(S):
6636 * [I] infoPtr : valid pointer to the listview structure
6637 * [I] COLORREF : text background color
6639 * RETURN:
6640 * SUCCESS : TRUE
6641 * FAILURE : FALSE
6643 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6645 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6647 if (infoPtr->clrTextBk != clrTextBk)
6649 infoPtr->clrTextBk = clrTextBk;
6650 LISTVIEW_InvalidateList(infoPtr);
6653 return TRUE;
6656 /***
6657 * DESCRIPTION:
6658 * Sets the text foreground color.
6660 * PARAMETER(S):
6661 * [I] infoPtr : valid pointer to the listview structure
6662 * [I] COLORREF : text color
6664 * RETURN:
6665 * SUCCESS : TRUE
6666 * FAILURE : FALSE
6668 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6670 TRACE("(clrText=%lx)\n", clrText);
6672 if (infoPtr->clrText != clrText)
6674 infoPtr->clrText = clrText;
6675 LISTVIEW_InvalidateList(infoPtr);
6678 return TRUE;
6681 /* LISTVIEW_SetToolTips */
6682 /* LISTVIEW_SetUnicodeFormat */
6683 /* LISTVIEW_SetWorkAreas */
6685 /***
6686 * DESCRIPTION:
6687 * Callback internally used by LISTVIEW_SortItems()
6689 * PARAMETER(S):
6690 * [I] LPVOID : first LISTVIEW_ITEM to compare
6691 * [I] LPVOID : second LISTVIEW_ITEM to compare
6692 * [I] LPARAM : HWND of control
6694 * RETURN:
6695 * if first comes before second : negative
6696 * if first comes after second : positive
6697 * if first and second are equivalent : zero
6699 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6701 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6702 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6703 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6705 /* Forward the call to the client defined callback */
6706 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6709 /***
6710 * DESCRIPTION:
6711 * Sorts the listview items.
6713 * PARAMETER(S):
6714 * [I] infoPtr : valid pointer to the listview structure
6715 * [I] WPARAM : application-defined value
6716 * [I] LPARAM : pointer to comparision callback
6718 * RETURN:
6719 * SUCCESS : TRUE
6720 * FAILURE : FALSE
6722 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6724 UINT lStyle = infoPtr->dwStyle;
6725 HDPA hdpaSubItems;
6726 LISTVIEW_ITEM *lpItem;
6727 LPVOID selectionMarkItem;
6728 LVITEMW item;
6729 int i;
6731 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6733 if (lStyle & LVS_OWNERDATA) return FALSE;
6735 if (!infoPtr->hdpaItems) return FALSE;
6737 /* if there are 0 or 1 items, there is no need to sort */
6738 if (infoPtr->nItemCount < 2) return TRUE;
6740 if (infoPtr->nFocusedItem >= 0)
6742 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6743 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6744 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6746 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
6747 /* clear the lpItem->state for non-selected ones */
6748 /* remove the selection ranges */
6750 infoPtr->pfnCompare = pfnCompare;
6751 infoPtr->lParamSort = lParamSort;
6752 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6754 /* Adjust selections and indices so that they are the way they should
6755 * be after the sort (otherwise, the list items move around, but
6756 * whatever is at the item's previous original position will be
6757 * selected instead)
6759 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6760 for (i=0; i < infoPtr->nItemCount; i++)
6762 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6763 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6765 if (lpItem->state & LVIS_SELECTED)
6767 item.state = LVIS_SELECTED;
6768 item.stateMask = LVIS_SELECTED;
6769 LISTVIEW_SetItemState(infoPtr, i, &item);
6771 if (lpItem->state & LVIS_FOCUSED)
6773 infoPtr->nFocusedItem = i;
6774 lpItem->state &= ~LVIS_FOCUSED;
6777 if (selectionMarkItem != NULL)
6778 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6779 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6781 /* align the items */
6782 LISTVIEW_AlignTop(infoPtr);
6784 /* refresh the display */
6785 LISTVIEW_InvalidateList(infoPtr); /* FIXME: display should not change for [SMALL]ICON view */
6787 return TRUE;
6790 /***
6791 * DESCRIPTION:
6792 * Updates an items or rearranges the listview control.
6794 * PARAMETER(S):
6795 * [I] infoPtr : valid pointer to the listview structure
6796 * [I] INT : item index
6798 * RETURN:
6799 * SUCCESS : TRUE
6800 * FAILURE : FALSE
6802 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
6804 LONG lStyle = infoPtr->dwStyle;
6805 UINT uView = lStyle & LVS_TYPEMASK;
6807 TRACE("(nItem=%d)\n", nItem);
6809 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6811 /* rearrange with default alignment style */
6812 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
6813 LISTVIEW_Arrange(infoPtr, 0);
6814 else
6815 LISTVIEW_InvalidateItem(infoPtr, nItem);
6817 return TRUE;
6821 /***
6822 * DESCRIPTION:
6823 * Creates the listview control.
6825 * PARAMETER(S):
6826 * [I] hwnd : window handle
6827 * [I] lpcs : the create parameters
6829 * RETURN:
6830 * Success: 0
6831 * Failure: -1
6833 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
6835 LISTVIEW_INFO *infoPtr;
6836 UINT uView = lpcs->style & LVS_TYPEMASK;
6837 LOGFONTW logFont;
6839 TRACE("(lpcs=%p)\n", lpcs);
6841 /* initialize info pointer */
6842 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
6843 if (!infoPtr) return -1;
6845 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
6847 infoPtr->hwndSelf = hwnd;
6848 infoPtr->dwStyle = lpcs->style;
6849 /* determine the type of structures to use */
6850 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
6851 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
6853 /* initialize color information */
6854 infoPtr->clrBk = CLR_NONE;
6855 infoPtr->clrText = comctl32_color.clrWindowText;
6856 infoPtr->clrTextBk = CLR_DEFAULT;
6857 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
6859 /* set default values */
6860 infoPtr->nFocusedItem = -1;
6861 infoPtr->nSelectionMark = -1;
6862 infoPtr->nHotItem = -1;
6863 infoPtr->bRedraw = TRUE;
6864 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
6865 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
6866 infoPtr->nEditLabelItem = -1;
6868 /* get default font (icon title) */
6869 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
6870 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
6871 infoPtr->hFont = infoPtr->hDefaultFont;
6872 LISTVIEW_SaveTextMetrics(infoPtr);
6874 /* create header */
6875 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
6876 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
6877 0, 0, 0, 0, hwnd, (HMENU)0,
6878 lpcs->hInstance, NULL);
6880 /* set header unicode format */
6881 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
6883 /* set header font */
6884 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
6885 (LPARAM)TRUE);
6887 if (uView == LVS_ICON)
6889 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
6890 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
6892 else if (uView == LVS_REPORT)
6894 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
6896 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6898 else
6900 /* set HDS_HIDDEN flag to hide the header bar */
6901 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
6902 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
6906 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6907 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6909 else
6911 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6912 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6915 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
6916 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
6918 /* display unsupported listview window styles */
6919 LISTVIEW_UnsupportedStyles(lpcs->style);
6921 /* allocate memory for the data structure */
6922 infoPtr->hdpaItems = DPA_Create(10);
6923 infoPtr->hdpaPosX = DPA_Create(10);
6924 infoPtr->hdpaPosY = DPA_Create(10);
6926 /* allocate memory for the selection ranges */
6927 infoPtr->hdpaSelectionRanges = DPA_Create(10);
6929 /* initialize size of items */
6930 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6931 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6933 /* initialize the hover time to -1(indicating the default system hover time) */
6934 infoPtr->dwHoverTime = -1;
6936 return 0;
6939 /***
6940 * DESCRIPTION:
6941 * Erases the background of the listview control.
6943 * PARAMETER(S):
6944 * [I] infoPtr : valid pointer to the listview structure
6945 * [I] hdc : device context handle
6947 * RETURN:
6948 * SUCCESS : TRUE
6949 * FAILURE : FALSE
6951 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
6953 RECT rc;
6955 TRACE("(hdc=%x)\n", hdc);
6957 if (!GetClipBox(hdc, &rc)) return FALSE;
6959 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
6963 /***
6964 * DESCRIPTION:
6965 * Helper function for LISTVIEW_[HV]Scroll *only*.
6966 * Performs vertical/horizontal scrolling by a give amount.
6968 * PARAMETER(S):
6969 * [I] infoPtr : valid pointer to the listview structure
6970 * [I] dx : amount of horizontal scroll
6971 * [I] dy : amount of vertical scroll
6973 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6975 /* now we can scroll the list */
6976 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
6977 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
6978 /* if we have focus, adjust rect */
6979 OffsetRect(&infoPtr->rcFocus, dx, dy);
6980 UpdateWindow(infoPtr->hwndSelf);
6983 /***
6984 * DESCRIPTION:
6985 * Performs vertical scrolling.
6987 * PARAMETER(S):
6988 * [I] infoPtr : valid pointer to the listview structure
6989 * [I] nScrollCode : scroll code
6990 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
6991 * [I] hScrollWnd : scrollbar control window handle
6993 * RETURN:
6994 * Zero
6996 * NOTES:
6997 * SB_LINEUP/SB_LINEDOWN:
6998 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
6999 * for LVS_REPORT is 1 line
7000 * for LVS_LIST cannot occur
7003 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7004 INT nScrollDiff, HWND hScrollWnd)
7006 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7007 INT nOldScrollPos, nNewScrollPos;
7008 SCROLLINFO scrollInfo;
7009 BOOL is_an_icon;
7011 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7013 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7015 scrollInfo.cbSize = sizeof(SCROLLINFO);
7016 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7018 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7020 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7022 nOldScrollPos = scrollInfo.nPos;
7023 switch (nScrollCode)
7025 case SB_INTERNAL:
7026 break;
7028 case SB_LINEUP:
7029 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7030 break;
7032 case SB_LINEDOWN:
7033 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7034 break;
7036 case SB_PAGEUP:
7037 nScrollDiff = -scrollInfo.nPage;
7038 break;
7040 case SB_PAGEDOWN:
7041 nScrollDiff = scrollInfo.nPage;
7042 break;
7044 case SB_THUMBPOSITION:
7045 case SB_THUMBTRACK:
7046 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7047 break;
7049 default:
7050 nScrollDiff = 0;
7053 /* quit right away if pos isn't changing */
7054 if (nScrollDiff == 0) return 0;
7056 /* calculate new position, and handle overflows */
7057 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7058 if (nScrollDiff > 0) {
7059 if (nNewScrollPos < nOldScrollPos ||
7060 nNewScrollPos > scrollInfo.nMax)
7061 nNewScrollPos = scrollInfo.nMax;
7062 } else {
7063 if (nNewScrollPos > nOldScrollPos ||
7064 nNewScrollPos < scrollInfo.nMin)
7065 nNewScrollPos = scrollInfo.nMin;
7068 /* set the new position, and reread in case it changed */
7069 scrollInfo.fMask = SIF_POS;
7070 scrollInfo.nPos = nNewScrollPos;
7071 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7073 /* carry on only if it really changed */
7074 if (nNewScrollPos == nOldScrollPos) return 0;
7076 /* now adjust to client coordinates */
7077 nScrollDiff = nOldScrollPos - nNewScrollPos;
7078 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7080 /* and scroll the window */
7081 scroll_list(infoPtr, 0, nScrollDiff);
7083 return 0;
7086 /***
7087 * DESCRIPTION:
7088 * Performs horizontal 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_LINELEFT/SB_LINERIGHT:
7101 * for LVS_ICON, LVS_SMALLICON 1 pixel
7102 * for LVS_REPORT is 1 pixel
7103 * for LVS_LIST is 1 column --> which is a 1 because the
7104 * scroll is based on columns not pixels
7107 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7108 INT nScrollDiff, HWND hScrollWnd)
7110 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7111 INT nOldScrollPos, nNewScrollPos;
7112 SCROLLINFO scrollInfo;
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 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7123 nOldScrollPos = scrollInfo.nPos;
7125 switch (nScrollCode)
7127 case SB_INTERNAL:
7128 break;
7130 case SB_LINELEFT:
7131 nScrollDiff = -1;
7132 break;
7134 case SB_LINERIGHT:
7135 nScrollDiff = 1;
7136 break;
7138 case SB_PAGELEFT:
7139 nScrollDiff = -scrollInfo.nPage;
7140 break;
7142 case SB_PAGERIGHT:
7143 nScrollDiff = scrollInfo.nPage;
7144 break;
7146 case SB_THUMBPOSITION:
7147 case SB_THUMBTRACK:
7148 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7149 break;
7151 default:
7152 nScrollDiff = 0;
7155 /* quit right away if pos isn't changing */
7156 if (nScrollDiff == 0) return 0;
7158 /* calculate new position, and handle overflows */
7159 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7160 if (nScrollDiff > 0) {
7161 if (nNewScrollPos < nOldScrollPos ||
7162 nNewScrollPos > scrollInfo.nMax)
7163 nNewScrollPos = scrollInfo.nMax;
7164 } else {
7165 if (nNewScrollPos > nOldScrollPos ||
7166 nNewScrollPos < scrollInfo.nMin)
7167 nNewScrollPos = scrollInfo.nMin;
7170 /* set the new position, and reread in case it changed */
7171 scrollInfo.fMask = SIF_POS;
7172 scrollInfo.nPos = nNewScrollPos;
7173 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7175 /* carry on only if it really changed */
7176 if (nNewScrollPos == nOldScrollPos) return 0;
7178 if(uView == LVS_REPORT)
7179 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7181 /* now adjust to client coordinates */
7182 nScrollDiff = nOldScrollPos - nNewScrollPos;
7183 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7185 /* and scroll the window */
7186 scroll_list(infoPtr, nScrollDiff, 0);
7188 return 0;
7191 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7193 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7194 INT gcWheelDelta = 0;
7195 UINT pulScrollLines = 3;
7196 SCROLLINFO scrollInfo;
7198 TRACE("(wheelDelta=%d)\n", wheelDelta);
7200 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7201 gcWheelDelta -= wheelDelta;
7203 scrollInfo.cbSize = sizeof(SCROLLINFO);
7204 scrollInfo.fMask = SIF_POS;
7206 switch(uView)
7208 case LVS_ICON:
7209 case LVS_SMALLICON:
7211 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7212 * should be fixed in the future.
7214 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7215 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7216 scrollInfo.nPos + (gcWheelDelta < 0) ?
7217 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7218 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7219 break;
7221 case LVS_REPORT:
7222 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7224 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7226 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7227 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7228 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7231 break;
7233 case LVS_LIST:
7234 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7235 break;
7237 return 0;
7240 /***
7241 * DESCRIPTION:
7242 * ???
7244 * PARAMETER(S):
7245 * [I] infoPtr : valid pointer to the listview structure
7246 * [I] INT : virtual key
7247 * [I] LONG : key data
7249 * RETURN:
7250 * Zero
7252 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7254 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7255 INT nItem = -1;
7256 NMLVKEYDOWN nmKeyDown;
7258 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7260 /* send LVN_KEYDOWN notification */
7261 nmKeyDown.wVKey = nVirtualKey;
7262 nmKeyDown.flags = 0;
7263 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7265 switch (nVirtualKey)
7267 case VK_RETURN:
7268 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7270 notify(infoPtr, NM_RETURN);
7271 notify(infoPtr, LVN_ITEMACTIVATE);
7273 break;
7275 case VK_HOME:
7276 if (infoPtr->nItemCount > 0)
7277 nItem = 0;
7278 break;
7280 case VK_END:
7281 if (infoPtr->nItemCount > 0)
7282 nItem = infoPtr->nItemCount - 1;
7283 break;
7285 case VK_LEFT:
7286 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7287 break;
7289 case VK_UP:
7290 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7291 break;
7293 case VK_RIGHT:
7294 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7295 break;
7297 case VK_DOWN:
7298 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7299 break;
7301 case VK_PRIOR:
7302 if (uView == LVS_REPORT)
7303 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7304 else
7305 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7306 * LISTVIEW_GetCountPerRow(infoPtr);
7307 if(nItem < 0) nItem = 0;
7308 break;
7310 case VK_NEXT:
7311 if (uView == LVS_REPORT)
7312 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7313 else
7314 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7315 * LISTVIEW_GetCountPerRow(infoPtr);
7316 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7317 break;
7320 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7321 LISTVIEW_KeySelection(infoPtr, nItem);
7323 return 0;
7326 /***
7327 * DESCRIPTION:
7328 * Kills the focus.
7330 * PARAMETER(S):
7331 * [I] infoPtr : valid pointer to the listview structure
7333 * RETURN:
7334 * Zero
7336 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7338 TRACE("()\n");
7340 /* if we did not have the focus, there's nothing to do */
7341 if (!infoPtr->bFocus) return 0;
7343 /* send NM_KILLFOCUS notification */
7344 notify(infoPtr, NM_KILLFOCUS);
7346 /* if we have a focus rectagle, get rid of it */
7347 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7349 /* set window focus flag */
7350 infoPtr->bFocus = FALSE;
7352 /* invalidate the selected items before reseting focus flag */
7353 LISTVIEW_InvalidateSelectedItems(infoPtr);
7355 return 0;
7358 /***
7359 * DESCRIPTION:
7360 * Processes double click messages (left mouse button).
7362 * PARAMETER(S):
7363 * [I] infoPtr : valid pointer to the listview structure
7364 * [I] wKey : key flag
7365 * [I] pts : mouse coordinate
7367 * RETURN:
7368 * Zero
7370 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7372 LVHITTESTINFO htInfo;
7374 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7376 /* send NM_RELEASEDCAPTURE notification */
7377 notify(infoPtr, NM_RELEASEDCAPTURE);
7379 htInfo.pt.x = pts.x;
7380 htInfo.pt.y = pts.y;
7382 /* send NM_DBLCLK notification */
7383 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7384 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7386 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7387 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7389 return 0;
7392 /***
7393 * DESCRIPTION:
7394 * Processes mouse down messages (left mouse button).
7396 * PARAMETER(S):
7397 * [I] infoPtr : valid pointer to the listview structure
7398 * [I] wKey : key flag
7399 * [I] pts : mouse coordinate
7401 * RETURN:
7402 * Zero
7404 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7406 LVHITTESTINFO lvHitTestInfo;
7407 LONG lStyle = infoPtr->dwStyle;
7408 static BOOL bGroupSelect = TRUE;
7409 POINT pt = { pts.x, pts.y };
7410 INT nItem;
7412 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7414 /* send NM_RELEASEDCAPTURE notification */
7415 notify(infoPtr, NM_RELEASEDCAPTURE);
7417 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7419 /* set left button down flag */
7420 infoPtr->bLButtonDown = TRUE;
7422 lvHitTestInfo.pt.x = pts.x;
7423 lvHitTestInfo.pt.y = pts.y;
7425 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7426 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7427 infoPtr->nEditLabelItem = -1;
7428 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7430 if (lStyle & LVS_SINGLESEL)
7432 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7433 infoPtr->nEditLabelItem = nItem;
7434 else
7435 LISTVIEW_SetSelection(infoPtr, nItem);
7437 else
7439 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7441 if (bGroupSelect)
7442 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7443 else
7445 LVITEMW item;
7447 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7448 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7450 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7451 infoPtr->nSelectionMark = nItem;
7454 else if (wKey & MK_CONTROL)
7456 LVITEMW item;
7458 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7460 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7461 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7462 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7463 infoPtr->nSelectionMark = nItem;
7465 else if (wKey & MK_SHIFT)
7467 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7469 else
7471 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7472 infoPtr->nEditLabelItem = nItem;
7474 /* set selection (clears other pre-existing selections) */
7475 LISTVIEW_SetSelection(infoPtr, nItem);
7479 else
7481 /* remove all selections */
7482 LISTVIEW_RemoveAllSelections(infoPtr, -1);
7485 return 0;
7488 /***
7489 * DESCRIPTION:
7490 * Processes mouse up messages (left mouse button).
7492 * PARAMETER(S):
7493 * [I] infoPtr : valid pointer to the listview structure
7494 * [I] wKey : key flag
7495 * [I] pts : mouse coordinate
7497 * RETURN:
7498 * Zero
7500 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7502 LVHITTESTINFO lvHitTestInfo;
7504 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7506 if (!infoPtr->bLButtonDown) return 0;
7508 lvHitTestInfo.pt.x = pts.x;
7509 lvHitTestInfo.pt.y = pts.y;
7511 /* send NM_CLICK notification */
7512 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7513 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7515 /* set left button flag */
7516 infoPtr->bLButtonDown = FALSE;
7518 /* if we clicked on a selected item, edit the label */
7519 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7520 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7522 return 0;
7525 /***
7526 * DESCRIPTION:
7527 * Destroys the listview control (called after WM_DESTROY).
7529 * PARAMETER(S):
7530 * [I] infoPtr : valid pointer to the listview structure
7532 * RETURN:
7533 * Zero
7535 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7537 LONG lStyle = infoPtr->dwStyle;
7539 TRACE("()\n");
7541 /* delete all items */
7542 LISTVIEW_DeleteAllItems(infoPtr);
7544 /* destroy data structure */
7545 DPA_Destroy(infoPtr->hdpaItems);
7546 DPA_Destroy(infoPtr->hdpaSelectionRanges);
7548 /* destroy image lists */
7549 if (!(lStyle & LVS_SHAREIMAGELISTS))
7551 /* FIXME: If the caller does a ImageList_Destroy and then we
7552 * do this code the area will be freed twice. Currently
7553 * this generates an "err:heap:HEAP_ValidateInUseArena
7554 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
7555 * has PREV_FREE flag" sometimes.
7557 * We will leak the memory till we figure out how to fix
7559 if (infoPtr->himlNormal)
7560 ImageList_Destroy(infoPtr->himlNormal);
7561 if (infoPtr->himlSmall)
7562 ImageList_Destroy(infoPtr->himlSmall);
7563 if (infoPtr->himlState)
7564 ImageList_Destroy(infoPtr->himlState);
7567 /* destroy font, bkgnd brush */
7568 infoPtr->hFont = 0;
7569 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7570 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7572 /* free listview info pointer*/
7573 COMCTL32_Free(infoPtr);
7575 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7576 return 0;
7579 /***
7580 * DESCRIPTION:
7581 * Handles notifications from children.
7583 * PARAMETER(S):
7584 * [I] infoPtr : valid pointer to the listview structure
7585 * [I] INT : control identifier
7586 * [I] LPNMHDR : notification information
7588 * RETURN:
7589 * Zero
7591 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7593 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7595 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7597 /* handle notification from header control */
7598 if (lpnmh->code == HDN_ENDTRACKW)
7600 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7601 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7603 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7605 /* Handle sorting by Header Column */
7606 NMLISTVIEW nmlv;
7608 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7609 nmlv.iItem = -1;
7610 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
7611 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7613 else if(lpnmh->code == NM_RELEASEDCAPTURE)
7615 /* Idealy this should be done in HDN_ENDTRACKA
7616 * but since SetItemBounds in Header.c is called after
7617 * the notification is sent, it is neccessary to handle the
7618 * update of the scroll bar here (Header.c works fine as it is,
7619 * no need to disturb it)
7621 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7622 LISTVIEW_UpdateScroll(infoPtr);
7623 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7628 return 0;
7631 /***
7632 * DESCRIPTION:
7633 * Determines the type of structure to use.
7635 * PARAMETER(S):
7636 * [I] infoPtr : valid pointer to the listview structureof the sender
7637 * [I] HWND : listview window handle
7638 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
7640 * RETURN:
7641 * Zero
7643 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7645 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7647 if (nCommand == NF_REQUERY)
7648 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7649 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7650 return 0;
7653 /***
7654 * DESCRIPTION:
7655 * Paints/Repaints the listview control.
7657 * PARAMETER(S):
7658 * [I] infoPtr : valid pointer to the listview structure
7659 * [I] HDC : device context handle
7661 * RETURN:
7662 * Zero
7664 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7666 TRACE("(hdc=%x)\n", hdc);
7668 if (hdc)
7669 LISTVIEW_Refresh(infoPtr, hdc);
7670 else
7672 PAINTSTRUCT ps;
7674 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7675 if (!hdc) return 1;
7676 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7677 LISTVIEW_Refresh(infoPtr, hdc);
7678 EndPaint(infoPtr->hwndSelf, &ps);
7681 return 0;
7684 /***
7685 * DESCRIPTION:
7686 * Processes double click messages (right mouse button).
7688 * PARAMETER(S):
7689 * [I] infoPtr : valid pointer to the listview structure
7690 * [I] wKey : key flag
7691 * [I] pts : mouse coordinate
7693 * RETURN:
7694 * Zero
7696 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7698 LVHITTESTINFO lvHitTestInfo;
7700 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7702 /* send NM_RELEASEDCAPTURE notification */
7703 notify(infoPtr, NM_RELEASEDCAPTURE);
7705 /* send NM_RDBLCLK notification */
7706 lvHitTestInfo.pt.x = pts.x;
7707 lvHitTestInfo.pt.y = pts.y;
7708 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7709 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
7711 return 0;
7714 /***
7715 * DESCRIPTION:
7716 * Processes mouse down messages (right mouse button).
7718 * PARAMETER(S):
7719 * [I] infoPtr : valid pointer to the listview structure
7720 * [I] wKey : key flag
7721 * [I] pts : mouse coordinate
7723 * RETURN:
7724 * Zero
7726 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7728 LVHITTESTINFO lvHitTestInfo;
7729 INT nItem;
7731 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7733 /* send NM_RELEASEDCAPTURE notification */
7734 notify(infoPtr, NM_RELEASEDCAPTURE);
7736 /* make sure the listview control window has the focus */
7737 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7739 /* set right button down flag */
7740 infoPtr->bRButtonDown = TRUE;
7742 /* determine the index of the selected item */
7743 lvHitTestInfo.pt.x = pts.x;
7744 lvHitTestInfo.pt.y = pts.y;
7745 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7747 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7749 LISTVIEW_SetItemFocus(infoPtr, nItem);
7750 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7751 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7752 LISTVIEW_SetSelection(infoPtr, nItem);
7754 else
7756 LISTVIEW_RemoveAllSelections(infoPtr, -1);
7759 return 0;
7762 /***
7763 * DESCRIPTION:
7764 * Processes mouse up messages (right mouse button).
7766 * PARAMETER(S):
7767 * [I] infoPtr : valid pointer to the listview structure
7768 * [I] wKey : key flag
7769 * [I] pts : mouse coordinate
7771 * RETURN:
7772 * Zero
7774 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7776 LVHITTESTINFO lvHitTestInfo;
7777 POINT pt;
7779 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7781 if (!infoPtr->bRButtonDown) return 0;
7783 /* set button flag */
7784 infoPtr->bRButtonDown = FALSE;
7786 /* Send NM_RClICK notification */
7787 lvHitTestInfo.pt.x = pts.x;
7788 lvHitTestInfo.pt.y = pts.y;
7789 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7790 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
7792 /* Change to screen coordinate for WM_CONTEXTMENU */
7793 pt = lvHitTestInfo.pt;
7794 ClientToScreen(infoPtr->hwndSelf, &pt);
7796 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
7797 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
7798 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
7800 return 0;
7804 /***
7805 * DESCRIPTION:
7806 * Sets the cursor.
7808 * PARAMETER(S):
7809 * [I] infoPtr : valid pointer to the listview structure
7810 * [I] hwnd : window handle of window containing the cursor
7811 * [I] nHittest : hit-test code
7812 * [I] wMouseMsg : ideintifier of the mouse message
7814 * RETURN:
7815 * TRUE if cursor is set
7816 * FALSE otherwise
7818 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
7820 LVHITTESTINFO lvHitTestInfo;
7822 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
7824 if(!infoPtr->hHotCursor) return FALSE;
7826 GetCursorPos(&lvHitTestInfo.pt);
7827 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
7829 SetCursor(infoPtr->hHotCursor);
7831 return TRUE;
7834 /***
7835 * DESCRIPTION:
7836 * Sets the focus.
7838 * PARAMETER(S):
7839 * [I] infoPtr : valid pointer to the listview structure
7840 * [I] infoPtr : handle of previously focused window
7842 * RETURN:
7843 * Zero
7845 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
7847 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
7849 /* if we have the focus already, there's nothing to do */
7850 if (infoPtr->bFocus) return 0;
7852 /* send NM_SETFOCUS notification */
7853 notify(infoPtr, NM_SETFOCUS);
7855 /* set window focus flag */
7856 infoPtr->bFocus = TRUE;
7858 /* put the focus rect back on */
7859 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
7861 /* redraw all visible selected items */
7862 LISTVIEW_InvalidateSelectedItems(infoPtr);
7864 return 0;
7867 /***
7868 * DESCRIPTION:
7869 * Sets the font.
7871 * PARAMETER(S):
7872 * [I] infoPtr : valid pointer to the listview structure
7873 * [I] HFONT : font handle
7874 * [I] WORD : redraw flag
7876 * RETURN:
7877 * Zero
7879 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
7881 HFONT oldFont = infoPtr->hFont;
7883 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
7885 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
7886 if (infoPtr->hFont == oldFont) return 0;
7888 LISTVIEW_SaveTextMetrics(infoPtr);
7890 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
7891 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
7893 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
7895 return 0;
7898 /***
7899 * DESCRIPTION:
7900 * Message handling for WM_SETREDRAW.
7901 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
7903 * PARAMETER(S):
7904 * [I] infoPtr : valid pointer to the listview structure
7905 * [I] bRedraw: state of redraw flag
7907 * RETURN:
7908 * DefWinProc return value
7910 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
7912 infoPtr->bRedraw = bRedraw;
7913 if(bRedraw)
7914 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
7915 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
7916 return 0;
7919 /***
7920 * DESCRIPTION:
7921 * Resizes the listview control. This function processes WM_SIZE
7922 * messages. At this time, the width and height are not used.
7924 * PARAMETER(S):
7925 * [I] infoPtr : valid pointer to the listview structure
7926 * [I] WORD : new width
7927 * [I] WORD : new height
7929 * RETURN:
7930 * Zero
7932 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
7934 LONG lStyle = infoPtr->dwStyle;
7935 UINT uView = lStyle & LVS_TYPEMASK;
7937 TRACE("(width=%d, height=%d)\n", Width, Height);
7939 if (LISTVIEW_UpdateSize(infoPtr))
7941 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
7943 if (lStyle & LVS_ALIGNLEFT)
7944 LISTVIEW_AlignLeft(infoPtr);
7945 else
7946 LISTVIEW_AlignTop(infoPtr);
7949 LISTVIEW_UpdateScroll(infoPtr);
7951 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7954 return 0;
7957 /***
7958 * DESCRIPTION:
7959 * Sets the size information.
7961 * PARAMETER(S):
7962 * [I] infoPtr : valid pointer to the listview structure
7964 * RETURN:
7965 * Zero if no size change
7966 * 1 of size changed
7968 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
7970 LONG lStyle = infoPtr->dwStyle;
7971 UINT uView = lStyle & LVS_TYPEMASK;
7972 RECT rcList;
7973 RECT rcOld;
7975 GetClientRect(infoPtr->hwndSelf, &rcList);
7976 CopyRect(&rcOld,&(infoPtr->rcList));
7977 infoPtr->rcList.left = 0;
7978 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
7979 infoPtr->rcList.top = 0;
7980 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
7982 if (uView == LVS_LIST)
7984 /* Apparently the "LIST" style is supposed to have the same
7985 * number of items in a column even if there is no scroll bar.
7986 * Since if a scroll bar already exists then the bottom is already
7987 * reduced, only reduce if the scroll bar does not currently exist.
7988 * The "2" is there to mimic the native control. I think it may be
7989 * related to either padding or edges. (GLA 7/2002)
7991 if (!(lStyle & WS_HSCROLL))
7993 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
7994 if (infoPtr->rcList.bottom > nHScrollHeight)
7995 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
7997 else
7999 if (infoPtr->rcList.bottom > 2)
8000 infoPtr->rcList.bottom -= 2;
8003 else if (uView == LVS_REPORT)
8005 HDLAYOUT hl;
8006 WINDOWPOS wp;
8008 hl.prc = &rcList;
8009 hl.pwpos = &wp;
8010 Header_Layout(infoPtr->hwndHeader, &hl);
8012 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8014 if (!(LVS_NOCOLUMNHEADER & lStyle))
8015 infoPtr->rcList.top = max(wp.cy, 0);
8017 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8020 /***
8021 * DESCRIPTION:
8022 * Processes WM_STYLECHANGED messages.
8024 * PARAMETER(S):
8025 * [I] infoPtr : valid pointer to the listview structure
8026 * [I] WPARAM : window style type (normal or extended)
8027 * [I] LPSTYLESTRUCT : window style information
8029 * RETURN:
8030 * Zero
8032 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8033 LPSTYLESTRUCT lpss)
8035 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8036 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8037 RECT rcList = infoPtr->rcList;
8039 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8040 wStyleType, lpss->styleOld, lpss->styleNew);
8042 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8044 if (wStyleType == GWL_STYLE)
8046 infoPtr->dwStyle = lpss->styleNew;
8048 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8049 ((lpss->styleNew & WS_HSCROLL) == 0))
8050 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8052 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8053 ((lpss->styleNew & WS_VSCROLL) == 0))
8054 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8056 /* If switching modes, then start with no scroll bars and then
8057 * decide.
8059 if (uNewView != uOldView)
8061 if (uOldView == LVS_REPORT)
8062 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8064 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8065 SetRectEmpty(&infoPtr->rcFocus);
8068 if (uNewView == LVS_ICON)
8070 INT oldcx, oldcy;
8072 /* First readjust the iconSize and if necessary the iconSpacing */
8073 oldcx = infoPtr->iconSize.cx;
8074 oldcy = infoPtr->iconSize.cy;
8075 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8076 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8077 if (infoPtr->himlNormal != NULL)
8079 INT cx, cy;
8080 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
8081 infoPtr->iconSize.cx = cx;
8082 infoPtr->iconSize.cy = cy;
8084 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
8086 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8087 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8088 LISTVIEW_SetIconSpacing(infoPtr,0);
8091 /* Now update the full item width and height */
8092 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8093 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8094 if (lpss->styleNew & LVS_ALIGNLEFT)
8095 LISTVIEW_AlignLeft(infoPtr);
8096 else
8097 LISTVIEW_AlignTop(infoPtr);
8099 else if (uNewView == LVS_REPORT)
8101 HDLAYOUT hl;
8102 WINDOWPOS wp;
8104 hl.prc = &rcList;
8105 hl.pwpos = &wp;
8106 Header_Layout(infoPtr->hwndHeader, &hl);
8107 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8108 wp.flags);
8109 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8110 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8112 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8113 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8114 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8115 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8117 else if (uNewView == LVS_LIST)
8119 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8120 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8121 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8122 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8124 else
8126 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8127 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8128 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8129 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8130 if (lpss->styleNew & LVS_ALIGNLEFT)
8131 LISTVIEW_AlignLeft(infoPtr);
8132 else
8133 LISTVIEW_AlignTop(infoPtr);
8136 /* update the size of the client area */
8137 LISTVIEW_UpdateSize(infoPtr);
8139 /* add scrollbars if needed */
8140 LISTVIEW_UpdateScroll(infoPtr);
8142 /* invalidate client area + erase background */
8143 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8145 /* print the list of unsupported window styles */
8146 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8149 /* If they change the view and we have an active edit control
8150 we will need to kill the control since the redraw will
8151 misplace the edit control.
8153 if (infoPtr->hwndEdit &&
8154 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8155 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8157 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8160 return 0;
8163 /***
8164 * DESCRIPTION:
8165 * Window procedure of the listview control.
8168 static LRESULT WINAPI
8169 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8171 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8173 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8175 if (!infoPtr && (uMsg != WM_CREATE))
8176 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8178 if (infoPtr)
8180 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8183 switch (uMsg)
8185 case LVM_APPROXIMATEVIEWRECT:
8186 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8187 LOWORD(lParam), HIWORD(lParam));
8188 case LVM_ARRANGE:
8189 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8191 /* case LVN_CANCELEDITLABEL */
8193 /* case LVM_CREATEDRAGIMAGE: */
8195 case LVM_DELETEALLITEMS:
8196 return LISTVIEW_DeleteAllItems(infoPtr);
8198 case LVM_DELETECOLUMN:
8199 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8201 case LVM_DELETEITEM:
8202 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8204 case LVM_EDITLABELW:
8205 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8207 case LVM_EDITLABELA:
8208 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8210 /* case LVN_ENABLEGROUPVIEW: */
8212 case LVM_ENSUREVISIBLE:
8213 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8215 case LVM_FINDITEMW:
8216 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8218 case LVM_FINDITEMA:
8219 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8221 case LVM_GETBKCOLOR:
8222 return infoPtr->clrBk;
8224 /* case LVM_GETBKIMAGE: */
8226 case LVM_GETCALLBACKMASK:
8227 return infoPtr->uCallbackMask;
8229 case LVM_GETCOLUMNA:
8230 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8232 case LVM_GETCOLUMNW:
8233 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8235 case LVM_GETCOLUMNORDERARRAY:
8236 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8238 case LVM_GETCOLUMNWIDTH:
8239 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8241 case LVM_GETCOUNTPERPAGE:
8242 return LISTVIEW_GetCountPerPage(infoPtr);
8244 case LVM_GETEDITCONTROL:
8245 return (LRESULT)infoPtr->hwndEdit;
8247 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8248 return infoPtr->dwLvExStyle;
8250 case LVM_GETHEADER:
8251 return (LRESULT)infoPtr->hwndHeader;
8253 case LVM_GETHOTCURSOR:
8254 return (LRESULT)infoPtr->hHotCursor;
8256 case LVM_GETHOTITEM:
8257 return infoPtr->nHotItem;
8259 case LVM_GETHOVERTIME:
8260 return infoPtr->dwHoverTime;
8262 case LVM_GETIMAGELIST:
8263 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8265 /* case LVN_GETINSERTMARK: */
8267 /* case LVN_GETINSERTMARKCOLOR: */
8269 /* case LVN_GETINSERTMARKRECT: */
8271 case LVM_GETISEARCHSTRINGA:
8272 case LVM_GETISEARCHSTRINGW:
8273 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8274 return FALSE;
8276 case LVM_GETITEMA:
8277 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8279 case LVM_GETITEMW:
8280 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8282 case LVM_GETITEMCOUNT:
8283 return infoPtr->nItemCount;
8285 case LVM_GETITEMPOSITION:
8286 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8288 case LVM_GETITEMRECT:
8289 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8291 case LVM_GETITEMSPACING:
8292 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8294 case LVM_GETITEMSTATE:
8295 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8297 case LVM_GETITEMTEXTA:
8298 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8300 case LVM_GETITEMTEXTW:
8301 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8303 case LVM_GETNEXTITEM:
8304 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8306 case LVM_GETNUMBEROFWORKAREAS:
8307 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8308 return 1;
8310 case LVM_GETORIGIN:
8311 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8313 /* case LVN_GETOUTLINECOLOR: */
8315 /* case LVM_GETSELECTEDCOLUMN: */
8317 case LVM_GETSELECTEDCOUNT:
8318 return LISTVIEW_GetSelectedCount(infoPtr);
8320 case LVM_GETSELECTIONMARK:
8321 return infoPtr->nSelectionMark;
8323 case LVM_GETSTRINGWIDTHA:
8324 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8326 case LVM_GETSTRINGWIDTHW:
8327 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8329 case LVM_GETSUBITEMRECT:
8330 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8332 case LVM_GETTEXTBKCOLOR:
8333 return infoPtr->clrTextBk;
8335 case LVM_GETTEXTCOLOR:
8336 return infoPtr->clrText;
8338 /* case LVN_GETTILEINFO: */
8340 /* case LVN_GETTILEVIEWINFO: */
8342 case LVM_GETTOOLTIPS:
8343 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8344 return FALSE;
8346 case LVM_GETTOPINDEX:
8347 return LISTVIEW_GetTopIndex(infoPtr);
8349 /*case LVM_GETUNICODEFORMAT:
8350 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8351 return FALSE;*/
8353 case LVM_GETVIEWRECT:
8354 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8356 case LVM_GETWORKAREAS:
8357 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8358 return FALSE;
8360 /* case LVN_HASGROUP: */
8362 case LVM_HITTEST:
8363 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8365 case LVM_INSERTCOLUMNA:
8366 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8368 case LVM_INSERTCOLUMNW:
8369 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8371 /* case LVN_INSERTGROUP: */
8373 /* case LVN_INSERTGROUPSORTED: */
8375 case LVM_INSERTITEMA:
8376 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8378 case LVM_INSERTITEMW:
8379 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8381 /* case LVN_INSERTMARKHITTEST: */
8383 /* case LVN_ISGROUPVIEWENABLED: */
8385 /* case LVN_MAPIDTOINDEX: */
8387 /* case LVN_INEDXTOID: */
8389 /* case LVN_MOVEGROUP: */
8391 /* case LVN_MOVEITEMTOGROUP: */
8393 case LVM_REDRAWITEMS:
8394 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8396 /* case LVN_REMOVEALLGROUPS: */
8398 /* case LVN_REMOVEGROUP: */
8400 case LVM_SCROLL:
8401 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8403 case LVM_SETBKCOLOR:
8404 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8406 /* case LVM_SETBKIMAGE: */
8408 case LVM_SETCALLBACKMASK:
8409 infoPtr->uCallbackMask = (UINT)wParam;
8410 return TRUE;
8412 case LVM_SETCOLUMNA:
8413 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8415 case LVM_SETCOLUMNW:
8416 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8418 case LVM_SETCOLUMNORDERARRAY:
8419 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8421 case LVM_SETCOLUMNWIDTH:
8422 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8424 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8425 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8427 /* case LVN_SETGROUPINFO: */
8429 /* case LVN_SETGROUPMETRICS: */
8431 case LVM_SETHOTCURSOR:
8432 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8434 case LVM_SETHOTITEM:
8435 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8437 case LVM_SETHOVERTIME:
8438 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8440 case LVM_SETICONSPACING:
8441 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8443 case LVM_SETIMAGELIST:
8444 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8446 /* case LVN_SETINFOTIP: */
8448 /* case LVN_SETINSERTMARK: */
8450 /* case LVN_SETINSERTMARKCOLOR: */
8452 case LVM_SETITEMA:
8453 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8455 case LVM_SETITEMW:
8456 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8458 case LVM_SETITEMCOUNT:
8459 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8461 case LVM_SETITEMPOSITION:
8463 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8464 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8467 case LVM_SETITEMPOSITION32:
8468 if (lParam == 0) return FALSE;
8469 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8471 case LVM_SETITEMSTATE:
8472 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8474 case LVM_SETITEMTEXTA:
8475 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8477 case LVM_SETITEMTEXTW:
8478 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8480 /* case LVN_SETOUTLINECOLOR: */
8482 /* case LVN_SETSELECTEDCOLUMN: */
8484 case LVM_SETSELECTIONMARK:
8485 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8487 case LVM_SETTEXTBKCOLOR:
8488 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8490 case LVM_SETTEXTCOLOR:
8491 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8493 /* case LVN_SETTILEINFO: */
8495 /* case LVN_SETTILEVIEWINFO: */
8497 /* case LVN_SETTILEWIDTH: */
8499 /* case LVM_SETTOOLTIPS: */
8501 /* case LVM_SETUNICODEFORMAT: */
8503 /* case LVN_SETVIEW: */
8505 /* case LVM_SETWORKAREAS: */
8507 /* case LVN_SORTGROUPS: */
8509 case LVM_SORTITEMS:
8510 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8512 case LVM_SUBITEMHITTEST:
8513 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8515 case LVM_UPDATE:
8516 return LISTVIEW_Update(infoPtr, (INT)wParam);
8518 case WM_CHAR:
8519 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8521 case WM_COMMAND:
8522 return LISTVIEW_Command(infoPtr, wParam, lParam);
8524 case WM_CREATE:
8525 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8527 case WM_ERASEBKGND:
8528 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8530 case WM_GETDLGCODE:
8531 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8533 case WM_GETFONT:
8534 return infoPtr->hFont;
8536 case WM_HSCROLL:
8537 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8539 case WM_KEYDOWN:
8540 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8542 case WM_KILLFOCUS:
8543 return LISTVIEW_KillFocus(infoPtr);
8545 case WM_LBUTTONDBLCLK:
8546 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8548 case WM_LBUTTONDOWN:
8549 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8551 case WM_LBUTTONUP:
8552 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8554 case WM_MOUSEMOVE:
8555 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8557 case WM_MOUSEHOVER:
8558 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8560 case WM_NCDESTROY:
8561 return LISTVIEW_NCDestroy(infoPtr);
8563 case WM_NOTIFY:
8564 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8566 case WM_NOTIFYFORMAT:
8567 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8569 case WM_PAINT:
8570 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8572 case WM_RBUTTONDBLCLK:
8573 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8575 case WM_RBUTTONDOWN:
8576 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8578 case WM_RBUTTONUP:
8579 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8581 case WM_SETCURSOR:
8582 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8583 return TRUE;
8584 goto fwd_msg;
8586 case WM_SETFOCUS:
8587 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8589 case WM_SETFONT:
8590 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8592 case WM_SETREDRAW:
8593 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8595 case WM_SIZE:
8596 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8598 case WM_STYLECHANGED:
8599 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8601 case WM_SYSCOLORCHANGE:
8602 COMCTL32_RefreshSysColors();
8603 return 0;
8605 /* case WM_TIMER: */
8607 case WM_VSCROLL:
8608 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8610 case WM_MOUSEWHEEL:
8611 if (wParam & (MK_SHIFT | MK_CONTROL))
8612 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8613 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8615 case WM_WINDOWPOSCHANGED:
8616 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8617 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8618 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8619 LISTVIEW_UpdateSize(infoPtr);
8620 LISTVIEW_UpdateScroll(infoPtr);
8622 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8624 /* case WM_WININICHANGE: */
8626 default:
8627 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8628 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8630 fwd_msg:
8631 /* call default window procedure */
8632 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8635 return 0;
8638 /***
8639 * DESCRIPTION:
8640 * Registers the window class.
8642 * PARAMETER(S):
8643 * None
8645 * RETURN:
8646 * None
8648 void LISTVIEW_Register(void)
8650 WNDCLASSW wndClass;
8652 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8653 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8654 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8655 wndClass.cbClsExtra = 0;
8656 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8657 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8658 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8659 wndClass.lpszClassName = WC_LISTVIEWW;
8660 RegisterClassW(&wndClass);
8663 /***
8664 * DESCRIPTION:
8665 * Unregisters the window class.
8667 * PARAMETER(S):
8668 * None
8670 * RETURN:
8671 * None
8673 void LISTVIEW_Unregister(void)
8675 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8678 /***
8679 * DESCRIPTION:
8680 * Handle any WM_COMMAND messages
8682 * PARAMETER(S):
8684 * RETURN:
8686 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8688 switch (HIWORD(wParam))
8690 case EN_UPDATE:
8693 * Adjust the edit window size
8695 WCHAR buffer[1024];
8696 HDC hdc = GetDC(infoPtr->hwndEdit);
8697 HFONT hFont, hOldFont = 0;
8698 RECT rect;
8699 SIZE sz;
8700 int len;
8702 if (!infoPtr->hwndEdit || !hdc) return 0;
8703 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8704 GetWindowRect(infoPtr->hwndEdit, &rect);
8706 /* Select font to get the right dimension of the string */
8707 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8708 if(hFont != 0)
8710 hOldFont = SelectObject(hdc, hFont);
8713 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8715 TEXTMETRICW textMetric;
8717 /* Add Extra spacing for the next character */
8718 GetTextMetricsW(hdc, &textMetric);
8719 sz.cx += (textMetric.tmMaxCharWidth * 2);
8721 SetWindowPos (
8722 infoPtr->hwndEdit,
8723 HWND_TOP,
8726 sz.cx,
8727 rect.bottom - rect.top,
8728 SWP_DRAWFRAME|SWP_NOMOVE);
8730 if(hFont != 0)
8731 SelectObject(hdc, hOldFont);
8733 ReleaseDC(infoPtr->hwndSelf, hdc);
8735 break;
8738 default:
8739 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8742 return 0;
8746 /***
8747 * DESCRIPTION:
8748 * Subclassed edit control windproc function
8750 * PARAMETER(S):
8752 * RETURN:
8754 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
8755 WPARAM wParam, LPARAM lParam, BOOL isW)
8757 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8758 BOOL cancel = FALSE;
8760 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8761 hwnd, uMsg, wParam, lParam, isW);
8763 switch (uMsg)
8765 case WM_GETDLGCODE:
8766 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8768 case WM_KILLFOCUS:
8769 break;
8771 case WM_DESTROY:
8773 WNDPROC editProc = infoPtr->EditWndProc;
8774 infoPtr->EditWndProc = 0;
8775 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
8776 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
8779 case WM_KEYDOWN:
8780 if (VK_ESCAPE == (INT)wParam)
8782 cancel = TRUE;
8783 break;
8785 else if (VK_RETURN == (INT)wParam)
8786 break;
8788 default:
8789 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
8792 /* kill the edit */
8793 if (infoPtr->hwndEdit)
8795 LPWSTR buffer = NULL;
8797 infoPtr->hwndEdit = 0;
8798 if (!cancel)
8800 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
8802 if (len)
8804 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
8806 if (isW) GetWindowTextW(hwnd, buffer, len+1);
8807 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
8811 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
8813 if (buffer) COMCTL32_Free(buffer);
8817 SendMessageW(hwnd, WM_CLOSE, 0, 0);
8818 return TRUE;
8821 /***
8822 * DESCRIPTION:
8823 * Subclassed edit control windproc function
8825 * PARAMETER(S):
8827 * RETURN:
8829 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8831 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
8834 /***
8835 * DESCRIPTION:
8836 * Subclassed edit control windproc function
8838 * PARAMETER(S):
8840 * RETURN:
8842 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8844 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
8847 /***
8848 * DESCRIPTION:
8849 * Creates a subclassed edit cotrol
8851 * PARAMETER(S):
8853 * RETURN:
8855 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
8856 INT x, INT y, INT width, INT height, BOOL isW)
8858 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
8859 HWND hedit;
8860 SIZE sz;
8861 HDC hdc;
8862 HDC hOldFont=0;
8863 TEXTMETRICW textMetric;
8864 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
8866 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
8868 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
8869 hdc = GetDC(infoPtr->hwndSelf);
8871 /* Select the font to get appropriate metric dimensions */
8872 if(infoPtr->hFont != 0)
8873 hOldFont = SelectObject(hdc, infoPtr->hFont);
8875 /*Get String Lenght in pixels */
8876 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
8878 /*Add Extra spacing for the next character */
8879 GetTextMetricsW(hdc, &textMetric);
8880 sz.cx += (textMetric.tmMaxCharWidth * 2);
8882 if(infoPtr->hFont != 0)
8883 SelectObject(hdc, hOldFont);
8885 ReleaseDC(infoPtr->hwndSelf, hdc);
8886 if (isW)
8887 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8888 else
8889 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8891 if (!hedit) return 0;
8893 infoPtr->EditWndProc = (WNDPROC)
8894 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
8895 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
8897 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
8899 return hedit;