Fix bug in ranges_shift which was corrupting selections.
[wine/hacks.git] / dlls / comctl32 / listview.c
blob6e97457c79f690675a354448edcf2b5276e6f9bf
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 BOOL bEditing;
171 WNDPROC EditWndProc;
172 INT nEditLabelItem;
173 DWORD dwHoverTime;
175 DWORD lastKeyPressTimestamp;
176 WPARAM charCode;
177 INT nSearchParamLength;
178 WCHAR szSearchParam[ MAX_PATH ];
179 BOOL bIsDrawing;
180 } LISTVIEW_INFO;
183 * constants
185 /* How many we debug buffer to allocate */
186 #define DEBUG_BUFFERS 20
187 /* The size of a single debug bbuffer */
188 #define DEBUG_BUFFER_SIZE 256
190 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
191 #define SB_INTERNAL -1
193 /* maximum size of a label */
194 #define DISP_TEXT_SIZE 512
196 /* padding for items in list and small icon display modes */
197 #define WIDTH_PADDING 12
199 /* padding for items in list, report and small icon display modes */
200 #define HEIGHT_PADDING 1
202 /* offset of items in report display mode */
203 #define REPORT_MARGINX 2
205 /* padding for icon in large icon display mode
206 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
207 * that HITTEST will see.
208 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
209 * ICON_TOP_PADDING - sum of the two above.
210 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
211 * LABEL_VERT_PADDING - between bottom of text and end of box
213 * ICON_LR_PADDING - additional width above icon size.
214 * ICON_LR_HALF - half of the above value
216 #define ICON_TOP_PADDING_NOTHITABLE 2
217 #define ICON_TOP_PADDING_HITABLE 2
218 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
219 #define ICON_BOTTOM_PADDING 4
220 #define LABEL_VERT_PADDING 7
221 #define ICON_LR_PADDING 16
222 #define ICON_LR_HALF (ICON_LR_PADDING/2)
224 /* default label width for items in list and small icon display modes */
225 #define DEFAULT_LABEL_WIDTH 40
227 /* default column width for items in list display mode */
228 #define DEFAULT_COLUMN_WIDTH 128
230 /* Size of "line" scroll for V & H scrolls */
231 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
233 /* Padding betwen image and label */
234 #define IMAGE_PADDING 2
236 /* Padding behind the label */
237 #define TRAILING_PADDING 5
239 /* Border for the icon caption */
240 #define CAPTION_BORDER 2
242 /* Standard DrawText flags */
243 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
244 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
245 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
247 /* The time in milisecods to reset the search in the list */
248 #define KEY_DELAY 450
250 /* Dump the LISTVIEW_INFO structure to the debug channel */
251 #define LISTVIEW_DUMP(iP) do { \
252 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
253 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
254 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
255 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
256 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
257 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
258 (iP->bFocus) ? "true" : "false"); \
259 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
260 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
261 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
262 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
263 iP->hwndSelf, \
264 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
265 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
266 } while(0)
270 * forward declarations
272 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
273 static BOOL LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
274 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
275 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
276 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
277 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *);
278 static BOOL LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
279 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
280 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
281 static INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *);
282 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
283 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *, INT);
284 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
285 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
286 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
287 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
288 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, POINT);
289 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
290 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
291 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
292 static void LISTVIEW_UnsupportedStyles(LONG);
293 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
294 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
295 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
296 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
297 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
298 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
299 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
300 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
301 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
302 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
303 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
304 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
305 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
307 /******** Text handling functions *************************************/
309 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
310 * text string. The string may be ANSI or Unicode, in which case
311 * the boolean isW tells us the type of the string.
313 * The name of the function tell what type of strings it expects:
314 * W: Unicode, T: ANSI/Unicode - function of isW
317 static inline BOOL is_textW(LPCWSTR text)
319 return text != NULL && text != LPSTR_TEXTCALLBACKW;
322 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
324 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
325 return is_textW(text);
328 static inline int textlenT(LPCWSTR text, BOOL isW)
330 return !is_textT(text, isW) ? 0 :
331 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
334 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
336 if (isDestW)
337 if (isSrcW) lstrcpynW(dest, src, max);
338 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
339 else
340 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
341 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
344 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
346 LPWSTR wstr = (LPWSTR)text;
348 if (!isW && is_textT(text, isW))
350 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
351 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
352 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
354 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
355 return wstr;
358 static inline void textfreeT(LPWSTR wstr, BOOL isW)
360 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
364 * dest is a pointer to a Unicode string
365 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
367 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
369 BOOL bResult = TRUE;
371 if (src == LPSTR_TEXTCALLBACKW)
373 if (is_textW(*dest)) COMCTL32_Free(*dest);
374 *dest = LPSTR_TEXTCALLBACKW;
376 else
378 LPWSTR pszText = textdupTtoW(src, isW);
379 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
380 bResult = Str_SetPtrW(dest, pszText);
381 textfreeT(pszText, isW);
383 return bResult;
387 * compares a Unicode to a Unicode/ANSI text string
389 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
391 if (!aw) return bt ? -1 : 0;
392 if (!bt) return aw ? 1 : 0;
393 if (aw == LPSTR_TEXTCALLBACKW)
394 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
395 if (bt != LPSTR_TEXTCALLBACKW)
397 LPWSTR bw = textdupTtoW(bt, isW);
398 int r = bw ? lstrcmpW(aw, bw) : 1;
399 textfreeT(bw, isW);
400 return r;
403 return 1;
406 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
408 int res;
410 n = min(min(n, strlenW(s1)), strlenW(s2));
411 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
412 return res ? res - sizeof(WCHAR) : res;
415 /******** Debugging functions *****************************************/
417 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
419 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
420 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
423 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
425 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
426 n = min(textlenT(text, isW), n);
427 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
430 static char* debug_getbuf()
432 static int index = 0;
433 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
434 return buffers[index++ % DEBUG_BUFFERS];
437 static inline char* debugpoint(const POINT* lppt)
439 if (lppt)
441 char* buf = debug_getbuf();
442 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
443 return buf;
444 } else return "(null)";
447 static inline char* debugrect(const RECT* rect)
449 if (rect)
451 char* buf = debug_getbuf();
452 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
453 rect->left, rect->top, rect->right, rect->bottom);
454 return buf;
455 } else return "(null)";
458 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
460 char* buf = debug_getbuf(), *text = buf;
461 int len, size = DEBUG_BUFFER_SIZE;
463 if (lpLVItem == NULL) return "(null)";
464 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
465 if (len == -1) goto end; buf += len; size -= len;
466 if (lpLVItem->mask & LVIF_STATE)
467 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
468 else len = 0;
469 if (len == -1) goto end; buf += len; size -= len;
470 if (lpLVItem->mask & LVIF_TEXT)
471 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
472 else len = 0;
473 if (len == -1) goto end; buf += len; size -= len;
474 if (lpLVItem->mask & LVIF_IMAGE)
475 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
476 else len = 0;
477 if (len == -1) goto end; buf += len; size -= len;
478 if (lpLVItem->mask & LVIF_PARAM)
479 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
480 else len = 0;
481 if (len == -1) goto end; buf += len; size -= len;
482 if (lpLVItem->mask & LVIF_INDENT)
483 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
484 else len = 0;
485 if (len == -1) goto end; buf += len; size -= len;
486 goto undo;
487 end:
488 buf = text + strlen(text);
489 undo:
490 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
491 return text;
494 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
496 char* buf = debug_getbuf(), *text = buf;
497 int len, size = DEBUG_BUFFER_SIZE;
499 if (lpColumn == NULL) return "(null)";
500 len = snprintf(buf, size, "{");
501 if (len == -1) goto end; buf += len; size -= len;
502 if (lpColumn->mask & LVCF_SUBITEM)
503 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
504 else len = 0;
505 if (len == -1) goto end; buf += len; size -= len;
506 if (lpColumn->mask & LVCF_FMT)
507 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
508 else len = 0;
509 if (len == -1) goto end; buf += len; size -= len;
510 if (lpColumn->mask & LVCF_WIDTH)
511 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
512 else len = 0;
513 if (len == -1) goto end; buf += len; size -= len;
514 if (lpColumn->mask & LVCF_TEXT)
515 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
516 else len = 0;
517 if (len == -1) goto end; buf += len; size -= len;
518 if (lpColumn->mask & LVCF_IMAGE)
519 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
520 else len = 0;
521 if (len == -1) goto end; buf += len; size -= len;
522 if (lpColumn->mask & LVCF_ORDER)
523 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
524 else len = 0;
525 if (len == -1) goto end; buf += len; size -= len;
526 goto undo;
527 end:
528 buf = text + strlen(text);
529 undo:
530 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
531 return text;
535 /******** Notification functions i************************************/
537 static inline BOOL notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
539 pnmh->hwndFrom = infoPtr->hwndSelf;
540 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
541 pnmh->code = code;
542 return (BOOL)SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
543 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
546 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code)
548 NMHDR nmh;
549 return notify_hdr(infoPtr, code, &nmh);
552 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
554 notify(infoPtr, LVN_ITEMACTIVATE);
557 static inline BOOL notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
559 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
562 static BOOL notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
564 NMLISTVIEW nmlv;
566 ZeroMemory(&nmlv, sizeof(nmlv));
567 nmlv.iItem = lvht->iItem;
568 nmlv.iSubItem = lvht->iSubItem;
569 nmlv.ptAction = lvht->pt;
570 return notify_listview(infoPtr, code, &nmlv);
573 static int get_ansi_notification(INT unicodeNotificationCode)
575 switch (unicodeNotificationCode)
577 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
578 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
579 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
580 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
581 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
582 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
584 ERR("unknown notification %x\n", unicodeNotificationCode);
585 return unicodeNotificationCode;
589 Send notification. depends on dispinfoW having same
590 structure as dispinfoA.
591 infoPtr : listview struct
592 notificationCode : *Unicode* notification code
593 pdi : dispinfo structure (can be unicode or ansi)
594 isW : TRUE if dispinfo is Unicode
596 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
598 BOOL bResult = FALSE;
599 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
600 INT realNotifCode;
601 INT cchTempBufMax = 0, savCchTextMax = 0;
602 LPWSTR pszTempBuf = NULL, savPszText = NULL;
604 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
606 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
607 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
610 if (convertToAnsi || convertToUnicode)
612 if (notificationCode != LVN_GETDISPINFOW)
614 cchTempBufMax = convertToUnicode ?
615 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
616 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
618 else
620 cchTempBufMax = pdi->item.cchTextMax;
621 *pdi->item.pszText = 0; /* make sure we don't process garbage */
624 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
625 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
626 if (!pszTempBuf) return FALSE;
627 if (convertToUnicode)
628 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
629 pszTempBuf, cchTempBufMax);
630 else
631 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
632 cchTempBufMax, NULL, NULL);
633 savCchTextMax = pdi->item.cchTextMax;
634 savPszText = pdi->item.pszText;
635 pdi->item.pszText = pszTempBuf;
636 pdi->item.cchTextMax = cchTempBufMax;
639 if (infoPtr->notifyFormat == NFR_ANSI)
640 realNotifCode = get_ansi_notification(notificationCode);
641 else
642 realNotifCode = notificationCode;
643 bResult = notify_hdr(infoPtr, realNotifCode, (LPNMHDR)pdi);
645 if (convertToUnicode || convertToAnsi)
647 if (convertToUnicode) /* note : pointer can be changed by app ! */
648 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
649 savCchTextMax, NULL, NULL);
650 else
651 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
652 savPszText, savCchTextMax);
653 pdi->item.pszText = savPszText; /* restores our buffer */
654 pdi->item.cchTextMax = savCchTextMax;
655 HeapFree(GetProcessHeap(), 0, pszTempBuf);
657 return bResult;
660 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc, LPRECT rcBounds, LVITEMW *lpLVItem)
662 BOOL isSelected;
664 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
665 lpnmlvcd->nmcd.hdc = hdc;
666 lpnmlvcd->nmcd.rc = *rcBounds;
667 if (lpLVItem)
669 lpnmlvcd->nmcd.dwItemSpec = lpLVItem->iItem;
670 lpnmlvcd->iSubItem = lpLVItem->iSubItem;
671 if (lpLVItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
672 if (lpLVItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
673 if (lpLVItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
674 lpnmlvcd->nmcd.lItemlParam = lpLVItem->lParam;
677 isSelected = lpnmlvcd->nmcd.uItemState & CDIS_SELECTED;
678 /* subitems are selected only in full-row-select, report mode */
679 if ( lpnmlvcd->iSubItem != 0 &&
680 !((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
681 (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) )
682 isSelected = FALSE;
684 if (isSelected && infoPtr->bFocus)
686 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
687 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
689 else if (isSelected && (infoPtr->dwStyle & LVS_SHOWSELALWAYS))
691 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
692 lpnmlvcd->clrText = comctl32_color.clrBtnText;
694 else
696 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
697 lpnmlvcd->clrText = infoPtr->clrText;
701 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
703 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
704 return notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
707 /******** Item iterator functions **********************************/
709 static BOOL ranges_add(HDPA ranges, RANGE range);
710 static BOOL ranges_del(HDPA ranges, RANGE range);
711 static void ranges_dump(HDPA ranges);
713 /***
714 * ITERATOR DOCUMENTATION
716 * The iterator functions allow for easy, and convenient iteration
717 * over items of iterest in the list. Typically, you create a
718 * iterator, use it, and destroy it, as such:
719 * ITERATOR i;
721 * iterator_xxxitems(&i, ...);
722 * while (iterator_{prev,next}(&i)
724 * //code which uses i.nItem
726 * iterator_destroy(&i);
728 * where xxx is either: framed, or visible.
729 * Note that it is important that the code destroys the iterator
730 * after it's done with it, as the creation of the iterator may
731 * allocate memory, which thus needs to be freed.
733 * You can iterate both forwards, and backwards through the list,
734 * by using iterator_next or iterator_prev respectively.
736 * Lower numbered items are draw on top of higher number items in
737 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
738 * items may overlap). So, to test items, you should use
739 * iterator_next
740 * which lists the items top to bottom (in Z-order).
741 * For drawing items, you should use
742 * iterator_prev
743 * which lists the items bottom to top (in Z-order).
744 * If you keep iterating over the items after the end-of-items
745 * marker (-1) is returned, the iterator will start from the
746 * beginning. Typically, you don't need to test for -1,
747 * because iterator_{next,prev} will return TRUE if more items
748 * are to be iterated over, or FALSE otherwise.
750 * Note: the iterator is defined to be bidirectional. That is,
751 * any number of prev followed by any number of next, or
752 * five versa, should leave the iterator at the same item:
753 * prev * n, next * n = next * n, prev * n
755 * The iterator has a notion of a out-of-order, special item,
756 * which sits at the start of the list. This is used in
757 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
758 * which needs to be first, as it may overlap other items.
760 * The code is a bit messy because we have:
761 * - a special item to deal with
762 * - simple range, or composite range
763 * - empty range.
764 * If find bugs, or want to add features, please make sure you
765 * always check/modify *both* iterator_prev, and iterator_next.
768 /****
769 * This function iterates through the items in increasing order,
770 * but prefixed by the special item, then -1. That is:
771 * special, 1, 2, 3, ..., n, -1.
772 * Each item is listed only once.
774 static inline BOOL iterator_next(ITERATOR* i)
776 if (i->nItem == -1)
778 i->nItem = i->nSpecial;
779 if (i->nItem != -1) return TRUE;
781 if (i->nItem == i->nSpecial)
783 if (i->ranges) i->index = 0;
784 goto pickarange;
787 i->nItem++;
788 testitem:
789 if (i->nItem == i->nSpecial) i->nItem++;
790 if (i->nItem <= i->range.upper) return TRUE;
792 pickarange:
793 if (i->ranges)
795 if (i->index < i->ranges->nItemCount)
796 i->range = *(RANGE*)DPA_GetPtr(i->ranges, i->index++);
797 else goto end;
799 else if (i->nItem > i->range.upper) goto end;
801 i->nItem = i->range.lower;
802 if (i->nItem >= 0) goto testitem;
803 end:
804 i->nItem = -1;
805 return FALSE;
808 /****
809 * This function iterates through the items in decreasing order,
810 * followed by the special item, then -1. That is:
811 * n, n-1, ..., 3, 2, 1, special, -1.
812 * Each item is listed only once.
814 static inline BOOL iterator_prev(ITERATOR* i)
816 BOOL start = FALSE;
818 if (i->nItem == -1)
820 start = TRUE;
821 if (i->ranges) i->index = i->ranges->nItemCount;
822 goto pickarange;
824 if (i->nItem == i->nSpecial)
826 i->nItem = -1;
827 return FALSE;
830 i->nItem--;
831 testitem:
832 if (i->nItem == i->nSpecial) i->nItem--;
833 if (i->nItem >= i->range.lower) return TRUE;
835 pickarange:
836 if (i->ranges)
838 if (i->index > 0)
839 i->range = *(RANGE*)DPA_GetPtr(i->ranges, --i->index);
840 else goto end;
842 else if (!start && i->nItem < i->range.lower) goto end;
844 i->nItem = i->range.upper;
845 if (i->nItem >= 0) goto testitem;
846 end:
847 return (i->nItem = i->nSpecial) != -1;
850 static RANGE iterator_range(ITERATOR* i)
852 RANGE range;
854 if (!i->ranges) return i->range;
856 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges, 0)).lower;
857 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges, i->ranges->nItemCount - 1)).upper;
858 return range;
861 /***
862 * Releases resources associated with this ierator.
864 static inline void iterator_destroy(ITERATOR* i)
866 if (i->ranges) DPA_Destroy(i->ranges);
869 /***
870 * Create an empty iterator.
872 static inline BOOL iterator_empty(ITERATOR* i)
874 ZeroMemory(i, sizeof(*i));
875 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
876 return TRUE;
879 /***
880 * Creates an iterator over the items which intersect lprc.
882 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
884 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
885 RECT frame = *lprc, rcItem, rcTemp;
886 POINT Origin;
888 /* in case we fail, we want to return an empty iterator */
889 if (!iterator_empty(i)) return FALSE;
891 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
893 OffsetRect(&frame, -Origin.x, -Origin.y);
895 if (uView == LVS_ICON || uView == LVS_SMALLICON)
897 INT nItem;
899 if (uView == LVS_ICON)
901 if (LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem) && IntersectRect(&rcTemp, &rcItem, lprc))
902 i->nSpecial = infoPtr->nFocusedItem;
904 if (!(i->ranges = DPA_Create(50))) return FALSE;
905 /* to do better here, we need to have PosX, and PosY sorted */
906 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
908 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
909 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
910 rcItem.right = rcItem.left + infoPtr->nItemWidth;
911 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
912 if (IntersectRect(&rcTemp, &rcItem, &frame))
914 RANGE item_range = { nItem, nItem };
915 ranges_add(i->ranges, item_range);
916 TRACE(" icon=%d\n", nItem);
919 return TRUE;
921 else if (uView == LVS_REPORT)
923 INT lower, upper;
925 if (frame.left >= infoPtr->nItemWidth) return TRUE;
926 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
928 lower = max(frame.top / infoPtr->nItemHeight, 0);
929 upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1);
930 if (upper < lower) return TRUE;
931 i->range.lower = lower;
932 i->range.upper = upper;
933 TRACE(" report=[%d,%d]\n", lower, upper);
935 else
937 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
938 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
939 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
940 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
941 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
942 INT lower = nFirstCol * nPerCol + nFirstRow;
943 RANGE item_range;
944 INT nCol;
946 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
947 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
949 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
951 if (!(i->ranges = DPA_Create(nLastCol - nFirstCol + 1))) return FALSE;
952 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
954 item_range.lower = nCol * nPerCol + nFirstRow;
955 if(item_range.lower >= infoPtr->nItemCount) break;
956 item_range.upper = min(nCol * nPerCol + nLastRow, infoPtr->nItemCount - 1);
957 TRACE(" list=[%d,%d]\n", item_range.lower, item_range.upper);
958 ranges_add(i->ranges, item_range);
962 return TRUE;
965 /***
966 * Creates an iterator over the items which intersect the visible region of hdc.
968 static BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
970 POINT Origin, Position;
971 RECT rcItem, rcClip;
972 INT rgntype;
974 rgntype = GetClipBox(hdc, &rcClip);
975 if (rgntype == NULLREGION) return iterator_empty(i);
976 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
977 if (rgntype == SIMPLEREGION) return TRUE;
979 /* first deal with the special item */
980 if (LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem) && !RectVisible(hdc, &rcItem))
981 i->nSpecial = -1;
983 /* if we can't deal with the region, we'll just go with the simple range */
984 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return TRUE;
985 if (!i->ranges)
987 if (!(i->ranges = DPA_Create(50))) return TRUE;
988 if (!ranges_add(i->ranges, i->range))
990 DPA_Destroy(i->ranges);
991 i->ranges = 0;
992 return TRUE;
996 /* now delete the invisible items from the list */
997 while(iterator_next(i))
999 if (!LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position)) continue;
1000 rcItem.left = Position.x + Origin.x;
1001 rcItem.top = Position.y + Origin.y;
1002 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1003 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1004 if (!RectVisible(hdc, &rcItem))
1006 RANGE item_range = { i->nItem, i->nItem };
1007 ranges_del(i->ranges, item_range);
1010 /* the iterator should restart on the next iterator_next */
1012 return TRUE;
1015 /******** Misc helper functions ************************************/
1017 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1018 WPARAM wParam, LPARAM lParam, BOOL isW)
1020 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1021 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1024 /******** Internal API functions ************************************/
1026 /* The Invalidate* are macros, so we preserve the caller location */
1027 #define LISTVIEW_InvalidateRect(infoPtr, rect) while(infoPtr->bRedraw) { \
1028 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
1029 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
1030 break;\
1033 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
1034 RECT rcBox; \
1035 if(LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox)) \
1036 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
1037 } while (0)
1039 #define LISTVIEW_InvalidateSubItem(infoPtr, nItem, nSubItem) do { \
1040 POINT Origin, Position; \
1041 RECT rcBox; \
1042 if (LISTVIEW_GetOrigin(infoPtr, &Origin) && \
1043 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position) && \
1044 Header_GetItemRect(infoPtr->hwndHeader, nSubItem, &rcBox)) { \
1045 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y); \
1046 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
1048 } while (0)
1050 #define LISTVIEW_InvalidateList(infoPtr)\
1051 LISTVIEW_InvalidateRect(infoPtr, NULL)
1053 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1055 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1058 /***
1059 * DESCRIPTION:
1060 * Retrieves the number of items that can fit vertically in the client area.
1062 * PARAMETER(S):
1063 * [I] infoPtr : valid pointer to the listview structure
1065 * RETURN:
1066 * Number of items per row.
1068 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1070 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1072 return max(nListWidth/infoPtr->nItemWidth, 1);
1075 /***
1076 * DESCRIPTION:
1077 * Retrieves the number of items that can fit horizontally in the client
1078 * area.
1080 * PARAMETER(S):
1081 * [I] infoPtr : valid pointer to the listview structure
1083 * RETURN:
1084 * Number of items per column.
1086 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1088 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1090 return max(nListHeight / infoPtr->nItemHeight, 1);
1094 /*************************************************************************
1095 * LISTVIEW_ProcessLetterKeys
1097 * Processes keyboard messages generated by pressing the letter keys
1098 * on the keyboard.
1099 * What this does is perform a case insensitive search from the
1100 * current position with the following quirks:
1101 * - If two chars or more are pressed in quick succession we search
1102 * for the corresponding string (e.g. 'abc').
1103 * - If there is a delay we wipe away the current search string and
1104 * restart with just that char.
1105 * - If the user keeps pressing the same character, whether slowly or
1106 * fast, so that the search string is entirely composed of this
1107 * character ('aaaaa' for instance), then we search for first item
1108 * that starting with that character.
1109 * - If the user types the above character in quick succession, then
1110 * we must also search for the corresponding string ('aaaaa'), and
1111 * go to that string if there is a match.
1113 * PARAMETERS
1114 * [I] hwnd : handle to the window
1115 * [I] charCode : the character code, the actual character
1116 * [I] keyData : key data
1118 * RETURNS
1120 * Zero.
1122 * BUGS
1124 * - The current implementation has a list of characters it will
1125 * accept and it ignores averything else. In particular it will
1126 * ignore accentuated characters which seems to match what
1127 * Windows does. But I'm not sure it makes sense to follow
1128 * Windows there.
1129 * - We don't sound a beep when the search fails.
1131 * SEE ALSO
1133 * TREEVIEW_ProcessLetterKeys
1135 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1137 INT nItem;
1138 INT endidx,idx;
1139 LVITEMW item;
1140 WCHAR buffer[MAX_PATH];
1141 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1143 /* simple parameter checking */
1144 if (!charCode || !keyData) return 0;
1146 /* only allow the valid WM_CHARs through */
1147 if (!isalnum(charCode) &&
1148 charCode != '.' && charCode != '`' && charCode != '!' &&
1149 charCode != '@' && charCode != '#' && charCode != '$' &&
1150 charCode != '%' && charCode != '^' && charCode != '&' &&
1151 charCode != '*' && charCode != '(' && charCode != ')' &&
1152 charCode != '-' && charCode != '_' && charCode != '+' &&
1153 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1154 charCode != '}' && charCode != '[' && charCode != '{' &&
1155 charCode != '/' && charCode != '?' && charCode != '>' &&
1156 charCode != '<' && charCode != ',' && charCode != '~')
1157 return 0;
1159 /* if there's one item or less, there is no where to go */
1160 if (infoPtr->nItemCount <= 1) return 0;
1162 /* update the search parameters */
1163 infoPtr->lastKeyPressTimestamp = GetTickCount();
1164 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1165 if (infoPtr->nSearchParamLength < MAX_PATH)
1166 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1167 if (infoPtr->charCode != charCode)
1168 infoPtr->charCode = charCode = 0;
1169 } else {
1170 infoPtr->charCode=charCode;
1171 infoPtr->szSearchParam[0]=charCode;
1172 infoPtr->nSearchParamLength=1;
1173 /* Redundant with the 1 char string */
1174 charCode=0;
1177 /* and search from the current position */
1178 nItem=-1;
1179 if (infoPtr->nFocusedItem >= 0) {
1180 endidx=infoPtr->nFocusedItem;
1181 idx=endidx;
1182 /* if looking for single character match,
1183 * then we must always move forward
1185 if (infoPtr->nSearchParamLength == 1)
1186 idx++;
1187 } else {
1188 endidx=infoPtr->nItemCount;
1189 idx=0;
1191 do {
1192 if (idx == infoPtr->nItemCount) {
1193 if (endidx == infoPtr->nItemCount || endidx == 0)
1194 break;
1195 idx=0;
1198 /* get item */
1199 item.mask = LVIF_TEXT;
1200 item.iItem = idx;
1201 item.iSubItem = 0;
1202 item.pszText = buffer;
1203 item.cchTextMax = MAX_PATH;
1204 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1206 /* check for a match */
1207 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1208 nItem=idx;
1209 break;
1210 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1211 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1212 /* This would work but we must keep looking for a longer match */
1213 nItem=idx;
1215 idx++;
1216 } while (idx != endidx);
1218 if (nItem != -1)
1219 LISTVIEW_KeySelection(infoPtr, nItem);
1221 return 0;
1224 /*************************************************************************
1225 * LISTVIEW_UpdateHeaderSize [Internal]
1227 * Function to resize the header control
1229 * PARAMS
1230 * hwnd [I] handle to a window
1231 * nNewScrollPos [I] Scroll Pos to Set
1233 * RETURNS
1234 * nothing
1236 * NOTES
1238 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1240 RECT winRect;
1241 POINT point[2];
1243 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1245 GetWindowRect(infoPtr->hwndHeader, &winRect);
1246 point[0].x = winRect.left;
1247 point[0].y = winRect.top;
1248 point[1].x = winRect.right;
1249 point[1].y = winRect.bottom;
1251 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1252 point[0].x = -nNewScrollPos;
1253 point[1].x += nNewScrollPos;
1255 SetWindowPos(infoPtr->hwndHeader,0,
1256 point[0].x,point[0].y,point[1].x,point[1].y,
1257 SWP_NOZORDER | SWP_NOACTIVATE);
1260 /***
1261 * DESCRIPTION:
1262 * Update the scrollbars. This functions should be called whenever
1263 * the content, size or view changes.
1265 * PARAMETER(S):
1266 * [I] infoPtr : valid pointer to the listview structure
1268 * RETURN:
1269 * None
1271 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1273 LONG lStyle = infoPtr->dwStyle;
1274 UINT uView = lStyle & LVS_TYPEMASK;
1275 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1276 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1277 SCROLLINFO scrollInfo;
1279 if (lStyle & LVS_NOSCROLL) return;
1281 scrollInfo.cbSize = sizeof(SCROLLINFO);
1283 if (uView == LVS_LIST)
1285 /* update horizontal scrollbar */
1286 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1287 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1289 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1290 infoPtr->nItemCount, nCountPerColumn, nCountPerRow);
1292 scrollInfo.nMin = 0;
1293 scrollInfo.nMax = infoPtr->nItemCount / nCountPerColumn;
1294 if((infoPtr->nItemCount % nCountPerColumn) == 0)
1295 scrollInfo.nMax--;
1296 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1297 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1298 scrollInfo.nPage = nCountPerRow;
1299 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1300 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1301 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1303 else if (uView == LVS_REPORT)
1305 BOOL test;
1307 /* update vertical scrollbar */
1308 scrollInfo.nMin = 0;
1309 scrollInfo.nMax = infoPtr->nItemCount - 1;
1310 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1311 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1312 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1313 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1314 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1315 scrollInfo.nMax, scrollInfo.nPage, test);
1316 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1317 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1319 /* update horizontal scrollbar */
1320 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1321 scrollInfo.fMask = SIF_POS;
1322 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1323 || infoPtr->nItemCount == 0)
1325 scrollInfo.nPos = 0;
1327 scrollInfo.nMin = 0;
1328 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1329 scrollInfo.nPage = nListWidth;
1330 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1331 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1332 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1333 scrollInfo.nMax, scrollInfo.nPage, test);
1334 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1335 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1337 /* Update the Header Control */
1338 scrollInfo.fMask = SIF_POS;
1339 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1340 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1343 else
1345 RECT rcView;
1347 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1349 INT nViewWidth = rcView.right - rcView.left;
1350 INT nViewHeight = rcView.bottom - rcView.top;
1352 /* Update Horizontal Scrollbar */
1353 scrollInfo.fMask = SIF_POS;
1354 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1355 || infoPtr->nItemCount == 0)
1357 scrollInfo.nPos = 0;
1359 scrollInfo.nMin = 0;
1360 scrollInfo.nMax = max(nViewWidth, 0)-1;
1361 scrollInfo.nPage = nListWidth;
1362 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1363 TRACE("LVS_ICON/SMALLICON Horz.\n");
1364 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1366 /* Update Vertical Scrollbar */
1367 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1368 scrollInfo.fMask = SIF_POS;
1369 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1370 || infoPtr->nItemCount == 0)
1372 scrollInfo.nPos = 0;
1374 scrollInfo.nMin = 0;
1375 scrollInfo.nMax = max(nViewHeight,0)-1;
1376 scrollInfo.nPage = nListHeight;
1377 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1378 TRACE("LVS_ICON/SMALLICON Vert.\n");
1379 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1385 /***
1386 * DESCRIPTION:
1387 * Shows/hides the focus rectangle.
1389 * PARAMETER(S):
1390 * [I] infoPtr : valid pointer to the listview structure
1391 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1393 * RETURN:
1394 * None
1396 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1398 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1399 HDC hdc;
1401 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1403 if (infoPtr->nFocusedItem < 0) return;
1405 /* we need some gymnastics in ICON mode to handle large items */
1406 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1408 RECT rcBox;
1410 if (!LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox))
1411 return;
1412 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1414 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1415 return;
1419 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1421 /* for some reason, owner draw should work only in report mode */
1422 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1424 DRAWITEMSTRUCT dis;
1425 LVITEMW item;
1427 item.iItem = infoPtr->nFocusedItem;
1428 item.iSubItem = 0;
1429 item.mask = LVIF_PARAM;
1430 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1432 ZeroMemory(&dis, sizeof(dis));
1433 dis.CtlType = ODT_LISTVIEW;
1434 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1435 dis.itemID = item.iItem;
1436 dis.itemAction = ODA_FOCUS;
1437 if (fShow) dis.itemState |= ODS_FOCUS;
1438 dis.hwndItem = infoPtr->hwndSelf;
1439 dis.hDC = hdc;
1440 if (!LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem)) goto done;
1441 dis.itemData = item.lParam;
1443 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1445 else
1447 DrawFocusRect(hdc, &infoPtr->rcFocus);
1449 done:
1450 ReleaseDC(infoPtr->hwndSelf, hdc);
1453 /***
1454 * Invalidates all visible selected items.
1456 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1458 ITERATOR i;
1460 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1461 while(iterator_next(&i))
1463 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1464 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1466 iterator_destroy(&i);
1470 /***
1471 * DESCRIPTION:
1472 * Prints a message for unsupported window styles.
1473 * A kind of TODO list for window styles.
1475 * PARAMETER(S):
1476 * [I] LONG : window style
1478 * RETURN:
1479 * None
1481 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1483 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1484 FIXME(" LVS_NOSCROLL\n");
1486 if (lStyle & LVS_NOLABELWRAP)
1487 FIXME(" LVS_NOLABELWRAP\n");
1489 if (lStyle & LVS_SORTASCENDING)
1490 FIXME(" LVS_SORTASCENDING\n");
1492 if (lStyle & LVS_SORTDESCENDING)
1493 FIXME(" LVS_SORTDESCENDING\n");
1497 /***
1498 * DESCRIPTION: [INTERNAL]
1499 * Computes an item's (left,top) corner, relative to rcView.
1500 * That is, the position has NOT been made relative to the Origin.
1501 * This is deliberate, to avoid computing the Origin over, and
1502 * over again, when this function is call in a loop. Instead,
1503 * one ca factor the computation of the Origin before the loop,
1504 * and offset the value retured by this function, on every iteration.
1506 * PARAMETER(S):
1507 * [I] infoPtr : valid pointer to the listview structure
1508 * [I] nItem : item number
1509 * [O] lpptOrig : item top, left corner
1511 * RETURN:
1512 * TRUE if computations OK
1513 * FALSE otherwise
1515 static BOOL LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1517 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1519 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
1521 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1523 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1524 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1526 else if (uView == LVS_LIST)
1528 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1529 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1530 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1532 else /* LVS_REPORT */
1534 lpptPosition->x = REPORT_MARGINX;
1535 lpptPosition->y = nItem * infoPtr->nItemHeight;
1538 return TRUE;
1541 /***
1542 * DESCRIPTION: [INTERNAL]
1543 * Compute the rectangles of an item. This is to localize all
1544 * the computations in one place. If you are not interested in some
1545 * of these values, simply pass in a NULL -- the fucntion is smart
1546 * enough to compute only what's necessary. The function computes
1547 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1548 * one, the BOX rectangle. This rectangle is very cheap to compute,
1549 * and is guaranteed to contain all the other rectangles. Computing
1550 * the ICON rect is also cheap, but all the others are potentaily
1551 * expensive. This gives an easy and effective optimization when
1552 * searching (like point inclusion, or rectangle intersection):
1553 * first test against the BOX, and if TRUE, test agains the desired
1554 * rectangle.
1555 * If the function does not have all the necessary information
1556 * to computed the requested rectangles, will crash with a
1557 * failed assertion. This is done so we catch all programming
1558 * errors, given that the function is called only from our code.
1560 * We have the following 'special' meanings for a few fields:
1561 * * If LVIS_FOCUSED is set, we assume the item has the focus
1562 * This is important in ICON mode, where it might get a larger
1563 * then usual rectange
1565 * Please note that subitem support works only in REPORT mode.
1567 * PARAMETER(S):
1568 * [I] infoPtr : valid pointer to the listview structure
1569 * [I] lpLVItem : item to compute the measures for
1570 * [O] lprcBox : ptr to Box rectangle
1571 * The internal LVIR_BOX rectangle
1572 * [0] lprcState : ptr to State icon rectangle
1573 * The internal LVIR_STATE rectangle
1574 * [O] lprcIcon : ptr to Icon rectangle
1575 * Same as LVM_GETITEMRECT with LVIR_ICON
1576 * [O] lprcLabel : ptr to Label rectangle
1577 * Same as LVM_GETITEMRECT with LVIR_LABEL
1579 * RETURN:
1580 * TRUE if computations OK
1581 * FALSE otherwise
1583 static BOOL LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem,
1584 LPRECT lprcBox, LPRECT lprcState,
1585 LPRECT lprcIcon, LPRECT lprcLabel)
1587 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1588 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1589 RECT Box, State, Icon, Label;
1591 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1593 /* Be smart and try to figure out the minimum we have to do */
1594 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1595 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1597 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1598 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1600 if (lprcLabel) doLabel = TRUE;
1601 if (doLabel || lprcIcon) doIcon = TRUE;
1602 if (doIcon || lprcState) doState = TRUE;
1604 /************************************************************/
1605 /* compute the box rectangle (it should be cheap to do) */
1606 /************************************************************/
1607 if (lpLVItem->iSubItem)
1609 if (!Header_GetItemRect(infoPtr->hwndHeader, lpLVItem->iSubItem, &Box)) return FALSE;
1611 else
1613 Box.left = 0;
1614 Box.right = infoPtr->nItemWidth;
1616 Box.top = 0;
1617 Box.bottom = infoPtr->nItemHeight;
1619 /************************************************************/
1620 /* compute STATEICON bounding box */
1621 /************************************************************/
1622 if (doState)
1624 if (uView == LVS_ICON)
1626 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1627 if (infoPtr->himlNormal)
1628 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1629 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1631 else
1633 /* we need the ident in report mode, if we don't have it, we fail */
1634 State.left = Box.left;
1635 if (uView == LVS_REPORT)
1637 State.left += REPORT_MARGINX;
1638 if (lpLVItem->iSubItem == 0)
1640 assert(lpLVItem->mask & LVIF_INDENT);
1641 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1644 State.top = Box.top;
1646 State.right = State.left;
1647 State.bottom = State.top;
1648 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1650 State.right += infoPtr->iconStateSize.cx;
1651 State.bottom += infoPtr->iconStateSize.cy;
1653 if (lprcState) *lprcState = State;
1654 TRACE(" - state=%s\n", debugrect(&State));
1657 /************************************************************/
1658 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1659 /************************************************************/
1660 if (doIcon)
1662 if (uView == LVS_ICON)
1664 Icon.left = Box.left;
1665 if (infoPtr->himlNormal)
1666 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1667 Icon.top = Box.top + ICON_TOP_PADDING;
1668 Icon.right = Icon.left;
1669 Icon.bottom = Icon.top;
1670 if (infoPtr->himlNormal)
1672 Icon.right += infoPtr->iconSize.cx;
1673 Icon.bottom += infoPtr->iconSize.cy;
1676 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1678 Icon.left = State.right;
1679 if (!IsRectEmpty(&State)) Icon.left += IMAGE_PADDING;
1680 Icon.top = Box.top;
1681 Icon.right = Icon.left;
1682 /* FIXME: add suport for icons for subitems */
1683 if (infoPtr->himlSmall && lpLVItem->iSubItem == 0) Icon.right += infoPtr->iconSize.cx;
1684 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1686 if(lprcIcon) *lprcIcon = Icon;
1687 TRACE(" - icon=%s\n", debugrect(&Icon));
1690 /************************************************************/
1691 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1692 /************************************************************/
1693 if (doLabel)
1695 SIZE labelSize = { 0, 0 };
1697 /* calculate how far to the right can the label strech */
1698 Label.right = Box.right;
1699 if (uView == LVS_REPORT)
1701 if (lpLVItem->iSubItem == 0 && !Header_GetItemRect(infoPtr->hwndHeader, 0, &Label)) return FALSE;
1702 Label.right -= REPORT_MARGINX;
1705 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1707 labelSize.cx = infoPtr->nItemWidth;
1708 labelSize.cy = infoPtr->nItemHeight;
1709 goto calc_label;
1712 /* we need the text in non owner draw mode */
1713 assert(lpLVItem->mask & LVIF_TEXT);
1714 if (is_textT(lpLVItem->pszText, TRUE))
1716 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1717 HDC hdc = GetDC(infoPtr->hwndSelf);
1718 HFONT hOldFont = SelectObject(hdc, hFont);
1719 UINT uFormat;
1720 RECT rcText;
1722 /* compute rough rectangle where the label will go */
1723 SetRectEmpty(&rcText);
1724 rcText.right = infoPtr->nItemWidth - TRAILING_PADDING;
1725 rcText.bottom = infoPtr->nItemHeight;
1726 if (uView == LVS_ICON)
1727 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1729 /* now figure out the flags */
1730 if (uView == LVS_ICON)
1731 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1732 else
1733 uFormat = LV_SL_DT_FLAGS;
1735 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1737 labelSize.cx = min(rcText.right - rcText.left + TRAILING_PADDING, infoPtr->nItemWidth);
1738 labelSize.cy = rcText.bottom - rcText.top;
1740 SelectObject(hdc, hOldFont);
1741 ReleaseDC(infoPtr->hwndSelf, hdc);
1744 calc_label:
1745 if (uView == LVS_ICON)
1747 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1748 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1749 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1750 Label.right = Label.left + labelSize.cx;
1751 Label.bottom = Label.top + infoPtr->nItemHeight;
1752 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1754 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1755 labelSize.cy /= infoPtr->ntmHeight;
1756 labelSize.cy = max(labelSize.cy, 1);
1757 labelSize.cy *= infoPtr->ntmHeight;
1759 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1761 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1763 Label.left = Icon.right;
1764 if (!IsRectEmpty(&Icon) || !IsRectEmpty(&State)) Label.left += IMAGE_PADDING;
1765 Label.top = Box.top;
1766 Label.right = min(Label.left + labelSize.cx, Label.right);
1767 Label.bottom = Label.top + infoPtr->nItemHeight;
1770 if (lprcLabel) *lprcLabel = Label;
1771 TRACE(" - label=%s\n", debugrect(&Label));
1774 /* Fix the Box if necessary */
1775 if (lprcBox)
1777 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1778 else *lprcBox = Box;
1780 TRACE(" - box=%s\n", debugrect(&Box));
1782 return TRUE;
1785 /***
1786 * DESCRIPTION: [INTERNAL]
1788 * PARAMETER(S):
1789 * [I] infoPtr : valid pointer to the listview structure
1790 * [I] nItem : item number
1791 * [O] lprcBox : ptr to Box rectangle
1793 * RETURN:
1794 * TRUE if computations OK
1795 * FALSE otherwise
1797 static BOOL LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
1799 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1800 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1801 POINT Position, Origin;
1802 LVITEMW lvItem;
1804 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) return FALSE;
1805 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
1807 /* Be smart and try to figure out the minimum we have to do */
1808 lvItem.mask = 0;
1809 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1810 lvItem.mask |= LVIF_TEXT;
1811 lvItem.iItem = nItem;
1812 lvItem.iSubItem = 0;
1813 lvItem.pszText = szDispText;
1814 lvItem.cchTextMax = DISP_TEXT_SIZE;
1815 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
1816 if (uView == LVS_ICON)
1818 lvItem.mask |= LVIF_STATE;
1819 lvItem.stateMask = LVIS_FOCUSED;
1820 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
1822 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0)) return FALSE;
1824 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
1826 return TRUE;
1829 /***
1830 * DESCRIPTION:
1831 * Aligns the items with the top edge of the window.
1833 * PARAMETER(S):
1834 * [I] infoPtr : valid pointer to the listview structure
1836 * RETURN:
1837 * None
1839 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1841 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1842 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1843 POINT ptItem;
1844 RECT rcView;
1845 INT i, off_x=0, off_y=0;
1847 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1849 /* Since SetItemPosition uses upper-left of icon, and for
1850 style=LVS_ICON the icon is not left adjusted, get the offset */
1851 if (uView == LVS_ICON)
1853 off_y = ICON_TOP_PADDING;
1854 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1856 ptItem.x = off_x;
1857 ptItem.y = off_y;
1858 ZeroMemory(&rcView, sizeof(RECT));
1859 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1860 off_x, off_y,
1861 infoPtr->rcList.left, infoPtr->rcList.right);
1863 if (nListWidth > infoPtr->nItemWidth)
1865 for (i = 0; i < infoPtr->nItemCount; i++)
1867 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1869 ptItem.x = off_x;
1870 ptItem.y += infoPtr->nItemHeight;
1873 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1874 ptItem.x += infoPtr->nItemWidth;
1875 rcView.right = max(rcView.right, ptItem.x);
1878 rcView.right -= off_x;
1879 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1881 else
1883 for (i = 0; i < infoPtr->nItemCount; i++)
1885 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1886 ptItem.y += infoPtr->nItemHeight;
1889 rcView.right = infoPtr->nItemWidth;
1890 rcView.bottom = ptItem.y-off_y;
1893 infoPtr->rcView = rcView;
1897 /***
1898 * DESCRIPTION:
1899 * Aligns the items with the left edge of the window.
1901 * PARAMETER(S):
1902 * [I] infoPtr : valid pointer to the listview structure
1904 * RETURN:
1905 * None
1907 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1909 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1910 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1911 POINT ptItem;
1912 RECT rcView;
1913 INT i, off_x=0, off_y=0;
1915 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1917 /* Since SetItemPosition uses upper-left of icon, and for
1918 style=LVS_ICON the icon is not left adjusted, get the offset */
1919 if (uView == LVS_ICON)
1921 off_y = ICON_TOP_PADDING;
1922 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1924 ptItem.x = off_x;
1925 ptItem.y = off_y;
1926 ZeroMemory(&rcView, sizeof(RECT));
1927 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1929 if (nListHeight > infoPtr->nItemHeight)
1931 for (i = 0; i < infoPtr->nItemCount; i++)
1933 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1935 ptItem.y = off_y;
1936 ptItem.x += infoPtr->nItemWidth;
1939 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1940 ptItem.y += infoPtr->nItemHeight;
1941 rcView.bottom = max(rcView.bottom, ptItem.y);
1944 rcView.right = ptItem.x + infoPtr->nItemWidth;
1946 else
1948 for (i = 0; i < infoPtr->nItemCount; i++)
1950 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1951 ptItem.x += infoPtr->nItemWidth;
1954 rcView.bottom = infoPtr->nItemHeight;
1955 rcView.right = ptItem.x;
1958 infoPtr->rcView = rcView;
1963 /***
1964 * DESCRIPTION:
1965 * Retrieves the bounding rectangle of all the items.
1967 * PARAMETER(S):
1968 * [I] infoPtr : valid pointer to the listview structure
1969 * [O] lprcView : bounding rectangle
1971 * RETURN:
1972 * SUCCESS : TRUE
1973 * FAILURE : FALSE
1975 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1977 POINT ptOrigin;
1979 TRACE("(lprcView=%p)\n", lprcView);
1981 if (!lprcView) return FALSE;
1983 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return FALSE;
1985 *lprcView = infoPtr->rcView;
1986 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
1988 TRACE("lprcView=%s\n", debugrect(lprcView));
1990 return TRUE;
1993 /***
1994 * DESCRIPTION:
1995 * Retrieves the subitem pointer associated with the subitem index.
1997 * PARAMETER(S):
1998 * [I] HDPA : DPA handle for a specific item
1999 * [I] INT : index of subitem
2001 * RETURN:
2002 * SUCCESS : subitem pointer
2003 * FAILURE : NULL
2005 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2007 LISTVIEW_SUBITEM *lpSubItem;
2008 INT i;
2010 /* we should binary search here if need be */
2011 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2013 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
2014 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
2015 return lpSubItem;
2018 return NULL;
2022 /***
2023 * DESCRIPTION:
2024 * Calculates the width of a specific item.
2026 * PARAMETER(S):
2027 * [I] infoPtr : valid pointer to the listview structure
2028 * [I] nItem : item to calculate width, or -1 for max of all
2030 * RETURN:
2031 * Returns the width of an item width an item.
2033 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr, INT nItem)
2035 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2036 INT nItemWidth = 0, i;
2038 if (uView == LVS_ICON)
2039 nItemWidth = infoPtr->iconSpacing.cx;
2040 else if (uView == LVS_REPORT)
2042 INT nHeaderItemCount;
2043 RECT rcHeaderItem;
2045 /* calculate width of header */
2046 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
2047 for (i = 0; i < nHeaderItemCount; i++)
2048 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem))
2049 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
2051 else
2053 INT nLabelWidth;
2055 if (infoPtr->nItemCount == 0) return DEFAULT_COLUMN_WIDTH;
2057 /* get width of string */
2058 if (nItem == -1)
2060 for (i = 0; i < infoPtr->nItemCount; i++)
2062 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
2063 nItemWidth = max(nItemWidth, nLabelWidth);
2066 else
2067 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
2068 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
2069 nItemWidth += WIDTH_PADDING;
2070 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2071 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2072 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
2075 return max(nItemWidth, 1);
2078 /***
2079 * DESCRIPTION:
2080 * Calculates the max width of any item in the list.
2082 * PARAMETER(S):
2083 * [I] infoPtr : valid pointer to the listview structure
2084 * [I] LONG : window style
2086 * RETURN:
2087 * Returns item width.
2089 static inline INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *infoPtr)
2091 return LISTVIEW_CalculateItemWidth(infoPtr, -1);
2094 /***
2095 * DESCRIPTION:
2096 * Retrieves and saves important text metrics info for the current
2097 * Listview font.
2099 * PARAMETER(S):
2100 * [I] infoPtr : valid pointer to the listview structure
2103 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2105 HDC hdc = GetDC(infoPtr->hwndSelf);
2106 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2107 HFONT hOldFont = SelectObject(hdc, hFont);
2108 TEXTMETRICW tm;
2110 if (GetTextMetricsW(hdc, &tm))
2111 infoPtr->ntmHeight = tm.tmHeight;
2112 SelectObject(hdc, hOldFont);
2113 ReleaseDC(infoPtr->hwndSelf, hdc);
2115 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2119 /***
2120 * DESCRIPTION:
2121 * Calculates the height of an item.
2123 * PARAMETER(S):
2124 * [I] infoPtr : valid pointer to the listview structure
2126 * RETURN:
2127 * Returns item height.
2129 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *infoPtr)
2131 INT nItemHeight;
2133 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
2134 nItemHeight = infoPtr->iconSpacing.cy;
2135 else
2137 nItemHeight = infoPtr->ntmHeight;
2138 if (infoPtr->himlState)
2139 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2140 if (infoPtr->himlSmall)
2141 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2142 if (infoPtr->himlState || infoPtr->himlSmall)
2143 nItemHeight += HEIGHT_PADDING;
2145 return nItemHeight;
2148 #if 0
2149 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
2151 ERR("Selections are:\n");
2152 ranges_dump(infoPtr->hdpaSelectionRanges);
2154 #endif
2156 /***
2157 * DESCRIPTION:
2158 * A compare function for ranges
2160 * PARAMETER(S)
2161 * [I] range1 : pointer to range 1;
2162 * [I] range2 : pointer to range 2;
2163 * [I] flags : flags
2165 * RETURNS:
2166 * >0 : if Item 1 > Item 2
2167 * <0 : if Item 2 > Item 1
2168 * 0 : if Item 1 == Item 2
2170 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2172 if (((RANGE*)range1)->upper < ((RANGE*)range2)->lower)
2173 return -1;
2174 if (((RANGE*)range2)->upper < ((RANGE*)range1)->lower)
2175 return 1;
2176 return 0;
2179 static void ranges_dump(HDPA ranges)
2181 INT i;
2183 for (i = 0; i < ranges->nItemCount; i++)
2185 RANGE *selection = DPA_GetPtr(ranges, i);
2186 TRACE(" [%d - %d]\n", selection->lower, selection->upper);
2190 static inline BOOL ranges_contain(HDPA ranges, INT nItem)
2192 RANGE srchrng = { nItem, nItem };
2194 TRACE("(nItem=%d)\n", nItem);
2195 if (TRACE_ON(listview)) ranges_dump(ranges);
2196 return DPA_Search(ranges, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2199 static BOOL ranges_shift(HDPA ranges, INT nItem, INT delta, INT nUpper)
2201 RANGE srchrng, *chkrng;
2202 INT index;
2204 srchrng.upper = nItem;
2205 srchrng.lower = nItem;
2207 index = DPA_Search(ranges, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2208 if (index == -1) return TRUE;
2210 for (;index < ranges->nItemCount; index++)
2212 chkrng = DPA_GetPtr(ranges, index);
2213 if (chkrng->lower >= nItem)
2214 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2215 if (chkrng->upper >= nItem)
2216 chkrng->upper = max(min(chkrng->upper + delta, nUpper - 1), 0);
2218 return TRUE;
2221 static BOOL ranges_add(HDPA ranges, RANGE range)
2223 RANGE srchrgn;
2224 INT index;
2226 TRACE("range=(%i - %i)\n", range.lower, range.upper);
2227 if (TRACE_ON(listview)) ranges_dump(ranges);
2229 /* try find overlapping regions first */
2230 srchrgn.lower = range.lower - 1;
2231 srchrgn.upper = range.upper + 1;
2232 index = DPA_Search(ranges, &srchrgn, 0, ranges_cmp, 0, 0);
2234 if (index == -1)
2236 RANGE *newrgn;
2238 TRACE("Adding new range\n");
2240 /* create the brand new range to insert */
2241 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2242 if(!newrgn) return FALSE;
2243 *newrgn = range;
2245 /* figure out where to insert it */
2246 index = DPA_Search(ranges, newrgn, 0, ranges_cmp, 0, DPAS_INSERTAFTER);
2247 if (index == -1) index = 0;
2249 /* and get it over with */
2250 DPA_InsertPtr(ranges, index, newrgn);
2252 else
2254 RANGE *chkrgn, *mrgrgn;
2255 INT fromindex, mergeindex;
2257 chkrgn = DPA_GetPtr(ranges, index);
2258 if (!chkrgn) return FALSE;
2259 TRACE("Merge with index %i (%d - %d)\n",
2260 index, chkrgn->lower, chkrgn->upper);
2262 chkrgn->lower = min(range.lower, chkrgn->lower);
2263 chkrgn->upper = max(range.upper, chkrgn->upper);
2265 TRACE("New range %i (%d - %d)\n",
2266 index, chkrgn->lower, chkrgn->upper);
2268 /* merge now common anges */
2269 fromindex = 0;
2270 srchrgn.lower = chkrgn->lower - 1;
2271 srchrgn.upper = chkrgn->upper + 1;
2275 mergeindex = DPA_Search(ranges, &srchrgn, fromindex, ranges_cmp, 0, 0);
2276 if (mergeindex == -1) break;
2277 if (mergeindex == index)
2279 fromindex = index + 1;
2280 continue;
2283 TRACE("Merge with index %i\n", mergeindex);
2285 mrgrgn = DPA_GetPtr(ranges, mergeindex);
2286 if (!mrgrgn) return FALSE;
2288 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2289 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2290 COMCTL32_Free(mrgrgn);
2291 DPA_DeletePtr(ranges, mergeindex);
2292 if (mergeindex < index) index --;
2293 } while(1);
2296 if (TRACE_ON(listview)) ranges_dump(ranges);
2297 return TRUE;
2300 static BOOL ranges_del(HDPA ranges, RANGE range)
2302 RANGE remrgn, tmprgn, *chkrgn;
2303 BOOL done = FALSE;
2304 INT index;
2306 TRACE("range: (%d - %d)\n", range.lower, range.upper);
2308 remrgn = range;
2311 index = DPA_Search(ranges, &remrgn, 0, ranges_cmp, 0, 0);
2312 if (index == -1) return TRUE;
2314 chkrgn = DPA_GetPtr(ranges, index);
2315 if (!chkrgn) return FALSE;
2317 TRACE("Matches range index %i (%d - %d)\n",
2318 index, chkrgn->lower, chkrgn->upper);
2320 /* case 1: Same range */
2321 if ( (chkrgn->upper == remrgn.upper) &&
2322 (chkrgn->lower == remrgn.lower) )
2324 DPA_DeletePtr(ranges, index);
2325 done = TRUE;
2327 /* case 2: engulf */
2328 else if ( (chkrgn->upper <= remrgn.upper) &&
2329 (chkrgn->lower >= remrgn.lower) )
2331 DPA_DeletePtr(ranges, index);
2333 /* case 3: overlap upper */
2334 else if ( (chkrgn->upper <= remrgn.upper) &&
2335 (chkrgn->lower < remrgn.lower) )
2337 chkrgn->upper = remrgn.lower - 1;
2339 /* case 4: overlap lower */
2340 else if ( (chkrgn->upper > remrgn.upper) &&
2341 (chkrgn->lower >= remrgn.lower) )
2343 chkrgn->lower = remrgn.upper + 1;
2345 /* case 5: fully internal */
2346 else
2348 RANGE *newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2349 if (!newrgn) return FALSE;
2350 tmprgn = *chkrgn;
2351 newrgn->lower = chkrgn->lower;
2352 newrgn->upper = remrgn.lower - 1;
2353 chkrgn->lower = remrgn.upper + 1;
2354 DPA_InsertPtr(ranges, index, newrgn);
2355 chkrgn = &tmprgn;
2358 while(!done);
2360 return TRUE;
2363 /***
2364 * DESCRIPTION:
2365 * Removes all selection ranges
2367 * Parameters(s):
2368 * [I] infoPtr : valid pointer to the listview structure
2370 * RETURNS:
2371 * SUCCESS : TRUE
2372 * FAILURE : TRUE
2374 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr)
2376 LVITEMW lvItem;
2377 RANGE *sel;
2378 INT i;
2380 if (infoPtr->bRemovingAllSelections) return TRUE;
2382 infoPtr->bRemovingAllSelections = TRUE;
2384 TRACE("()\n");
2386 lvItem.state = 0;
2387 lvItem.stateMask = LVIS_SELECTED;
2391 sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, 0);
2392 if (!sel) continue;
2393 for(i = sel->lower; i <= sel->upper; i++)
2394 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
2396 while (infoPtr->hdpaSelectionRanges->nItemCount > 0);
2398 infoPtr->bRemovingAllSelections = FALSE;
2400 return TRUE;
2403 /***
2404 * DESCRIPTION:
2405 * Retrieves the number of items that are marked as selected.
2407 * PARAMETER(S):
2408 * [I] infoPtr : valid pointer to the listview structure
2410 * RETURN:
2411 * Number of items selected.
2413 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2415 INT i, nSelectedCount = 0;
2417 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2419 INT i;
2420 for (i = 0; i < infoPtr->nItemCount; i++)
2422 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2423 nSelectedCount++;
2426 else
2428 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
2430 RANGE *sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
2431 nSelectedCount += sel->upper - sel->lower + 1;
2435 TRACE("nSelectedCount=%d\n", nSelectedCount);
2436 return nSelectedCount;
2439 /***
2440 * DESCRIPTION:
2441 * Manages the item focus.
2443 * PARAMETER(S):
2444 * [I] infoPtr : valid pointer to the listview structure
2445 * [I] INT : item index
2447 * RETURN:
2448 * TRUE : focused item changed
2449 * FALSE : focused item has NOT changed
2451 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2453 INT oldFocus = infoPtr->nFocusedItem;
2454 LVITEMW lvItem;
2456 lvItem.state = LVIS_FOCUSED;
2457 lvItem.stateMask = LVIS_FOCUSED;
2458 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2460 return oldFocus != infoPtr->nFocusedItem;
2463 /* Helper function for LISTVIEW_ShiftIndices *only* */
2464 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2466 if (nShiftItem < nItem) return nShiftItem;
2468 if (nShiftItem > nItem) return nShiftItem + direction;
2470 if (direction > 0) return nShiftItem + direction;
2472 return min(nShiftItem, infoPtr->nItemCount - 1);
2476 * DESCRIPTION:
2477 * Updates the various indices after an item has been inserted or deleted.
2479 * PARAMETER(S):
2480 * [I] infoPtr : valid pointer to the listview structure
2481 * [I] nItem : item index
2482 * [I] direction : Direction of shift, +1 or -1.
2484 * RETURN:
2485 * None
2487 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2489 INT nNewFocus;
2491 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2493 ranges_shift(infoPtr->hdpaSelectionRanges, nItem, direction, infoPtr->nItemCount);
2495 assert(abs(direction) == 1);
2497 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2499 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2500 if (nNewFocus != infoPtr->nFocusedItem)
2501 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2503 /* But we are not supposed to modify nHotItem! */
2508 * DESCRIPTION:
2509 * Adds a block of selections.
2511 * PARAMETER(S):
2512 * [I] infoPtr : valid pointer to the listview structure
2513 * [I] INT : item index
2515 * RETURN:
2516 * None
2518 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2520 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2521 INT nLast = max(infoPtr->nSelectionMark, nItem);
2522 INT i;
2523 LVITEMW item;
2525 if (nFirst == -1)
2526 nFirst = nItem;
2528 item.state = LVIS_SELECTED;
2529 item.stateMask = LVIS_SELECTED;
2531 /* FIXME: this is not correct LVS_OWNERDATA
2532 * See docu for LVN_ITEMCHANGED. Is there something similar for
2533 * RemoveGroupSelection (is there such a thing?)?
2535 for (i = nFirst; i <= nLast; i++)
2536 LISTVIEW_SetItemState(infoPtr,i,&item);
2538 LISTVIEW_SetItemFocus(infoPtr, nItem);
2539 infoPtr->nSelectionMark = nItem;
2543 /***
2544 * DESCRIPTION:
2545 * Sets a single group selection.
2547 * PARAMETER(S):
2548 * [I] infoPtr : valid pointer to the listview structure
2549 * [I] INT : item index
2551 * RETURN:
2552 * None
2554 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2556 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2557 INT i;
2558 LVITEMW item;
2559 POINT ptItem;
2560 RECT rcSel;
2562 LISTVIEW_RemoveAllSelections(infoPtr);
2564 item.state = LVIS_SELECTED;
2565 item.stateMask = LVIS_SELECTED;
2567 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2569 INT nFirst, nLast;
2571 if (infoPtr->nSelectionMark == -1)
2572 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2573 else
2575 nFirst = min(infoPtr->nSelectionMark, nItem);
2576 nLast = max(infoPtr->nSelectionMark, nItem);
2578 for (i = nFirst; i <= nLast; i++)
2579 LISTVIEW_SetItemState(infoPtr, i, &item);
2581 else
2583 RECT rcItem, rcSelMark;
2584 ITERATOR i;
2586 rcItem.left = LVIR_BOUNDS;
2587 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2588 rcSelMark.left = LVIR_BOUNDS;
2589 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2590 UnionRect(&rcSel, &rcItem, &rcSelMark);
2591 iterator_frameditems(&i, infoPtr, &rcSel);
2592 while(iterator_next(&i))
2594 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
2595 if (PtInRect(&rcSel, ptItem))
2596 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
2598 iterator_destroy(&i);
2601 LISTVIEW_SetItemFocus(infoPtr, nItem);
2604 /***
2605 * DESCRIPTION:
2606 * Sets a single selection.
2608 * PARAMETER(S):
2609 * [I] infoPtr : valid pointer to the listview structure
2610 * [I] INT : item index
2612 * RETURN:
2613 * None
2615 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2617 LVITEMW lvItem;
2619 TRACE("nItem=%d\n", nItem);
2621 LISTVIEW_RemoveAllSelections(infoPtr);
2623 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2624 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2625 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2627 infoPtr->nSelectionMark = nItem;
2630 /***
2631 * DESCRIPTION:
2632 * Set selection(s) with keyboard.
2634 * PARAMETER(S):
2635 * [I] infoPtr : valid pointer to the listview structure
2636 * [I] INT : item index
2638 * RETURN:
2639 * SUCCESS : TRUE (needs to be repainted)
2640 * FAILURE : FALSE (nothing has changed)
2642 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2644 /* FIXME: pass in the state */
2645 LONG lStyle = infoPtr->dwStyle;
2646 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2647 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2648 BOOL bResult = FALSE;
2650 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2652 if (lStyle & LVS_SINGLESEL)
2654 bResult = TRUE;
2655 LISTVIEW_SetSelection(infoPtr, nItem);
2657 else
2659 if (wShift)
2661 bResult = TRUE;
2662 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2664 else if (wCtrl)
2666 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2668 else
2670 bResult = TRUE;
2671 LISTVIEW_SetSelection(infoPtr, nItem);
2674 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2677 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2678 return bResult;
2682 /***
2683 * DESCRIPTION:
2684 * Called when the mouse is being actively tracked and has hovered for a specified
2685 * amount of time
2687 * PARAMETER(S):
2688 * [I] infoPtr : valid pointer to the listview structure
2689 * [I] fwKeys : key indicator
2690 * [I] pts : mouse position
2692 * RETURN:
2693 * 0 if the message was processed, non-zero if there was an error
2695 * INFO:
2696 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2697 * over the item for a certain period of time.
2700 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2702 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2703 /* FIXME: select the item!!! */
2704 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2706 return 0;
2709 /***
2710 * DESCRIPTION:
2711 * Called whenever WM_MOUSEMOVE is received.
2713 * PARAMETER(S):
2714 * [I] infoPtr : valid pointer to the listview structure
2715 * [I] fwKeys : key indicator
2716 * [I] pts : mouse position
2718 * RETURN:
2719 * 0 if the message is processed, non-zero if there was an error
2721 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2723 TRACKMOUSEEVENT trackinfo;
2725 /* see if we are supposed to be tracking mouse hovering */
2726 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2727 /* fill in the trackinfo struct */
2728 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2729 trackinfo.dwFlags = TME_QUERY;
2730 trackinfo.hwndTrack = infoPtr->hwndSelf;
2731 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2733 /* see if we are already tracking this hwnd */
2734 _TrackMouseEvent(&trackinfo);
2736 if(!(trackinfo.dwFlags & TME_HOVER)) {
2737 trackinfo.dwFlags = TME_HOVER;
2739 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2740 _TrackMouseEvent(&trackinfo);
2744 return 0;
2748 /***
2749 * Tests wheather the item is assignable to a list with style lStyle
2751 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2753 if ( (lpLVItem->mask & LVIF_TEXT) &&
2754 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2755 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2757 return TRUE;
2760 /***
2761 * DESCRIPTION:
2762 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2764 * PARAMETER(S):
2765 * [I] infoPtr : valid pointer to the listview structure
2766 * [I] lpLVItem : valid pointer to new item atttributes
2767 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2769 * RETURN:
2770 * SUCCESS : TRUE
2771 * FAILURE : FALSE
2773 static BOOL set_owner_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2775 LONG lStyle = infoPtr->dwStyle;
2776 NMLISTVIEW nmlv;
2777 INT oldState;
2779 /* a virtual listview stores only the state for the main item */
2780 if (lpLVItem->iSubItem || !(lpLVItem->mask & LVIF_STATE)) return FALSE;
2782 oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, LVIS_FOCUSED | LVIS_SELECTED);
2783 TRACE("oldState=%x, newState=%x, uCallbackMask=%x\n",
2784 oldState, lpLVItem->state, infoPtr->uCallbackMask);
2786 /* we're done if we don't need to change anything we handle */
2787 if ( !((oldState ^ lpLVItem->state) & lpLVItem->stateMask &
2788 ~infoPtr->uCallbackMask & (LVIS_FOCUSED | LVIS_SELECTED))) return TRUE;
2791 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2792 * by LVS_OWERNDATA list controls
2795 /* if we handle the focus, and we're asked to change it, do it now */
2796 if ( lpLVItem->stateMask & LVIS_FOCUSED )
2798 if (lpLVItem->state & LVIS_FOCUSED)
2799 infoPtr->nFocusedItem = lpLVItem->iItem;
2800 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2801 infoPtr->nFocusedItem = -1;
2804 /* and the selection is the only other state a virtual list may hold */
2805 if (lpLVItem->stateMask & LVIS_SELECTED)
2807 RANGE range = { lpLVItem->iItem, lpLVItem->iItem };
2809 if (lpLVItem->state & LVIS_SELECTED)
2811 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2812 ranges_add(infoPtr->hdpaSelectionRanges, range);
2814 else
2815 ranges_del(infoPtr->hdpaSelectionRanges, range);
2818 /* notify the parent now that things have changed */
2819 ZeroMemory(&nmlv, sizeof(nmlv));
2820 nmlv.iItem = lpLVItem->iItem;
2821 nmlv.uNewState = lpLVItem->state;
2822 nmlv.uOldState = oldState;
2823 nmlv.uChanged = LVIF_STATE;
2824 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2826 return TRUE;
2829 /***
2830 * DESCRIPTION:
2831 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2833 * PARAMETER(S):
2834 * [I] infoPtr : valid pointer to the listview structure
2835 * [I] lpLVItem : valid pointer to new item atttributes
2836 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2838 * RETURN:
2839 * SUCCESS : TRUE
2840 * FAILURE : FALSE
2842 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2844 LONG lStyle = infoPtr->dwStyle;
2845 HDPA hdpaSubItems;
2846 LISTVIEW_ITEM *lpItem;
2847 NMLISTVIEW nmlv;
2848 UINT uChanged = 0;
2850 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2851 if (!hdpaSubItems && hdpaSubItems != (HDPA)-1) return FALSE;
2853 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2854 if (!lpItem) return FALSE;
2856 /* we need to handle the focus, and selection differently */
2857 lpItem->state &= ~(LVIS_FOCUSED | LVIS_SELECTED);
2858 if (~infoPtr->uCallbackMask & LVIS_FOCUSED)
2860 if (lpLVItem->iItem == infoPtr->nFocusedItem)
2861 lpItem->state |= LVIS_FOCUSED;
2863 if (~infoPtr->uCallbackMask & LVIS_SELECTED)
2865 if (ranges_contain(infoPtr->hdpaSelectionRanges, lpLVItem->iItem))
2866 lpItem->state |= LVIS_SELECTED;
2869 TRACE("lpItem->state=0x%x\n", lpItem->state);
2870 /* determine what fields will change */
2871 if ((lpLVItem->mask & LVIF_STATE) &&
2872 ((lpItem->state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
2873 uChanged |= LVIF_STATE;
2875 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2876 uChanged |= LVIF_IMAGE;
2878 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2879 uChanged |= LVIF_PARAM;
2881 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2882 uChanged |= LVIF_INDENT;
2884 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2885 uChanged |= LVIF_TEXT;
2887 TRACE("uChanged=0x%x\n", uChanged);
2888 if (!uChanged) return TRUE;
2890 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2891 nmlv.iItem = lpLVItem->iItem;
2892 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2893 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2894 nmlv.uChanged = uChanged;
2895 nmlv.lParam = lpItem->lParam;
2897 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2898 if(lpItem->valid && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
2899 return FALSE;
2901 /* copy information */
2902 if (lpLVItem->mask & LVIF_TEXT)
2903 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
2905 if (lpLVItem->mask & LVIF_IMAGE)
2906 lpItem->hdr.iImage = lpLVItem->iImage;
2908 if (lpLVItem->mask & LVIF_PARAM)
2909 lpItem->lParam = lpLVItem->lParam;
2911 if (lpLVItem->mask & LVIF_INDENT)
2912 lpItem->iIndent = lpLVItem->iIndent;
2914 if (uChanged & LVIF_STATE)
2916 RANGE range = { lpLVItem->iItem, lpLVItem->iItem };
2918 lpItem->state &= ~lpLVItem->stateMask;
2919 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2920 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
2922 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2923 ranges_add(infoPtr->hdpaSelectionRanges, range);
2925 else if (lpLVItem->stateMask & LVIS_SELECTED)
2926 ranges_del(infoPtr->hdpaSelectionRanges, range);
2928 /* if we are asked to change focus, and we manage it, do it */
2929 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
2931 if (lpLVItem->state & LVIS_FOCUSED)
2933 infoPtr->nFocusedItem = lpLVItem->iItem;
2934 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
2936 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2937 infoPtr->nFocusedItem = -1;
2941 /* if we're inserting the item, we're done */
2942 if (!lpItem->valid) return TRUE;
2944 /* send LVN_ITEMCHANGED notification */
2945 nmlv.lParam = lpItem->lParam;
2946 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2948 return TRUE;
2951 /***
2952 * DESCRIPTION:
2953 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2955 * PARAMETER(S):
2956 * [I] infoPtr : valid pointer to the listview structure
2957 * [I] lpLVItem : valid pointer to new subitem atttributes
2958 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2960 * RETURN:
2961 * SUCCESS : TRUE
2962 * FAILURE : FALSE
2964 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2966 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2967 HDPA hdpaSubItems;
2968 LISTVIEW_SUBITEM *lpSubItem;
2969 BOOL bModified = FALSE;
2971 /* set subitem only if column is present */
2972 if (Header_GetItemCount(infoPtr->hwndHeader) <= lpLVItem->iSubItem)
2973 return FALSE;
2975 /* First do some sanity checks */
2976 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
2977 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
2979 /* get the subitem structure, and create it if not there */
2980 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2981 if (!hdpaSubItems) return FALSE;
2983 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
2984 if (!lpSubItem)
2986 LISTVIEW_SUBITEM *tmpSubItem;
2987 INT i;
2989 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2990 if (!lpSubItem) return FALSE;
2991 /* we could binary search here, if need be...*/
2992 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2994 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2995 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
2997 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
2999 COMCTL32_Free(lpSubItem);
3000 return FALSE;
3002 lpSubItem->iSubItem = lpLVItem->iSubItem;
3003 bModified = TRUE;
3006 if (lpLVItem->mask & LVIF_IMAGE)
3007 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3009 lpSubItem->hdr.iImage = lpLVItem->iImage;
3010 bModified = TRUE;
3013 if (lpLVItem->mask & LVIF_TEXT)
3014 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3016 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3017 bModified = TRUE;
3020 if (bModified && !infoPtr->bIsDrawing && uView == LVS_REPORT)
3021 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3023 return TRUE;
3026 /***
3027 * DESCRIPTION:
3028 * Sets item attributes.
3030 * PARAMETER(S):
3031 * [I] infoPtr : valid pointer to the listview structure
3032 * [I] LPLVITEM : new item atttributes
3033 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3035 * RETURN:
3036 * SUCCESS : TRUE
3037 * FAILURE : FALSE
3039 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
3041 INT nOldFocus = infoPtr->nFocusedItem;
3042 LPWSTR pszText = NULL;
3043 BOOL bResult;
3045 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3047 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3048 return FALSE;
3050 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3051 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3053 pszText = lpLVItem->pszText;
3054 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3057 /* actually set the fields */
3058 if (infoPtr->dwStyle & LVS_OWNERDATA)
3059 bResult = set_owner_item(infoPtr, lpLVItem, TRUE);
3060 else
3062 /* sanity checks first */
3063 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3065 if (lpLVItem->iSubItem)
3066 bResult = set_sub_item(infoPtr, lpLVItem, TRUE);
3067 else
3068 bResult = set_main_item(infoPtr, lpLVItem, TRUE);
3071 /* redraw item, if necessary */
3072 if (bResult && !infoPtr->bIsDrawing && lpLVItem->iSubItem == 0)
3074 if (nOldFocus != infoPtr->nFocusedItem && infoPtr->bFocus)
3075 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->rcFocus);
3077 /* this little optimization eliminates some nasty flicker */
3078 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
3079 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3080 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
3081 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3082 else
3083 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3085 /* restore text */
3086 if (pszText)
3088 textfreeT(lpLVItem->pszText, isW);
3089 lpLVItem->pszText = pszText;
3092 return bResult;
3095 /***
3096 * DESCRIPTION:
3097 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3099 * PARAMETER(S):
3100 * [I] infoPtr : valid pointer to the listview structure
3102 * RETURN:
3103 * item index
3105 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3107 LONG lStyle = infoPtr->dwStyle;
3108 UINT uView = lStyle & LVS_TYPEMASK;
3109 INT nItem = 0;
3110 SCROLLINFO scrollInfo;
3112 scrollInfo.cbSize = sizeof(SCROLLINFO);
3113 scrollInfo.fMask = SIF_POS;
3115 if (uView == LVS_LIST)
3117 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3118 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3120 else if (uView == LVS_REPORT)
3122 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3123 nItem = scrollInfo.nPos;
3125 else
3127 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3128 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3131 TRACE("nItem=%d\n", nItem);
3133 return nItem;
3137 /***
3138 * DESCRIPTION:
3139 * Erases the background of the given rectangle
3141 * PARAMETER(S):
3142 * [I] infoPtr : valid pointer to the listview structure
3143 * [I] hdc : device context handle
3144 * [I] lprcBox : clipping rectangle
3146 * RETURN:
3147 * Success: TRUE
3148 * Failure: FALSE
3150 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
3152 if (!infoPtr->hBkBrush) return FALSE;
3154 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3156 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3159 /***
3160 * DESCRIPTION:
3161 * Draws an item.
3163 * PARAMETER(S):
3164 * [I] infoPtr : valid pointer to the listview structure
3165 * [I] hdc : device context handle
3166 * [I] nItem : item index
3167 * [I] nSubItem : subitem index
3168 * [I] pos : item position in client coordinates
3169 * [I] cdmode : custom draw mode
3171 * RETURN:
3172 * Success: TRUE
3173 * Failure: FALSE
3175 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3177 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3178 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3179 DWORD cditemmode = CDRF_DODEFAULT;
3180 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3181 NMLVCUSTOMDRAW nmlvcd;
3182 HIMAGELIST himl;
3183 LVITEMW lvItem;
3185 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3187 /* get information needed for drawing the item */
3188 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3189 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3190 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3191 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3192 lvItem.iItem = nItem;
3193 lvItem.iSubItem = nSubItem;
3194 lvItem.cchTextMax = DISP_TEXT_SIZE;
3195 lvItem.pszText = szDispText;
3196 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3197 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3199 /* now check if we need to update the focus rectangle */
3200 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3202 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3203 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel)) return FALSE;
3204 OffsetRect(&rcBox, pos.x, pos.y);
3205 OffsetRect(&rcState, pos.x, pos.y);
3206 OffsetRect(&rcIcon, pos.x, pos.y);
3207 OffsetRect(&rcLabel, pos.x, pos.y);
3208 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3209 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3211 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3212 if (cdmode & CDRF_NOTIFYITEMDRAW)
3213 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3214 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3216 /* state icons */
3217 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3219 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3220 if (uStateImage)
3221 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3224 /* small icons */
3225 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3226 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3227 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3228 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3230 /* Don't bother painting item being edited */
3231 if (infoPtr->bEditing && lprcFocus && nSubItem == 0) goto postpaint;
3233 /* Set the text attributes */
3234 SetBkMode(hdc, nmlvcd.clrTextBk == CLR_NONE ? TRANSPARENT : OPAQUE);
3235 if (nmlvcd.clrTextBk != CLR_NONE)
3236 SetBkColor(hdc, nmlvcd.clrTextBk == CLR_DEFAULT ? infoPtr->clrTextBkDefault : nmlvcd.clrTextBk);
3237 SetTextColor(hdc, nmlvcd.clrText);
3239 /* draw the selection background, if we're drawing the main item */
3240 if (nSubItem == 0)
3242 rcSelect = rcLabel;
3243 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3244 rcSelect.right = rcBox.right;
3246 if (lvItem.state & LVIS_SELECTED)
3247 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3248 if(lprcFocus) *lprcFocus = rcSelect;
3251 /* figure out the text drawing flags */
3252 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3253 if (uView == LVS_ICON)
3254 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3255 else
3257 INT align = DT_LEFT;
3259 if (nSubItem)
3261 LVCOLUMNW lvColumn;
3262 lvColumn.mask = LVCF_FMT;
3263 LISTVIEW_GetColumnT(infoPtr, nSubItem, &lvColumn, TRUE);
3264 TRACE("lvColumn=%s\n", debuglvcolumn_t(&lvColumn, TRUE));
3265 if (lvColumn.fmt & LVCFMT_RIGHT) align = DT_RIGHT;
3266 else if (lvColumn.fmt & LVCFMT_CENTER) align = DT_CENTER;
3268 uFormat |= align;
3270 if (!(uFormat & (DT_RIGHT | DT_CENTER))) rcLabel.left += 2;
3271 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3273 postpaint:
3274 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3275 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3276 return TRUE;
3279 /***
3280 * DESCRIPTION:
3281 * Draws listview items when in owner draw mode.
3283 * PARAMETER(S):
3284 * [I] infoPtr : valid pointer to the listview structure
3285 * [I] hdc : device context handle
3287 * RETURN:
3288 * None
3290 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
3292 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3293 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3294 POINT Origin, Position;
3295 DRAWITEMSTRUCT dis;
3296 LVITEMW item;
3297 ITERATOR i;
3299 TRACE("()\n");
3301 ZeroMemory(&dis, sizeof(dis));
3303 /* Get scroll info once before loop */
3304 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3306 /* figure out what we need to draw */
3307 iterator_visibleitems(&i, infoPtr, hdc);
3309 /* send cache hint notification */
3310 if (infoPtr->dwStyle & LVS_OWNERDATA)
3312 RANGE range = iterator_range(&i);
3313 NMLVCACHEHINT nmlv;
3315 nmlv.iFrom = range.lower;
3316 nmlv.iTo = range.upper;
3317 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3320 /* iterate through the invalidated rows */
3321 while(iterator_prev(&i))
3323 item.iItem = i.nItem;
3324 item.iSubItem = 0;
3325 item.mask = LVIF_PARAM | LVIF_STATE;
3326 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3327 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3329 dis.CtlType = ODT_LISTVIEW;
3330 dis.CtlID = uID;
3331 dis.itemID = item.iItem;
3332 dis.itemAction = ODA_DRAWENTIRE;
3333 dis.itemState = 0;
3334 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3335 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3336 dis.hwndItem = infoPtr->hwndSelf;
3337 dis.hDC = hdc;
3338 if (!LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position)) continue;
3339 dis.rcItem.left = Position.x + Origin.x;
3340 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3341 dis.rcItem.top = Position.y + Origin.y;
3342 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3343 dis.itemData = item.lParam;
3345 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3346 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3348 iterator_destroy(&i);
3351 /***
3352 * DESCRIPTION:
3353 * Draws listview items when in report display mode.
3355 * PARAMETER(S):
3356 * [I] infoPtr : valid pointer to the listview structure
3357 * [I] hdc : device context handle
3358 * [I] cdmode : custom draw mode
3360 * RETURN:
3361 * None
3363 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3365 INT rgntype, nColumnCount, nFirstCol, nLastCol, nCol;
3366 RECT rcClip;
3367 COLUMNCACHE *lpCols;
3368 POINT Origin, Position;
3369 ITERATOR i;
3371 TRACE("()\n");
3373 /* figure out what to draw */
3374 rgntype = GetClipBox(hdc, &rcClip);
3375 if (rgntype == NULLREGION) return;
3377 /* cache column info */
3378 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3379 lpCols = COMCTL32_Alloc(nColumnCount * sizeof(COLUMNCACHE));
3380 if (!lpCols) return;
3381 for (nCol = 0; nCol < nColumnCount; nCol++)
3383 Header_GetItemRect(infoPtr->hwndHeader, nCol, &lpCols[nCol].rc);
3384 TRACE("lpCols[%d].rc=%s\n", nCol, debugrect(&lpCols[nCol].rc));
3387 /* Get scroll info once before loop */
3388 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3390 /* we now narrow the columns as well */
3391 nLastCol = nColumnCount - 1;
3392 for(nFirstCol = 0; nFirstCol < nColumnCount; nFirstCol++)
3393 if (lpCols[nFirstCol].rc.right + Origin.x >= rcClip.left) break;
3394 for(nLastCol = nColumnCount - 1; nLastCol >= 0; nLastCol--)
3395 if (lpCols[nLastCol].rc.left + Origin.x < rcClip.right) break;
3397 /* figure out what we need to draw */
3398 iterator_visibleitems(&i, infoPtr, hdc);
3400 /* a last few bits before we start drawing */
3401 TRACE("Colums=(%di - %d)\n", nFirstCol, nLastCol);
3403 /* iterate through the invalidated rows */
3404 while(iterator_prev(&i))
3406 /* iterate through the invalidated columns */
3407 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
3409 if (!LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position)) continue;
3410 Position.x += Origin.x;
3411 Position.y += Origin.y;
3413 if (rgntype == COMPLEXREGION)
3415 RECT rcItem;
3416 rcItem.left = Position.x + lpCols[nCol].rc.left;
3417 rcItem.right = rcItem.left + (lpCols[nCol].rc.right - lpCols[nCol].rc.left);
3418 rcItem.top = Position.y;
3419 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3420 if (!RectVisible(hdc, &rcItem)) continue;
3423 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, nCol, Position, cdmode);
3426 iterator_destroy(&i);
3428 /* cleanup the mess */
3429 COMCTL32_Free(lpCols);
3432 /***
3433 * DESCRIPTION:
3434 * Draws listview items when in list display mode.
3436 * PARAMETER(S):
3437 * [I] infoPtr : valid pointer to the listview structure
3438 * [I] hdc : device context handle
3439 * [I] cdmode : custom draw mode
3441 * RETURN:
3442 * None
3444 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3446 POINT Origin, Position;
3447 ITERATOR i;
3449 /* Get scroll info once before loop */
3450 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3452 /* figure out what we need to draw */
3453 iterator_visibleitems(&i, infoPtr, hdc);
3455 while(iterator_prev(&i))
3457 if (!LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position)) continue;
3458 Position.x += Origin.x;
3459 Position.y += Origin.y;
3461 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, 0, Position, cdmode);
3463 iterator_destroy(&i);
3467 /***
3468 * DESCRIPTION:
3469 * Draws listview items.
3471 * PARAMETER(S):
3472 * [I] infoPtr : valid pointer to the listview structure
3473 * [I] HDC : device context handle
3475 * RETURN:
3476 * NoneX
3478 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3480 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3481 COLORREF oldTextColor;
3482 NMLVCUSTOMDRAW nmlvcd;
3483 HFONT hOldFont;
3484 DWORD cdmode;
3485 INT oldBkMode;
3486 RECT rcClient;
3488 LISTVIEW_DUMP(infoPtr);
3490 infoPtr->bIsDrawing = TRUE;
3492 /* save dc values we're gonna trash while drawing */
3493 hOldFont = SelectObject(hdc, infoPtr->hFont);
3494 oldBkMode = GetBkMode(hdc);
3495 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3496 oldTextColor = GetTextColor(hdc);
3498 GetClientRect(infoPtr->hwndSelf, &rcClient);
3499 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, NULL);
3500 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3501 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3503 /* nothing to draw */
3504 if(infoPtr->nItemCount == 0) goto enddraw;
3506 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3507 LISTVIEW_RefreshOwnerDraw(infoPtr, hdc);
3508 else
3510 if (uView == LVS_REPORT)
3511 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3512 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3513 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3515 /* if we have a focus rect, draw it */
3516 if (infoPtr->bFocus)
3517 DrawFocusRect(hdc, &infoPtr->rcFocus);
3520 enddraw:
3521 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3522 notify_customdraw(infoPtr, CDDS_POSTPAINT, &nmlvcd);
3524 /* unselect objects */
3525 SelectObject(hdc, hOldFont);
3526 SetBkMode(hdc, oldBkMode);
3527 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3528 SetTextColor(hdc, oldTextColor);
3529 infoPtr->bIsDrawing = FALSE;
3533 /***
3534 * DESCRIPTION:
3535 * Calculates the approximate width and height of a given number of items.
3537 * PARAMETER(S):
3538 * [I] infoPtr : valid pointer to the listview structure
3539 * [I] INT : number of items
3540 * [I] INT : width
3541 * [I] INT : height
3543 * RETURN:
3544 * Returns a DWORD. The width in the low word and the height in high word.
3546 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3547 WORD wWidth, WORD wHeight)
3549 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3550 INT nItemCountPerColumn = 1;
3551 INT nColumnCount = 0;
3552 DWORD dwViewRect = 0;
3554 if (nItemCount == -1)
3555 nItemCount = infoPtr->nItemCount;
3557 if (uView == LVS_LIST)
3559 if (wHeight == 0xFFFF)
3561 /* use current height */
3562 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3565 if (wHeight < infoPtr->nItemHeight)
3566 wHeight = infoPtr->nItemHeight;
3568 if (nItemCount > 0)
3570 if (infoPtr->nItemHeight > 0)
3572 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3573 if (nItemCountPerColumn == 0)
3574 nItemCountPerColumn = 1;
3576 if (nItemCount % nItemCountPerColumn != 0)
3577 nColumnCount = nItemCount / nItemCountPerColumn;
3578 else
3579 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3583 /* Microsoft padding magic */
3584 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3585 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3587 dwViewRect = MAKELONG(wWidth, wHeight);
3589 else if (uView == LVS_REPORT)
3590 FIXME("uView == LVS_REPORT: not implemented\n");
3591 else if (uView == LVS_SMALLICON)
3592 FIXME("uView == LVS_SMALLICON: not implemented\n");
3593 else if (uView == LVS_ICON)
3594 FIXME("uView == LVS_ICON: not implemented\n");
3596 return dwViewRect;
3599 /***
3600 * DESCRIPTION:
3601 * Arranges listview items in icon display mode.
3603 * PARAMETER(S):
3604 * [I] infoPtr : valid pointer to the listview structure
3605 * [I] INT : alignment code
3607 * RETURN:
3608 * SUCCESS : TRUE
3609 * FAILURE : FALSE
3611 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3613 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3614 BOOL bResult = FALSE;
3616 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3618 switch (nAlignCode)
3620 case LVA_ALIGNLEFT:
3621 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3622 break;
3623 case LVA_ALIGNTOP:
3624 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3625 break;
3626 case LVA_DEFAULT:
3627 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3628 break;
3629 case LVA_SNAPTOGRID:
3630 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3631 break;
3635 return bResult;
3638 /* << LISTVIEW_CreateDragImage >> */
3641 /***
3642 * DESCRIPTION:
3643 * Removes all listview items and subitems.
3645 * PARAMETER(S):
3646 * [I] infoPtr : valid pointer to the listview structure
3648 * RETURN:
3649 * SUCCESS : TRUE
3650 * FAILURE : FALSE
3652 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3654 LONG lStyle = infoPtr->dwStyle;
3655 UINT uView = lStyle & LVS_TYPEMASK;
3656 LISTVIEW_ITEM *lpItem;
3657 LISTVIEW_SUBITEM *lpSubItem;
3658 NMLISTVIEW nmlv;
3659 BOOL bSuppress;
3660 BOOL bResult = FALSE;
3661 HDPA hdpaSubItems;
3663 TRACE("()\n");
3665 LISTVIEW_RemoveAllSelections(infoPtr);
3666 infoPtr->nSelectionMark=-1;
3667 infoPtr->nFocusedItem=-1;
3668 SetRectEmpty(&infoPtr->rcFocus);
3669 /* But we are supposed to leave nHotItem as is! */
3671 if (lStyle & LVS_OWNERDATA)
3673 infoPtr->nItemCount = 0;
3674 LISTVIEW_InvalidateList(infoPtr);
3675 return TRUE;
3678 if (infoPtr->nItemCount > 0)
3680 INT i, j;
3682 /* send LVN_DELETEALLITEMS notification */
3683 /* verify if subsequent LVN_DELETEITEM notifications should be
3684 suppressed */
3685 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3686 nmlv.iItem = -1;
3687 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3689 for (i = 0; i < infoPtr->nItemCount; i++)
3691 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3692 if (hdpaSubItems != NULL)
3694 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3696 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3697 if (lpSubItem != NULL)
3699 /* free subitem string */
3700 if (is_textW(lpSubItem->hdr.pszText))
3701 COMCTL32_Free(lpSubItem->hdr.pszText);
3703 /* free subitem */
3704 COMCTL32_Free(lpSubItem);
3708 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3709 if (lpItem != NULL)
3711 if (!bSuppress)
3713 /* send LVN_DELETEITEM notification */
3714 nmlv.iItem = i;
3715 nmlv.lParam = lpItem->lParam;
3716 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3719 /* free item string */
3720 if (is_textW(lpItem->hdr.pszText))
3721 COMCTL32_Free(lpItem->hdr.pszText);
3723 /* free item */
3724 COMCTL32_Free(lpItem);
3727 DPA_Destroy(hdpaSubItems);
3731 /* reinitialize listview memory */
3732 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3733 infoPtr->nItemCount = 0;
3734 DPA_DeleteAllPtrs(infoPtr->hdpaPosX);
3735 DPA_DeleteAllPtrs(infoPtr->hdpaPosY);
3737 /* align items (set position of each item) */
3738 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3740 if (lStyle & LVS_ALIGNLEFT)
3742 LISTVIEW_AlignLeft(infoPtr);
3744 else
3746 LISTVIEW_AlignTop(infoPtr);
3750 LISTVIEW_UpdateScroll(infoPtr);
3752 LISTVIEW_InvalidateList(infoPtr);
3755 return bResult;
3758 /***
3759 * DESCRIPTION:
3760 * Removes a column from the listview control.
3762 * PARAMETER(S):
3763 * [I] infoPtr : valid pointer to the listview structure
3764 * [I] INT : column index
3766 * RETURN:
3767 * SUCCESS : TRUE
3768 * FAILURE : FALSE
3770 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3772 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3773 RECT rcCol, rcOld;
3775 TRACE("nColumn=%d\n", nColumn);
3777 if (nColumn <= 0) return FALSE;
3779 if (uView == LVS_REPORT)
3781 if (!Header_GetItemRect(infoPtr->hwndHeader, nColumn, &rcCol))
3782 return FALSE;
3784 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3785 return FALSE;
3788 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3790 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3791 HDPA hdpaSubItems;
3792 INT nItem, nSubItem, i;
3794 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3796 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3797 if (!hdpaSubItems) continue;
3798 nSubItem = 0;
3799 lpDelItem = 0;
3800 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3802 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3803 if (!lpSubItem) break;
3804 if (lpSubItem->iSubItem == nColumn)
3806 nSubItem = i;
3807 lpDelItem = lpSubItem;
3809 else if (lpSubItem->iSubItem > nColumn)
3811 lpSubItem->iSubItem--;
3815 /* if we found our subitem, zapp it */
3816 if (nSubItem > 0)
3818 /* free string */
3819 if (is_textW(lpDelItem->hdr.pszText))
3820 COMCTL32_Free(lpDelItem->hdr.pszText);
3822 /* free item */
3823 COMCTL32_Free(lpDelItem);
3825 /* free dpa memory */
3826 DPA_DeletePtr(hdpaSubItems, nSubItem);
3831 /* we need to worry about display issues in report mode only */
3832 if (uView != LVS_REPORT) return TRUE;
3834 /* if we have a focus, must first erase the focus rect */
3835 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
3837 /* Need to reset the item width when deleting a column */
3838 infoPtr->nItemWidth -= rcCol.right - rcCol.left;
3840 /* update scrollbar(s) */
3841 LISTVIEW_UpdateScroll(infoPtr);
3843 /* scroll to cover the deleted column, and invalidate for redraw */
3844 rcOld = infoPtr->rcList;
3845 rcOld.left = rcCol.left;
3846 ScrollWindowEx(infoPtr->hwndSelf, -(rcCol.right - rcCol.left), 0,
3847 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3849 /* we can restore focus now */
3850 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
3852 return TRUE;
3855 /***
3856 * DESCRIPTION:
3857 * Removes an item from the listview control.
3859 * PARAMETER(S):
3860 * [I] infoPtr : valid pointer to the listview structure
3861 * [I] INT : item index
3863 * RETURN:
3864 * SUCCESS : TRUE
3865 * FAILURE : FALSE
3867 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
3869 LONG lStyle = infoPtr->dwStyle;
3870 UINT uView = lStyle & LVS_TYPEMASK;
3871 LONG lCtrlId = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3872 NMLISTVIEW nmlv;
3873 BOOL bResult = FALSE;
3874 HDPA hdpaSubItems;
3875 LISTVIEW_ITEM *lpItem;
3876 LISTVIEW_SUBITEM *lpSubItem;
3877 INT i;
3878 LVITEMW item;
3880 TRACE("(nItem=%d)\n", nItem);
3883 /* First, send LVN_DELETEITEM notification. */
3884 memset(&nmlv, 0, sizeof (NMLISTVIEW));
3885 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
3886 nmlv.hdr.idFrom = lCtrlId;
3887 nmlv.hdr.code = LVN_DELETEITEM;
3888 nmlv.iItem = nItem;
3889 SendMessageW((infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)lCtrlId,
3890 (LPARAM)&nmlv);
3892 if (nItem == infoPtr->nFocusedItem)
3894 infoPtr->nFocusedItem = -1;
3895 SetRectEmpty(&infoPtr->rcFocus);
3898 /* remove it from the selection range */
3899 item.state = LVIS_SELECTED;
3900 item.stateMask = LVIS_SELECTED;
3901 LISTVIEW_SetItemState(infoPtr,nItem,&item);
3903 if (lStyle & LVS_OWNERDATA)
3905 infoPtr->nItemCount--;
3906 LISTVIEW_InvalidateList(infoPtr); /*FIXME: optimize */
3907 return TRUE;
3910 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3912 /* initialize memory */
3913 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3915 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
3916 if (hdpaSubItems != NULL)
3918 infoPtr->nItemCount--;
3919 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3921 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3922 if (lpSubItem != NULL)
3924 /* free item string */
3925 if (is_textW(lpSubItem->hdr.pszText))
3926 COMCTL32_Free(lpSubItem->hdr.pszText);
3928 /* free item */
3929 COMCTL32_Free(lpSubItem);
3933 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3934 if (lpItem != NULL)
3936 /* free item string */
3937 if (is_textW(lpItem->hdr.pszText))
3938 COMCTL32_Free(lpItem->hdr.pszText);
3940 /* free item */
3941 COMCTL32_Free(lpItem);
3944 bResult = DPA_Destroy(hdpaSubItems);
3945 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
3946 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
3949 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
3951 /* align items (set position of each item) */
3952 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
3954 if (lStyle & LVS_ALIGNLEFT)
3955 LISTVIEW_AlignLeft(infoPtr);
3956 else
3957 LISTVIEW_AlignTop(infoPtr);
3960 LISTVIEW_UpdateScroll(infoPtr);
3962 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
3965 return bResult;
3969 /***
3970 * DESCRIPTION:
3971 * Callback implementation for editlabel control
3973 * PARAMETER(S):
3974 * [I] infoPtr : valid pointer to the listview structure
3975 * [I] pszText : modified text
3976 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
3978 * RETURN:
3979 * SUCCESS : TRUE
3980 * FAILURE : FALSE
3982 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
3984 NMLVDISPINFOW dispInfo;
3986 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
3988 infoPtr->bEditing = FALSE;
3990 ZeroMemory(&dispInfo, sizeof(dispInfo));
3991 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
3992 dispInfo.item.iItem = infoPtr->nEditLabelItem;
3993 dispInfo.item.iSubItem = 0;
3994 dispInfo.item.stateMask = ~0;
3995 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
3996 dispInfo.item.pszText = pszText;
3997 dispInfo.item.cchTextMax = textlenT(pszText, isW);
3999 /* Do we need to update the Item Text */
4000 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4001 if (!pszText) return TRUE;
4003 ZeroMemory(&dispInfo, sizeof(dispInfo));
4004 dispInfo.item.mask = LVIF_TEXT;
4005 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4006 dispInfo.item.iSubItem = 0;
4007 dispInfo.item.pszText = pszText;
4008 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4009 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4012 /***
4013 * DESCRIPTION:
4014 * Begin in place editing of specified list view item
4016 * PARAMETER(S):
4017 * [I] infoPtr : valid pointer to the listview structure
4018 * [I] INT : item index
4019 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4021 * RETURN:
4022 * SUCCESS : TRUE
4023 * FAILURE : FALSE
4025 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4027 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4028 NMLVDISPINFOW dispInfo;
4029 RECT rect;
4031 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4033 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4035 infoPtr->nEditLabelItem = nItem;
4037 /* Is the EditBox still there, if so remove it */
4038 if(infoPtr->hwndEdit != 0)
4040 SetFocus(infoPtr->hwndSelf);
4041 infoPtr->hwndEdit = 0;
4044 LISTVIEW_SetSelection(infoPtr, nItem);
4045 LISTVIEW_SetItemFocus(infoPtr, nItem);
4047 rect.left = LVIR_LABEL;
4048 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4050 ZeroMemory(&dispInfo, sizeof(dispInfo));
4051 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4052 dispInfo.item.iItem = nItem;
4053 dispInfo.item.iSubItem = 0;
4054 dispInfo.item.stateMask = ~0;
4055 dispInfo.item.pszText = szDispText;
4056 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4057 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4059 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4060 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4061 if (!infoPtr->hwndEdit) return 0;
4063 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4065 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4066 infoPtr->hwndEdit = 0;
4067 return 0;
4070 infoPtr->bEditing = TRUE;
4071 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4072 SetFocus(infoPtr->hwndEdit);
4073 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4074 return infoPtr->hwndEdit;
4078 /***
4079 * DESCRIPTION:
4080 * Ensures the specified item is visible, scrolling into view if necessary.
4082 * PARAMETER(S):
4083 * [I] infoPtr : valid pointer to the listview structure
4084 * [I] nItem : item index
4085 * [I] bPartial : partially or entirely visible
4087 * RETURN:
4088 * SUCCESS : TRUE
4089 * FAILURE : FALSE
4091 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4093 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4094 INT nScrollPosHeight = 0;
4095 INT nScrollPosWidth = 0;
4096 INT nHorzAdjust = 0;
4097 INT nVertAdjust = 0;
4098 INT nHorzDiff = 0;
4099 INT nVertDiff = 0;
4100 RECT rcItem, rcTemp;
4102 rcItem.left = LVIR_BOUNDS;
4103 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4105 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4107 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4109 /* scroll left/right, but in LVS_REPORT mode */
4110 if (uView == LVS_LIST)
4111 nScrollPosWidth = infoPtr->nItemWidth;
4112 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4113 nScrollPosWidth = 1;
4115 if (rcItem.left < infoPtr->rcList.left)
4117 nHorzAdjust = -1;
4118 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4120 else
4122 nHorzAdjust = 1;
4123 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4127 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4129 /* scroll up/down, but not in LVS_LIST mode */
4130 if (uView == LVS_REPORT)
4131 nScrollPosHeight = infoPtr->nItemHeight;
4132 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4133 nScrollPosHeight = 1;
4135 if (rcItem.top < infoPtr->rcList.top)
4137 nVertAdjust = -1;
4138 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4140 else
4142 nVertAdjust = 1;
4143 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4147 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4149 if (nScrollPosWidth)
4151 INT diff = nHorzDiff / nScrollPosWidth;
4152 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4153 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4156 if (nScrollPosHeight)
4158 INT diff = nVertDiff / nScrollPosHeight;
4159 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4160 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4163 return TRUE;
4166 /***
4167 * DESCRIPTION:
4168 * Searches for an item with specific characteristics.
4170 * PARAMETER(S):
4171 * [I] hwnd : window handle
4172 * [I] nStart : base item index
4173 * [I] lpFindInfo : item information to look for
4175 * RETURN:
4176 * SUCCESS : index of item
4177 * FAILURE : -1
4179 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4180 LPLVFINDINFOW lpFindInfo)
4182 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4183 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4184 BOOL bWrap = FALSE, bNearest = FALSE;
4185 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4186 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4187 POINT Position, Destination;
4188 LVITEMW lvItem;
4190 if (!lpFindInfo || nItem < 0) return -1;
4192 lvItem.mask = 0;
4193 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4195 lvItem.mask |= LVIF_TEXT;
4196 lvItem.pszText = szDispText;
4197 lvItem.cchTextMax = DISP_TEXT_SIZE;
4200 if (lpFindInfo->flags & LVFI_WRAP)
4201 bWrap = TRUE;
4203 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4204 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4206 POINT Origin;
4208 FIXME("LVFI_NEARESTXY is slow.\n");
4209 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
4210 Destination.x = lpFindInfo->pt.x - Origin.x;
4211 Destination.y = lpFindInfo->pt.y - Origin.y;
4212 switch(lpFindInfo->vkDirection)
4214 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4215 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4216 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4217 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4218 case VK_HOME: Destination.x = Destination.y = 0; break;
4219 case VK_END: Destination.x = infoPtr->rcView.right; Destination.y = infoPtr->rcView.bottom; break;
4220 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4221 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4222 default: FIXME("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4224 bNearest = TRUE;
4227 /* if LVFI_PARAM is specified, all other flags are ignored */
4228 if (lpFindInfo->flags & LVFI_PARAM)
4230 lvItem.mask |= LVIF_PARAM;
4231 bNearest = FALSE;
4232 lvItem.mask &= ~LVIF_TEXT;
4235 again:
4236 for (; nItem < nLast; nItem++)
4238 lvItem.iItem = nItem;
4239 lvItem.iSubItem = 0;
4240 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4242 if (lvItem.mask & LVIF_PARAM && lpFindInfo->lParam == lvItem.lParam)
4243 return nItem;
4245 if (lvItem.mask & LVIF_TEXT)
4247 if (lpFindInfo->flags & LVFI_PARTIAL)
4249 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4251 else
4253 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4257 if (!bNearest) return nItem;
4259 /* This is very inefficient. To do a good job here,
4260 * we need a sorted array of (x,y) item positions */
4261 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) continue;
4263 /* compute the distance^2 to the destination */
4264 xdist = Destination.x - Position.x;
4265 ydist = Destination.y - Position.y;
4266 dist = xdist * xdist + ydist * ydist;
4268 /* remember the distance, and item if it's closer */
4269 if (dist < mindist)
4271 mindist = dist;
4272 nNearestItem = nItem;
4276 if (bWrap)
4278 nItem = 0;
4279 nLast = min(nStart + 1, infoPtr->nItemCount);
4280 bWrap = FALSE;
4281 goto again;
4284 return nNearestItem;
4287 /***
4288 * DESCRIPTION:
4289 * Searches for an item with specific characteristics.
4291 * PARAMETER(S):
4292 * [I] hwnd : window handle
4293 * [I] nStart : base item index
4294 * [I] lpFindInfo : item information to look for
4296 * RETURN:
4297 * SUCCESS : index of item
4298 * FAILURE : -1
4300 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4301 LPLVFINDINFOA lpFindInfo)
4303 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4304 LVFINDINFOW fiw;
4305 LRESULT res;
4307 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4308 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4309 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4310 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4311 return res;
4314 /***
4315 * DESCRIPTION:
4316 * Retrieves the background image of the listview control.
4318 * PARAMETER(S):
4319 * [I] infoPtr : valid pointer to the listview structure
4320 * [O] LPLVMKBIMAGE : background image attributes
4322 * RETURN:
4323 * SUCCESS : TRUE
4324 * FAILURE : FALSE
4326 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4327 /* { */
4328 /* FIXME (listview, "empty stub!\n"); */
4329 /* return FALSE; */
4330 /* } */
4332 /***
4333 * DESCRIPTION:
4334 * Retrieves column attributes.
4336 * PARAMETER(S):
4337 * [I] infoPtr : valid pointer to the listview structure
4338 * [I] INT : column index
4339 * [IO] LPLVCOLUMNW : column information
4340 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4341 * otherwise it is in fact a LPLVCOLUMNA
4343 * RETURN:
4344 * SUCCESS : TRUE
4345 * FAILURE : FALSE
4347 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4349 HDITEMW hdi;
4350 BOOL bResult = FALSE;
4352 if (lpColumn != NULL)
4355 /* initialize memory */
4356 ZeroMemory(&hdi, sizeof(hdi));
4358 if (lpColumn->mask & LVCF_FMT)
4359 hdi.mask |= HDI_FORMAT;
4361 if (lpColumn->mask & LVCF_WIDTH)
4362 hdi.mask |= HDI_WIDTH;
4364 if (lpColumn->mask & LVCF_TEXT)
4366 hdi.mask |= HDI_TEXT;
4367 hdi.cchTextMax = lpColumn->cchTextMax;
4368 hdi.pszText = lpColumn->pszText;
4371 if (lpColumn->mask & LVCF_IMAGE)
4372 hdi.mask |= HDI_IMAGE;
4374 if (lpColumn->mask & LVCF_ORDER)
4375 hdi.mask |= HDI_ORDER;
4377 if (isW)
4378 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4379 else
4380 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4382 if (bResult)
4384 if (lpColumn->mask & LVCF_FMT)
4386 lpColumn->fmt = 0;
4388 if (hdi.fmt & HDF_LEFT)
4389 lpColumn->fmt |= LVCFMT_LEFT;
4390 else if (hdi.fmt & HDF_RIGHT)
4391 lpColumn->fmt |= LVCFMT_RIGHT;
4392 else if (hdi.fmt & HDF_CENTER)
4393 lpColumn->fmt |= LVCFMT_CENTER;
4395 if (hdi.fmt & HDF_IMAGE)
4396 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4398 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4399 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4402 if (lpColumn->mask & LVCF_WIDTH)
4403 lpColumn->cx = hdi.cxy;
4405 if (lpColumn->mask & LVCF_IMAGE)
4406 lpColumn->iImage = hdi.iImage;
4408 if (lpColumn->mask & LVCF_ORDER)
4409 lpColumn->iOrder = hdi.iOrder;
4411 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4412 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4417 return bResult;
4421 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4423 INT i;
4425 if (!lpiArray)
4426 return FALSE;
4428 /* FIXME: little hack */
4429 for (i = 0; i < iCount; i++)
4430 lpiArray[i] = i;
4432 return TRUE;
4435 /***
4436 * DESCRIPTION:
4437 * Retrieves the column width.
4439 * PARAMETER(S):
4440 * [I] infoPtr : valid pointer to the listview structure
4441 * [I] int : column index
4443 * RETURN:
4444 * SUCCESS : column width
4445 * FAILURE : zero
4447 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4449 INT nColumnWidth = 0;
4450 HDITEMW hdi;
4452 TRACE("nColumn=%d\n", nColumn);
4454 /* we have a 'column' in LIST and REPORT mode only */
4455 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4457 case LVS_LIST:
4458 nColumnWidth = infoPtr->nItemWidth;
4459 break;
4460 case LVS_REPORT:
4461 hdi.mask = HDI_WIDTH;
4462 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4463 nColumnWidth = hdi.cxy;
4464 break;
4467 TRACE("nColumnWidth=%d\n", nColumnWidth);
4468 return nColumnWidth;
4471 /***
4472 * DESCRIPTION:
4473 * In list or report display mode, retrieves the number of items that can fit
4474 * vertically in the visible area. In icon or small icon display mode,
4475 * retrieves the total number of visible items.
4477 * PARAMETER(S):
4478 * [I] infoPtr : valid pointer to the listview structure
4480 * RETURN:
4481 * Number of fully visible items.
4483 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4485 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4486 INT nItemCount = 0;
4488 if (uView == LVS_LIST)
4490 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4492 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4493 LISTVIEW_GetCountPerColumn(infoPtr);
4496 else if (uView == LVS_REPORT)
4498 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4500 else
4502 nItemCount = infoPtr->nItemCount;
4505 return nItemCount;
4509 /***
4510 * DESCRIPTION:
4511 * Retrieves an image list handle.
4513 * PARAMETER(S):
4514 * [I] infoPtr : valid pointer to the listview structure
4515 * [I] nImageList : image list identifier
4517 * RETURN:
4518 * SUCCESS : image list handle
4519 * FAILURE : NULL
4521 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4523 HIMAGELIST himl = NULL;
4525 switch (nImageList)
4527 case LVSIL_NORMAL:
4528 himl = infoPtr->himlNormal;
4529 break;
4530 case LVSIL_SMALL:
4531 himl = infoPtr->himlSmall;
4532 break;
4533 case LVSIL_STATE:
4534 himl = infoPtr->himlState;
4535 break;
4538 return (LRESULT)himl;
4541 /* LISTVIEW_GetISearchString */
4543 /***
4544 * DESCRIPTION:
4545 * Retrieves item attributes.
4547 * PARAMETER(S):
4548 * [I] hwnd : window handle
4549 * [IO] lpLVItem : item info
4550 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4551 * if FALSE, the lpLVItem is a LPLVITEMA.
4553 * NOTE:
4554 * This is the internal 'GetItem' interface -- it tries to
4555 * be smart, and avoids text copies, if possible, by modifing
4556 * lpLVItem->pszText to point to the text string. Please note
4557 * that this is not always possible (e.g. OWNERDATA), so on
4558 * entry you *must* supply valid values for pszText, and cchTextMax.
4559 * The only difference to the documented interface is that upon
4560 * return, you should use *only* the lpLVItem->pszText, rather than
4561 * the buffer pointer you provided on input. Most code already does
4562 * that, so it's not a problem.
4563 * For the two cases when the text must be copied (that is,
4564 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4566 * RETURN:
4567 * SUCCESS : TRUE
4568 * FAILURE : FALSE
4570 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4572 NMLVDISPINFOW dispInfo;
4573 LISTVIEW_ITEM *lpItem;
4574 ITEMHDR* pItemHdr;
4575 HDPA hdpaSubItems;
4577 /* In the following:
4578 * lpLVItem describes the information requested by the user
4579 * lpItem is what we have
4580 * dispInfo is a structure we use to request the missing
4581 * information from the application
4584 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4586 if (!lpLVItem || (lpLVItem->iItem < 0) ||
4587 (lpLVItem->iItem >= infoPtr->nItemCount))
4588 return FALSE;
4590 /* a quick optimization if all we're asked is the focus state
4591 * these queries are worth optimising since they are common,
4592 * and can be answered in constant time, without the heavy accesses */
4593 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIF_STATE) &&
4594 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4596 lpLVItem->state = 0;
4597 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4598 lpLVItem->state |= LVIS_FOCUSED;
4599 return TRUE;
4602 ZeroMemory(&dispInfo, sizeof(dispInfo));
4604 /* if the app stores all the data, handle it separately */
4605 if (infoPtr->dwStyle & LVS_OWNERDATA)
4607 dispInfo.item.state = 0;
4609 /* if we need to callback, do it now */
4610 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4612 /* NOTE: copy only fields which we _know_ are initialized, some apps
4613 * depend on the uninitialized fields being 0 */
4614 dispInfo.item.mask = lpLVItem->mask;
4615 dispInfo.item.iItem = lpLVItem->iItem;
4616 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4617 if (lpLVItem->mask & LVIF_TEXT)
4619 dispInfo.item.pszText = lpLVItem->pszText;
4620 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4622 if (lpLVItem->mask & LVIF_STATE)
4623 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4624 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4625 dispInfo.item.stateMask = lpLVItem->stateMask;
4626 *lpLVItem = dispInfo.item;
4627 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4630 /* we store only a little state, so if we're not asked, we're done */
4631 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4633 /* if focus is handled by us, report it */
4634 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4636 lpLVItem->state &= ~LVIS_FOCUSED;
4637 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4638 lpLVItem->state |= LVIS_FOCUSED;
4641 /* and do the same for selection, if we handle it */
4642 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4644 lpLVItem->state &= ~LVIS_SELECTED;
4645 if (ranges_contain(infoPtr->hdpaSelectionRanges, lpLVItem->iItem))
4646 lpLVItem->state |= LVIS_SELECTED;
4649 return TRUE;
4652 /* find the item and subitem structures before we proceed */
4653 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4654 if (hdpaSubItems == NULL) return FALSE;
4656 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4657 return FALSE;
4659 if (lpLVItem->iSubItem)
4661 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4662 if(!lpSubItem) return FALSE;
4663 pItemHdr = &lpSubItem->hdr;
4665 else
4666 pItemHdr = &lpItem->hdr;
4668 /* Do we need to query the state from the app? */
4669 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4671 dispInfo.item.mask |= LVIF_STATE;
4672 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4675 /* Do we need to enquire about the image? */
4676 if ((lpLVItem->mask & LVIF_IMAGE) && (pItemHdr->iImage==I_IMAGECALLBACK))
4677 dispInfo.item.mask |= LVIF_IMAGE;
4679 /* Do we need to enquire about the text? */
4680 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4682 dispInfo.item.mask |= LVIF_TEXT;
4683 dispInfo.item.pszText = lpLVItem->pszText;
4684 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4685 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4686 *dispInfo.item.pszText = '\0';
4689 /* If we don't have all the requested info, query the application */
4690 if (dispInfo.item.mask != 0)
4692 dispInfo.item.iItem = lpLVItem->iItem;
4693 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4694 dispInfo.item.lParam = lpItem->lParam;
4695 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4696 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4699 /* Now, handle the iImage field */
4700 if (dispInfo.item.mask & LVIF_IMAGE)
4702 lpLVItem->iImage = dispInfo.item.iImage;
4703 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (pItemHdr->iImage==I_IMAGECALLBACK))
4704 pItemHdr->iImage = dispInfo.item.iImage;
4706 else if (lpLVItem->mask & LVIF_IMAGE)
4707 lpLVItem->iImage = pItemHdr->iImage;
4709 /* The pszText field */
4710 if (dispInfo.item.mask & LVIF_TEXT)
4712 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4713 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4715 lpLVItem->pszText = dispInfo.item.pszText;
4717 else if (lpLVItem->mask & LVIF_TEXT)
4719 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4720 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4723 /* if this is a subitem, we're done*/
4724 if (lpLVItem->iSubItem) return TRUE;
4726 /* Next is the lParam field */
4727 if (dispInfo.item.mask & LVIF_PARAM)
4729 lpLVItem->lParam = dispInfo.item.lParam;
4730 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4731 lpItem->lParam = dispInfo.item.lParam;
4733 else if (lpLVItem->mask & LVIF_PARAM)
4734 lpLVItem->lParam = lpItem->lParam;
4736 /* ... the state field (this one is different due to uCallbackmask) */
4737 if (lpLVItem->mask & LVIF_STATE)
4739 lpLVItem->state = lpItem->state;
4740 if (dispInfo.item.mask & LVIF_STATE)
4742 lpLVItem->state &= ~dispInfo.item.stateMask;
4743 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4745 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4747 lpLVItem->state &= ~LVIS_FOCUSED;
4748 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4749 lpLVItem->state |= LVIS_FOCUSED;
4751 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4753 lpLVItem->state &= ~LVIS_SELECTED;
4754 if (ranges_contain(infoPtr->hdpaSelectionRanges, lpLVItem->iItem))
4755 lpLVItem->state |= LVIS_SELECTED;
4759 /* and last, but not least, the indent field */
4760 if (lpLVItem->mask & LVIF_INDENT)
4761 lpLVItem->iIndent = lpItem->iIndent;
4763 return TRUE;
4766 /***
4767 * DESCRIPTION:
4768 * Retrieves item attributes.
4770 * PARAMETER(S):
4771 * [I] hwnd : window handle
4772 * [IO] lpLVItem : item info
4773 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4774 * if FALSE, the lpLVItem is a LPLVITEMA.
4776 * NOTE:
4777 * This is the external 'GetItem' interface -- it properly copies
4778 * the text in the provided buffer.
4780 * RETURN:
4781 * SUCCESS : TRUE
4782 * FAILURE : FALSE
4784 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4786 LPWSTR pszText;
4787 BOOL bResult;
4789 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4790 return FALSE;
4792 pszText = lpLVItem->pszText;
4793 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4794 if (bResult && lpLVItem->pszText != pszText)
4795 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4796 lpLVItem->pszText = pszText;
4798 return bResult;
4802 /***
4803 * DESCRIPTION:
4804 * Retrieves the position (upper-left) of the listview control item.
4805 * Note that for LVS_ICON style, the upper-left is that of the icon
4806 * and not the bounding box.
4808 * PARAMETER(S):
4809 * [I] infoPtr : valid pointer to the listview structure
4810 * [I] nItem : item index
4811 * [O] lpptPosition : coordinate information
4813 * RETURN:
4814 * SUCCESS : TRUE
4815 * FAILURE : FALSE
4817 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4819 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4820 POINT Origin;
4822 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4824 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4825 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition)) return FALSE;
4826 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
4828 if (uView == LVS_ICON)
4830 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
4831 lpptPosition->y += ICON_TOP_PADDING;
4833 lpptPosition->x += Origin.x;
4834 lpptPosition->y += Origin.y;
4836 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
4837 return TRUE;
4841 /***
4842 * DESCRIPTION:
4843 * Retrieves the bounding rectangle for a listview control item.
4845 * PARAMETER(S):
4846 * [I] infoPtr : valid pointer to the listview structure
4847 * [I] nItem : item index
4848 * [IO] lprc : bounding rectangle coordinates
4849 * lprc->left specifies the portion of the item for which the bounding
4850 * rectangle will be retrieved.
4852 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
4853 * including the icon and label.
4855 * * For LVS_ICON
4856 * * Experiment shows that native control returns:
4857 * * width = min (48, length of text line)
4858 * * .left = position.x - (width - iconsize.cx)/2
4859 * * .right = .left + width
4860 * * height = #lines of text * ntmHeight + icon height + 8
4861 * * .top = position.y - 2
4862 * * .bottom = .top + height
4863 * * separation between items .y = itemSpacing.cy - height
4864 * * .x = itemSpacing.cx - width
4865 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
4867 * * For LVS_ICON
4868 * * Experiment shows that native control returns:
4869 * * width = iconSize.cx + 16
4870 * * .left = position.x - (width - iconsize.cx)/2
4871 * * .right = .left + width
4872 * * height = iconSize.cy + 4
4873 * * .top = position.y - 2
4874 * * .bottom = .top + height
4875 * * separation between items .y = itemSpacing.cy - height
4876 * * .x = itemSpacing.cx - width
4877 * LVIR_LABEL Returns the bounding rectangle of the item text.
4879 * * For LVS_ICON
4880 * * Experiment shows that native control returns:
4881 * * width = text length
4882 * * .left = position.x - width/2
4883 * * .right = .left + width
4884 * * height = ntmH * linecount + 2
4885 * * .top = position.y + iconSize.cy + 6
4886 * * .bottom = .top + height
4887 * * separation between items .y = itemSpacing.cy - height
4888 * * .x = itemSpacing.cx - width
4889 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
4890 * rectangles, but excludes columns in report view.
4892 * RETURN:
4893 * SUCCESS : TRUE
4894 * FAILURE : FALSE
4896 * NOTES
4897 * Note that the bounding rectangle of the label in the LVS_ICON view depends
4898 * upon whether the window has the focus currently and on whether the item
4899 * is the one with the focus. Ensure that the control's record of which
4900 * item has the focus agrees with the items' records.
4902 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
4904 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4905 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4906 BOOL doLabel = TRUE, oversizedBox = FALSE;
4907 POINT Position, Origin;
4908 LVITEMW lvItem;
4909 RECT label_rect;
4911 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
4913 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4914 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
4915 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) return FALSE;
4917 /* Be smart and try to figure out the minimum we have to do */
4918 if (lprc->left == LVIR_ICON) doLabel = FALSE;
4919 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
4920 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
4921 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
4922 oversizedBox = TRUE;
4924 /* get what we need from the item before hand, so we make
4925 * only one request. This can speed up things, if data
4926 * is stored on the app side */
4927 lvItem.mask = 0;
4928 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
4929 if (doLabel) lvItem.mask |= LVIF_TEXT;
4930 lvItem.iItem = nItem;
4931 lvItem.iSubItem = 0;
4932 lvItem.pszText = szDispText;
4933 lvItem.cchTextMax = DISP_TEXT_SIZE;
4934 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
4935 /* we got the state already up, simulate it here, to avoid a reget */
4936 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
4938 lvItem.mask |= LVIF_STATE;
4939 lvItem.stateMask = LVIS_FOCUSED;
4940 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
4943 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
4944 lprc->left = LVIR_BOUNDS;
4945 switch(lprc->left)
4947 case LVIR_ICON:
4948 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL)) return FALSE;
4949 break;
4951 case LVIR_LABEL:
4952 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc)) return FALSE;
4953 break;
4955 case LVIR_BOUNDS:
4956 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL)) return FALSE;
4957 break;
4959 case LVIR_SELECTBOUNDS:
4960 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect)) return FALSE;
4961 UnionRect(lprc, lprc, &label_rect);
4962 break;
4964 default:
4965 WARN("Unknown value: %d\n", lprc->left);
4966 return FALSE;
4969 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
4971 TRACE(" rect=%s\n", debugrect(lprc));
4973 return TRUE;
4976 /***
4977 * DESCRIPTION:
4978 * Retrieves the spacing between listview control items.
4980 * PARAMETER(S):
4981 * [I] infoPtr : valid pointer to the listview structure
4982 * [IO] lprc : rectangle to receive the output
4983 * on input, lprc->top = nSubItem
4984 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
4986 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
4987 * not only those of the first column.
4988 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
4990 * RETURN:
4991 * TRUE: success
4992 * FALSE: failure
4994 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
4996 POINT Position, Origin;
4997 LVITEMW lvItem;
4999 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5001 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5003 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
5004 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5006 lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5007 lvItem.iItem = nItem;
5008 lvItem.iSubItem = lprc->top;
5010 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5011 switch(lprc->left)
5013 case LVIR_ICON:
5014 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL)) return FALSE;
5015 break;
5017 case LVIR_LABEL:
5018 case LVIR_BOUNDS:
5019 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL)) return FALSE;
5020 break;
5022 default:
5023 ERR("Unknown bounds=%d\n", lprc->left);
5024 return FALSE;
5027 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5028 return TRUE;
5032 /***
5033 * DESCRIPTION:
5034 * Retrieves the width of a label.
5036 * PARAMETER(S):
5037 * [I] infoPtr : valid pointer to the listview structure
5039 * RETURN:
5040 * SUCCESS : string width (in pixels)
5041 * FAILURE : zero
5043 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5045 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5046 LVITEMW lvItem;
5048 TRACE("(nItem=%d)\n", nItem);
5050 lvItem.mask = LVIF_TEXT;
5051 lvItem.iItem = nItem;
5052 lvItem.iSubItem = 0;
5053 lvItem.pszText = szDispText;
5054 lvItem.cchTextMax = DISP_TEXT_SIZE;
5055 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5057 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5060 /***
5061 * DESCRIPTION:
5062 * Retrieves the spacing between listview control items.
5064 * PARAMETER(S):
5065 * [I] infoPtr : valid pointer to the listview structure
5066 * [I] BOOL : flag for small or large icon
5068 * RETURN:
5069 * Horizontal + vertical spacing
5071 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5073 LONG lResult;
5075 if (!bSmall)
5077 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5079 else
5081 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5082 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5083 else
5084 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5086 return lResult;
5089 /***
5090 * DESCRIPTION:
5091 * Retrieves the state of a listview control item.
5093 * PARAMETER(S):
5094 * [I] infoPtr : valid pointer to the listview structure
5095 * [I] nItem : item index
5096 * [I] uMask : state mask
5098 * RETURN:
5099 * State specified by the mask.
5101 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5103 LVITEMW lvItem;
5105 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5107 lvItem.iItem = nItem;
5108 lvItem.iSubItem = 0;
5109 lvItem.mask = LVIF_STATE;
5110 lvItem.stateMask = uMask;
5111 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5113 return lvItem.state & uMask;
5116 /***
5117 * DESCRIPTION:
5118 * Retrieves the text of a listview control item or subitem.
5120 * PARAMETER(S):
5121 * [I] hwnd : window handle
5122 * [I] nItem : item index
5123 * [IO] lpLVItem : item information
5124 * [I] isW : TRUE if lpLVItem is Unicode
5126 * RETURN:
5127 * SUCCESS : string length
5128 * FAILURE : 0
5130 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5132 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5134 lpLVItem->mask = LVIF_TEXT;
5135 lpLVItem->iItem = nItem;
5136 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5138 return textlenT(lpLVItem->pszText, isW);
5141 /***
5142 * DESCRIPTION:
5143 * Searches for an item based on properties + relationships.
5145 * PARAMETER(S):
5146 * [I] infoPtr : valid pointer to the listview structure
5147 * [I] nItem : item index
5148 * [I] uFlags : relationship flag
5150 * FIXME:
5151 * This function is very, very inefficient! Needs work.
5153 * RETURN:
5154 * SUCCESS : item index
5155 * FAILURE : -1
5157 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5159 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5160 UINT uMask = 0;
5161 LVFINDINFOW lvFindInfo;
5162 INT nCountPerColumn;
5163 INT i;
5165 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5166 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5168 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5170 if (uFlags & LVNI_CUT)
5171 uMask |= LVIS_CUT;
5173 if (uFlags & LVNI_DROPHILITED)
5174 uMask |= LVIS_DROPHILITED;
5176 if (uFlags & LVNI_FOCUSED)
5177 uMask |= LVIS_FOCUSED;
5179 if (uFlags & LVNI_SELECTED)
5180 uMask |= LVIS_SELECTED;
5182 /* if we're asked for the focused item, that's only one,
5183 * so it's worth optimizing */
5184 if (uFlags & LVNI_FOCUSED)
5186 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5187 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5190 if (uFlags & LVNI_ABOVE)
5192 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5194 while (nItem >= 0)
5196 nItem--;
5197 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5198 return nItem;
5201 else
5203 lvFindInfo.flags = LVFI_NEARESTXY;
5204 lvFindInfo.vkDirection = VK_UP;
5205 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5206 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5208 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5209 return nItem;
5213 else if (uFlags & LVNI_BELOW)
5215 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5217 while (nItem < infoPtr->nItemCount)
5219 nItem++;
5220 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5221 return nItem;
5224 else
5226 lvFindInfo.flags = LVFI_NEARESTXY;
5227 lvFindInfo.vkDirection = VK_DOWN;
5228 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5229 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5231 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5232 return nItem;
5236 else if (uFlags & LVNI_TOLEFT)
5238 if (uView == LVS_LIST)
5240 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5241 while (nItem - nCountPerColumn >= 0)
5243 nItem -= nCountPerColumn;
5244 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5245 return nItem;
5248 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5250 lvFindInfo.flags = LVFI_NEARESTXY;
5251 lvFindInfo.vkDirection = VK_LEFT;
5252 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5253 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5255 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5256 return nItem;
5260 else if (uFlags & LVNI_TORIGHT)
5262 if (uView == LVS_LIST)
5264 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5265 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5267 nItem += nCountPerColumn;
5268 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5269 return nItem;
5272 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5274 lvFindInfo.flags = LVFI_NEARESTXY;
5275 lvFindInfo.vkDirection = VK_RIGHT;
5276 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5277 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5279 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5280 return nItem;
5284 else
5286 nItem++;
5288 /* search by index */
5289 for (i = nItem; i < infoPtr->nItemCount; i++)
5291 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5292 return i;
5296 return -1;
5299 /* LISTVIEW_GetNumberOfWorkAreas */
5301 /***
5302 * DESCRIPTION:
5303 * Retrieves the origin coordinates when in icon or small icon display mode.
5305 * PARAMETER(S):
5306 * [I] infoPtr : valid pointer to the listview structure
5307 * [O] lpptOrigin : coordinate information
5309 * RETURN:
5310 * SUCCESS : TRUE
5311 * FAILURE : FALSE
5313 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5315 DWORD lStyle = infoPtr->dwStyle;
5316 UINT uView = lStyle & LVS_TYPEMASK;
5317 INT nHorzPos = 0, nVertPos = 0;
5318 SCROLLINFO scrollInfo;
5320 if (!lpptOrigin) return FALSE;
5322 scrollInfo.cbSize = sizeof(SCROLLINFO);
5323 scrollInfo.fMask = SIF_POS;
5325 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5326 nHorzPos = scrollInfo.nPos;
5327 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5328 nVertPos = scrollInfo.nPos;
5330 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5332 lpptOrigin->x = infoPtr->rcList.left;
5333 lpptOrigin->y = infoPtr->rcList.top;
5334 if (uView == LVS_LIST)
5335 nHorzPos *= infoPtr->nItemWidth;
5336 else if (uView == LVS_REPORT)
5337 nVertPos *= infoPtr->nItemHeight;
5339 lpptOrigin->x -= nHorzPos;
5340 lpptOrigin->y -= nVertPos;
5342 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5344 return TRUE;
5347 /***
5348 * DESCRIPTION:
5349 * Retrieves the width of a string.
5351 * PARAMETER(S):
5352 * [I] hwnd : window handle
5353 * [I] lpszText : text string to process
5354 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5356 * RETURN:
5357 * SUCCESS : string width (in pixels)
5358 * FAILURE : zero
5360 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5362 SIZE stringSize;
5364 stringSize.cx = 0;
5365 if (is_textT(lpszText, isW))
5367 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5368 HDC hdc = GetDC(infoPtr->hwndSelf);
5369 HFONT hOldFont = SelectObject(hdc, hFont);
5371 if (isW)
5372 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5373 else
5374 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5375 SelectObject(hdc, hOldFont);
5376 ReleaseDC(infoPtr->hwndSelf, hdc);
5378 return stringSize.cx;
5381 /***
5382 * DESCRIPTION:
5383 * Determines which listview item is located at the specified position.
5385 * PARAMETER(S):
5386 * [I] infoPtr : valid pointer to the listview structure
5387 * [IO] lpht : hit test information
5388 * [I] subitem : fill out iSubItem.
5389 * [I] select : return the index only if the hit selects the item
5391 * NOTE:
5392 * (mm 20001022): We must not allow iSubItem to be touched, for
5393 * an app might pass only a structure with space up to iItem!
5394 * (MS Office 97 does that for instance in the file open dialog)
5396 * RETURN:
5397 * SUCCESS : item index
5398 * FAILURE : -1
5400 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5402 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5403 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5404 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5405 POINT Origin, Position, opt;
5406 LVITEMW lvItem;
5407 ITERATOR i;
5409 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5411 lpht->flags = 0;
5412 lpht->iItem = -1;
5413 if (subitem) lpht->iSubItem = 0;
5415 if (infoPtr->rcList.left > lpht->pt.x)
5416 lpht->flags |= LVHT_TOLEFT;
5417 else if (infoPtr->rcList.right < lpht->pt.x)
5418 lpht->flags |= LVHT_TORIGHT;
5420 if (infoPtr->rcList.top > lpht->pt.y)
5421 lpht->flags |= LVHT_ABOVE;
5422 else if (infoPtr->rcList.bottom < lpht->pt.y)
5423 lpht->flags |= LVHT_BELOW;
5425 TRACE("lpht->flags=0x%x\n", lpht->flags);
5426 if (lpht->flags) return -1;
5428 lpht->flags |= LVHT_NOWHERE;
5430 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
5432 /* first deal with the large items */
5433 rcSearch.left = lpht->pt.x;
5434 rcSearch.top = lpht->pt.y;
5435 rcSearch.right = rcSearch.left + 1;
5436 rcSearch.bottom = rcSearch.top + 1;
5438 iterator_frameditems(&i, infoPtr, &rcSearch);
5439 iterator_next(&i); /* go to first item in the sequence */
5440 lpht->iItem = i.nItem;
5441 iterator_destroy(&i);
5443 TRACE("lpht->iItem=%d\n", lpht->iItem);
5444 if (lpht->iItem == -1) return -1;
5446 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5447 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5448 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5449 if (uView == LVS_ICON && infoPtr->bFocus) lvItem.stateMask |= LVIS_FOCUSED;
5450 lvItem.iItem = lpht->iItem;
5451 lvItem.iSubItem = 0;
5452 lvItem.pszText = szDispText;
5453 lvItem.cchTextMax = DISP_TEXT_SIZE;
5454 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5456 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel)) return -1;
5457 if (!LISTVIEW_GetItemOrigin(infoPtr, lpht->iItem, &Position)) return -1;
5458 opt.x = lpht->pt.x - Position.x - Origin.x;
5459 opt.y = lpht->pt.y - Position.y - Origin.y;
5461 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
5462 rcBounds = rcBox;
5463 else
5464 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5465 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5466 if (!PtInRect(&rcBounds, opt)) return -1;
5468 if (PtInRect(&rcIcon, opt))
5469 lpht->flags |= LVHT_ONITEMICON;
5470 else if (PtInRect(&rcLabel, opt))
5471 lpht->flags |= LVHT_ONITEMLABEL;
5472 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5473 lpht->flags |= LVHT_ONITEMSTATEICON;
5474 if (lpht->flags & LVHT_ONITEM)
5475 lpht->flags &= ~LVHT_NOWHERE;
5477 TRACE("lpht->flags=0x%x\n", lpht->flags);
5478 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5480 INT j, nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5481 rcBounds.right = rcBounds.left;
5482 for (j = 0; j < nColumnCount; j++)
5484 rcBounds.left = rcBounds.right;
5485 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5486 if (PtInRect(&rcBounds, opt))
5488 lpht->iSubItem = j;
5489 break;
5494 if (!select || lpht->iItem == -1) return lpht->iItem;
5496 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5498 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5499 return PtInRect(&rcBounds, opt) ? lpht->iItem : -1;
5503 /***
5504 * DESCRIPTION:
5505 * Inserts a new column.
5507 * PARAMETER(S):
5508 * [I] infoPtr : valid pointer to the listview structure
5509 * [I] INT : column index
5510 * [I] LPLVCOLUMNW : column information
5512 * RETURN:
5513 * SUCCESS : new column index
5514 * FAILURE : -1
5516 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5517 LPLVCOLUMNW lpColumn, BOOL isW)
5519 RECT rcOld, rcCol;
5520 INT nNewColumn;
5521 HDITEMW hdi;
5523 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5525 if (!lpColumn) return -1;
5527 hdi.mask = hdi.fmt = 0;
5528 if (lpColumn->mask & LVCF_FMT)
5530 /* format member is valid */
5531 hdi.mask |= HDI_FORMAT;
5533 /* set text alignment (leftmost column must be left-aligned) */
5534 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5535 hdi.fmt |= HDF_LEFT;
5536 else if (lpColumn->fmt & LVCFMT_RIGHT)
5537 hdi.fmt |= HDF_RIGHT;
5538 else if (lpColumn->fmt & LVCFMT_CENTER)
5539 hdi.fmt |= HDF_CENTER;
5541 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5542 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5544 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5546 hdi.fmt |= HDF_IMAGE;
5547 hdi.iImage = I_IMAGECALLBACK;
5550 if (lpColumn->fmt & LVCFMT_IMAGE)
5551 ; /* FIXME: enable images for *(sub)items* this column */
5554 if (lpColumn->mask & LVCF_WIDTH)
5556 hdi.mask |= HDI_WIDTH;
5557 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5559 /* make it fill the remainder of the controls width */
5560 HDITEMW hdit;
5561 RECT rcHeader;
5562 INT item_index;
5564 /* get the width of every item except the current one */
5565 hdit.mask = HDI_WIDTH;
5566 hdi.cxy = 0;
5568 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5569 if (Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit)))
5570 hdi.cxy += hdit.cxy;
5572 /* retrieve the layout of the header */
5573 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5574 TRACE("start cxy=%d rcHeader=%s\n", hdi.cxy, debugrect(&rcHeader));
5576 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
5578 else
5579 hdi.cxy = lpColumn->cx;
5582 if (lpColumn->mask & LVCF_TEXT)
5584 hdi.mask |= HDI_TEXT | HDI_FORMAT;
5585 hdi.fmt |= HDF_STRING;
5586 hdi.pszText = lpColumn->pszText;
5587 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
5590 if (lpColumn->mask & LVCF_IMAGE)
5592 hdi.mask |= HDI_IMAGE;
5593 hdi.iImage = lpColumn->iImage;
5596 if (lpColumn->mask & LVCF_ORDER)
5598 hdi.mask |= HDI_ORDER;
5599 hdi.iOrder = lpColumn->iOrder;
5602 /* insert item in header control */
5603 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5604 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5605 (WPARAM)nColumn, (LPARAM)&hdi);
5606 if (nNewColumn == -1) return -1;
5607 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &rcCol)) return -1;
5609 /* now we have to actually adjust the data */
5610 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
5612 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
5613 HDPA hdpaSubItems;
5614 INT nItem, i;
5616 /* preallocate memory, so we can fail gracefully */
5617 if (nNewColumn == 0)
5619 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->nItemCount);
5620 if (!lpNewItems) return -1;
5621 for (i = 0; i < infoPtr->nItemCount; i++)
5622 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
5623 if (i != infoPtr->nItemCount)
5625 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5626 COMCTL32_Free(lpNewItems);
5627 return -1;
5631 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5633 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5634 if (!hdpaSubItems) continue;
5635 for (i = 1; i < hdpaSubItems->nItemCount; i++)
5637 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
5638 if (!lpSubItem) break;
5639 if (lpSubItem->iSubItem >= nNewColumn)
5640 lpSubItem->iSubItem++;
5643 /* if we found our subitem, zapp it */
5644 if (nNewColumn == 0)
5646 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
5647 lpSubItem = lpNewItems[nItem];
5648 lpSubItem->hdr = lpMainItem->hdr;
5649 lpSubItem->iSubItem = 1;
5650 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
5651 lpMainItem->iSubItem = 0;
5652 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
5656 COMCTL32_Free(lpNewItems);
5659 /* we don't have to worry abiut display issues in non-report mode */
5660 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return nNewColumn;
5662 /* if we have a focus, must first erase the focus rect */
5663 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
5665 /* Need to reset the item width when inserting a new column */
5666 infoPtr->nItemWidth += rcCol.right - rcCol.left;
5668 LISTVIEW_UpdateScroll(infoPtr);
5670 /* scroll to cover the deleted column, and invalidate for redraw */
5671 rcOld = infoPtr->rcList;
5672 rcOld.left = rcCol.left;
5673 ScrollWindowEx(infoPtr->hwndSelf, rcCol.right - rcCol.left, 0,
5674 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5676 /* we can restore focus now */
5677 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
5679 return nNewColumn;
5682 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5683 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5684 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5685 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5686 their own sort proc. when sending LVM_SORTITEMS.
5688 /* Platform SDK:
5689 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5691 LVS_SORTXXX must be specified,
5692 LVS_OWNERDRAW is not set,
5693 <item>.pszText is not LPSTR_TEXTCALLBACK.
5695 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5696 are sorted based on item text..."
5698 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5700 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5701 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5702 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5704 /* if we're sorting descending, negate the return value */
5705 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5708 /***
5709 * nESCRIPTION:
5710 * Inserts a new item in the listview control.
5712 * PARAMETER(S):
5713 * [I] infoPtr : valid pointer to the listview structure
5714 * [I] lpLVItem : item information
5715 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5717 * RETURN:
5718 * SUCCESS : new item index
5719 * FAILURE : -1
5721 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5723 LONG lStyle = infoPtr->dwStyle;
5724 UINT uView = lStyle & LVS_TYPEMASK;
5725 INT nItem = -1;
5726 HDPA hdpaSubItems;
5727 NMLISTVIEW nmlv;
5728 LISTVIEW_ITEM *lpItem;
5729 BOOL is_sorted;
5731 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5733 if (lStyle & LVS_OWNERDATA)
5735 nItem = infoPtr->nItemCount;
5736 infoPtr->nItemCount++;
5737 return nItem;
5740 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5741 if (!lpLVItem || lpLVItem->iSubItem) return -1;
5743 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5745 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5746 return -1;
5748 /* insert item in listview control data structure */
5749 if ( (hdpaSubItems = DPA_Create(8)) )
5750 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5751 if (nItem == -1) goto fail;
5753 /* FIXME: is the handling of this LVS_OWNERDRAWFIXED correct? */
5754 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5755 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5757 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
5758 is_sorted ? infoPtr->nItemCount + 1 : lpLVItem->iItem,
5759 hdpaSubItems );
5760 if (nItem == -1) goto fail;
5761 infoPtr->nItemCount++;
5763 if (!LISTVIEW_SetItemT(infoPtr, lpLVItem, isW))
5764 goto undo;
5766 /* if we're sorted, sort the list, and update the index */
5767 if (is_sorted)
5769 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5770 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5771 if (nItem == -1)
5773 ERR("We can't find the item we just inserted, possible memory corruption.");
5774 /* we can't remove it from the list if we can't find it, so just fail */
5775 /* we don't deallocate memory here, as it will probably cause more problems */
5776 return -1;
5780 /* make room for the position, if we are in the right mode */
5781 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5783 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5784 goto undo;
5785 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5787 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5788 goto undo;
5792 /* Add the subitem list to the items array. Do this last in case we go to
5793 * fail during the above.
5795 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5797 lpItem->valid = TRUE;
5799 /* send LVN_INSERTITEM notification */
5800 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5801 nmlv.iItem = nItem;
5802 nmlv.lParam = lpItem->lParam;
5803 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5805 /* align items (set position of each item) */
5806 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5808 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
5809 else LISTVIEW_AlignTop(infoPtr);
5812 LISTVIEW_UpdateScroll(infoPtr);
5814 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
5816 TRACE(" <- %d\n", nItem);
5817 return nItem;
5819 undo:
5820 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5821 infoPtr->nItemCount--;
5822 fail:
5823 DPA_DeletePtr(hdpaSubItems, 0);
5824 DPA_Destroy (hdpaSubItems);
5825 COMCTL32_Free (lpItem);
5826 return -1;
5829 /***
5830 * DESCRIPTION:
5831 * Redraws a range of items.
5833 * PARAMETER(S):
5834 * [I] infoPtr : valid pointer to the listview structure
5835 * [I] INT : first item
5836 * [I] INT : last item
5838 * RETURN:
5839 * SUCCESS : TRUE
5840 * FAILURE : FALSE
5842 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
5844 INT i;
5846 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
5847 max(nFirst, nLast) >= infoPtr->nItemCount)
5848 return FALSE;
5850 for (i = nFirst; i <= nLast; i++)
5851 LISTVIEW_InvalidateItem(infoPtr, i);
5853 return TRUE;
5856 /***
5857 * DESCRIPTION:
5858 * Scroll the content of a listview.
5860 * PARAMETER(S):
5861 * [I] infoPtr : valid pointer to the listview structure
5862 * [I] INT : horizontal scroll amount in pixels
5863 * [I] INT : vertical scroll amount in pixels
5865 * RETURN:
5866 * SUCCESS : TRUE
5867 * FAILURE : FALSE
5869 * COMMENTS:
5870 * If the control is in report mode (LVS_REPORT) the control can
5871 * be scrolled only in line increments. "dy" will be rounded to the
5872 * nearest number of pixels that are a whole line. Ex: if line height
5873 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
5874 * is passed the the scroll will be 0. (per MSDN 7/2002)
5876 * For: (per experimentaion with native control and CSpy ListView)
5877 * LVS_ICON dy=1 = 1 pixel (vertical only)
5878 * dx ignored
5879 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
5880 * dx ignored
5881 * LVS_LIST dx=1 = 1 column (horizontal only)
5882 * but will only scroll 1 column per message
5883 * no matter what the value.
5884 * dy must be 0 or FALSE returned.
5885 * LVS_REPORT dx=1 = 1 pixel
5886 * dy= see above
5889 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
5891 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
5892 case LVS_REPORT:
5893 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
5894 dy /= infoPtr->nItemHeight;
5895 break;
5896 case LVS_LIST:
5897 if (dy != 0) return FALSE;
5898 break;
5899 default: /* icon */
5900 dx = 0;
5901 break;
5904 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
5905 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
5907 return TRUE;
5910 /***
5911 * DESCRIPTION:
5912 * Sets the background color.
5914 * PARAMETER(S):
5915 * [I] infoPtr : valid pointer to the listview structure
5916 * [I] COLORREF : background color
5918 * RETURN:
5919 * SUCCESS : TRUE
5920 * FAILURE : FALSE
5922 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
5924 TRACE("(clrBk=%lx)\n", clrBk);
5926 if(infoPtr->clrBk != clrBk) {
5927 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
5928 infoPtr->clrBk = clrBk;
5929 if (clrBk == CLR_NONE)
5930 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
5931 else
5932 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
5933 LISTVIEW_InvalidateList(infoPtr);
5936 return TRUE;
5939 /* LISTVIEW_SetBkImage */
5941 /***
5942 * DESCRIPTION:
5943 * Sets the attributes of a header item.
5945 * PARAMETER(S):
5946 * [I] infoPtr : valid pointer to the listview structure
5947 * [I] INT : column index
5948 * [I] LPLVCOLUMNW : column attributes
5949 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
5950 * otherwise it is in fact a LPLVCOLUMNA
5952 * RETURN:
5953 * SUCCESS : TRUE
5954 * FAILURE : FALSE
5956 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5957 LPLVCOLUMNW lpColumn, BOOL isW)
5959 BOOL bResult = FALSE;
5960 HDITEMW hdi, hdiget;
5962 if ((lpColumn != NULL) && (nColumn >= 0) &&
5963 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
5965 /* initialize memory */
5966 ZeroMemory(&hdi, sizeof(hdi));
5968 if (lpColumn->mask & LVCF_FMT)
5970 /* format member is valid */
5971 hdi.mask |= HDI_FORMAT;
5973 /* get current format first */
5974 hdiget.mask = HDI_FORMAT;
5975 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
5976 /* preserve HDF_STRING if present */
5977 hdi.fmt = hdiget.fmt & HDF_STRING;
5979 /* set text alignment (leftmost column must be left-aligned) */
5980 if (nColumn == 0)
5982 hdi.fmt |= HDF_LEFT;
5984 else
5986 if (lpColumn->fmt & LVCFMT_LEFT)
5987 hdi.fmt |= HDF_LEFT;
5988 else if (lpColumn->fmt & LVCFMT_RIGHT)
5989 hdi.fmt |= HDF_RIGHT;
5990 else if (lpColumn->fmt & LVCFMT_CENTER)
5991 hdi.fmt |= HDF_CENTER;
5994 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5995 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5997 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5998 hdi.fmt |= HDF_IMAGE;
6000 if (lpColumn->fmt & LVCFMT_IMAGE)
6002 hdi.fmt |= HDF_IMAGE;
6003 hdi.iImage = I_IMAGECALLBACK;
6007 if (lpColumn->mask & LVCF_WIDTH)
6009 hdi.mask |= HDI_WIDTH;
6010 hdi.cxy = lpColumn->cx;
6013 if (lpColumn->mask & LVCF_TEXT)
6015 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6016 hdi.pszText = lpColumn->pszText;
6017 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6018 hdi.fmt |= HDF_STRING;
6021 if (lpColumn->mask & LVCF_IMAGE)
6023 hdi.mask |= HDI_IMAGE;
6024 hdi.iImage = lpColumn->iImage;
6027 if (lpColumn->mask & LVCF_ORDER)
6029 hdi.mask |= HDI_ORDER;
6030 hdi.iOrder = lpColumn->iOrder;
6033 /* set header item attributes */
6034 if (isW)
6035 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6036 else
6037 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6040 return bResult;
6043 /***
6044 * DESCRIPTION:
6045 * Sets the column order array
6047 * PARAMETERS:
6048 * [I] infoPtr : valid pointer to the listview structure
6049 * [I] INT : number of elements in column order array
6050 * [I] INT : pointer to column order array
6052 * RETURN:
6053 * SUCCESS : TRUE
6054 * FAILURE : FALSE
6056 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6058 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6060 if (!lpiArray)
6061 return FALSE;
6063 return TRUE;
6067 /***
6068 * DESCRIPTION:
6069 * Sets the width of a column
6071 * PARAMETERS:
6072 * [I] infoPtr : valid pointer to the listview structure
6073 * [I] INT : column index
6074 * [I] INT : column width
6076 * RETURN:
6077 * SUCCESS : TRUE
6078 * FAILURE : FALSE
6080 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6082 HDITEMW hdi;
6083 LRESULT lret;
6084 LONG lStyle = infoPtr->dwStyle;
6085 UINT uView = lStyle & LVS_TYPEMASK;
6086 HDC hdc;
6087 HFONT header_font;
6088 HFONT old_font;
6089 SIZE size;
6090 WCHAR text_buffer[DISP_TEXT_SIZE];
6091 INT header_item_count;
6092 INT item_index;
6093 INT nLabelWidth;
6094 RECT rcHeader;
6095 LVITEMW lvItem;
6096 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6098 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6100 /* set column width only if in report or list mode */
6101 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6103 /* take care of invalid cx values */
6104 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6105 else if (uView == LVS_LIST && cx < 1) return FALSE;
6107 /* resize all columns if in LVS_LIST mode */
6108 if(uView == LVS_LIST)
6110 infoPtr->nItemWidth = cx;
6111 LISTVIEW_InvalidateList(infoPtr);
6112 return TRUE;
6115 /* autosize based on listview items width */
6116 if(cx == LVSCW_AUTOSIZE)
6118 /* set the width of the column to the width of the widest item */
6119 if (iCol == 0 || uView == LVS_LIST)
6121 cx = 0;
6122 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6124 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6125 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6127 if (infoPtr->himlSmall)
6128 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6130 else
6132 lvItem.iSubItem = iCol;
6133 lvItem.mask = LVIF_TEXT;
6134 lvItem.pszText = szDispText;
6135 lvItem.cchTextMax = DISP_TEXT_SIZE;
6136 cx = 0;
6137 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6139 lvItem.iItem = item_index;
6140 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6141 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6142 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6145 cx += TRAILING_PADDING;
6146 } /* autosize based on listview header width */
6147 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6149 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6151 /* if iCol is the last column make it fill the remainder of the controls width */
6152 if(iCol == (header_item_count - 1)) {
6153 /* get the width of every item except the current one */
6154 hdi.mask = HDI_WIDTH;
6155 cx = 0;
6157 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6158 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6159 cx+=hdi.cxy;
6162 /* retrieve the layout of the header */
6163 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6165 cx = (rcHeader.right - rcHeader.left) - cx;
6167 else
6169 /* Despite what the MS docs say, if this is not the last
6170 column, then MS resizes the column to the width of the
6171 largest text string in the column, including headers
6172 and items. This is different from LVSCW_AUTOSIZE in that
6173 LVSCW_AUTOSIZE ignores the header string length.
6176 /* retrieve header font */
6177 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6179 /* retrieve header text */
6180 hdi.mask = HDI_TEXT;
6181 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6182 hdi.pszText = text_buffer;
6184 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6186 /* determine the width of the text in the header */
6187 hdc = GetDC(infoPtr->hwndSelf);
6188 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6190 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6192 SelectObject(hdc, old_font); /* restore the old font */
6193 ReleaseDC(infoPtr->hwndSelf, hdc);
6195 lvItem.iSubItem = iCol;
6196 lvItem.mask = LVIF_TEXT;
6197 lvItem.pszText = szDispText;
6198 lvItem.cchTextMax = DISP_TEXT_SIZE;
6199 cx = size.cx;
6200 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6202 lvItem.iItem = item_index;
6203 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6204 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6205 nLabelWidth += TRAILING_PADDING;
6206 /* While it is possible for subitems to have icons, even MS messes
6207 up the positioning, so I suspect no applications actually use
6208 them. */
6209 if (item_index == 0 && infoPtr->himlSmall)
6210 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6211 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6216 /* call header to update the column change */
6217 hdi.mask = HDI_WIDTH;
6219 hdi.cxy = cx;
6220 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6222 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6224 return lret;
6227 /***
6228 * DESCRIPTION:
6229 * Sets the extended listview style.
6231 * PARAMETERS:
6232 * [I] infoPtr : valid pointer to the listview structure
6233 * [I] DWORD : mask
6234 * [I] DWORD : style
6236 * RETURN:
6237 * SUCCESS : previous style
6238 * FAILURE : 0
6240 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6242 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6244 /* set new style */
6245 if (dwMask)
6246 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6247 else
6248 infoPtr->dwLvExStyle = dwStyle;
6250 return dwOldStyle;
6253 /***
6254 * DESCRIPTION:
6255 * Sets the new hot cursor used during hot tracking and hover selection.
6257 * PARAMETER(S):
6258 * [I] infoPtr : valid pointer to the listview structure
6259 * [I} hCurosr : the new hot cursor handle
6261 * RETURN:
6262 * Returns the previous hot cursor
6264 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6266 HCURSOR oldCursor = infoPtr->hHotCursor;
6267 infoPtr->hHotCursor = hCursor;
6268 return oldCursor;
6272 /***
6273 * DESCRIPTION:
6274 * Sets the hot item index.
6276 * PARAMETERS:
6277 * [I] infoPtr : valid pointer to the listview structure
6278 * [I] INT : index
6280 * RETURN:
6281 * SUCCESS : previous hot item index
6282 * FAILURE : -1 (no hot item)
6284 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6286 INT iOldIndex = infoPtr->nHotItem;
6287 infoPtr->nHotItem = iIndex;
6288 return iOldIndex;
6292 /***
6293 * DESCRIPTION:
6294 * Sets the amount of time the cursor must hover over an item before it is selected.
6296 * PARAMETER(S):
6297 * [I] infoPtr : valid pointer to the listview structure
6298 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6300 * RETURN:
6301 * Returns the previous hover time
6303 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6305 DWORD oldHoverTime = infoPtr->dwHoverTime;
6306 infoPtr->dwHoverTime = dwHoverTime;
6307 return oldHoverTime;
6310 /***
6311 * DESCRIPTION:
6312 * Sets spacing for icons of LVS_ICON style.
6314 * PARAMETER(S):
6315 * [I] infoPtr : valid pointer to the listview structure
6316 * [I] DWORD : MAKELONG(cx, cy)
6318 * RETURN:
6319 * MAKELONG(oldcx, oldcy)
6321 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6323 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6324 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6325 LONG lStyle = infoPtr->dwStyle;
6326 UINT uView = lStyle & LVS_TYPEMASK;
6328 TRACE("requested=(%d,%d)\n", cx, cy);
6330 /* this is supported only for LVS_ICON style */
6331 if (uView != LVS_ICON) return oldspacing;
6333 /* set to defaults, if instructed to */
6334 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6335 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6337 /* if 0 then compute width
6338 * FIXME: Should scan each item and determine max width of
6339 * icon or label, then make that the width */
6340 if (cx == 0)
6341 cx = infoPtr->iconSpacing.cx;
6343 /* if 0 then compute height */
6344 if (cy == 0)
6345 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6346 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6349 infoPtr->iconSpacing.cx = cx;
6350 infoPtr->iconSpacing.cy = cy;
6352 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6353 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6354 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6355 infoPtr->ntmHeight);
6357 /* these depend on the iconSpacing */
6358 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6359 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6361 return oldspacing;
6364 inline void update_icon_size(HIMAGELIST himl, SIZE *size)
6366 INT cx, cy;
6368 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6370 size->cx = cx;
6371 size->cy = cy;
6373 else
6374 size->cx = size->cy = 0;
6377 /***
6378 * DESCRIPTION:
6379 * Sets image lists.
6381 * PARAMETER(S):
6382 * [I] infoPtr : valid pointer to the listview structure
6383 * [I] INT : image list type
6384 * [I] HIMAGELIST : image list handle
6386 * RETURN:
6387 * SUCCESS : old image list
6388 * FAILURE : NULL
6390 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6392 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6393 INT oldHeight = infoPtr->nItemHeight;
6394 HIMAGELIST himlOld = 0;
6396 switch (nType)
6398 case LVSIL_NORMAL:
6399 himlOld = infoPtr->himlNormal;
6400 infoPtr->himlNormal = himl;
6401 if (uView == LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6402 LISTVIEW_SetIconSpacing(infoPtr, 0);
6403 break;
6405 case LVSIL_SMALL:
6406 himlOld = infoPtr->himlSmall;
6407 infoPtr->himlSmall = himl;
6408 if (uView != LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6409 break;
6411 case LVSIL_STATE:
6412 himlOld = infoPtr->himlState;
6413 infoPtr->himlState = himl;
6414 update_icon_size(himl, &infoPtr->iconStateSize);
6415 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6416 break;
6418 default:
6419 ERR("Unknown icon type=%d\n", nType);
6420 return NULL;
6423 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6424 if (infoPtr->nItemHeight != oldHeight)
6425 LISTVIEW_UpdateScroll(infoPtr);
6427 return himlOld;
6430 /***
6431 * DESCRIPTION:
6432 * Preallocates memory (does *not* set the actual count of items !)
6434 * PARAMETER(S):
6435 * [I] infoPtr : valid pointer to the listview structure
6436 * [I] INT : item count (projected number of items to allocate)
6437 * [I] DWORD : update flags
6439 * RETURN:
6440 * SUCCESS : TRUE
6441 * FAILURE : FALSE
6443 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6445 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6447 if (infoPtr->dwStyle & LVS_OWNERDATA)
6449 int precount,topvisible;
6451 TRACE("LVS_OWNERDATA is set!\n");
6452 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6453 FIXME("flags %s %s not implemented\n",
6454 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6455 : "",
6456 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6458 LISTVIEW_RemoveAllSelections(infoPtr);
6460 precount = infoPtr->nItemCount;
6461 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6462 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6464 infoPtr->nItemCount = nItems;
6465 infoPtr->nItemWidth = max(LISTVIEW_CalculateMaxWidth(infoPtr),
6466 DEFAULT_COLUMN_WIDTH);
6468 LISTVIEW_UpdateSize(infoPtr);
6469 LISTVIEW_UpdateScroll(infoPtr);
6471 if (min(precount,infoPtr->nItemCount) < topvisible)
6472 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6474 else
6476 /* According to MSDN for non-LVS_OWNERDATA this is just
6477 * a performance issue. The control allocates its internal
6478 * data structures for the number of items specified. It
6479 * cuts down on the number of memory allocations. Therefore
6480 * we will just issue a WARN here
6482 WARN("for non-ownerdata performance option not implemented.\n");
6485 return TRUE;
6488 /***
6489 * DESCRIPTION:
6490 * Sets the position of an item.
6492 * PARAMETER(S):
6493 * [I] infoPtr : valid pointer to the listview structure
6494 * [I] nItem : item index
6495 * [I] pt : coordinate
6497 * RETURN:
6498 * SUCCESS : TRUE
6499 * FAILURE : FALSE
6501 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6503 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6504 POINT old;
6506 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6508 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6509 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6511 /* This point value seems to be an undocumented feature.
6512 * The best guess is that it means either at the origin,
6513 * or at true beginning of the list. I will assume the origin. */
6514 if ((pt.x == -1) && (pt.y == -1))
6515 LISTVIEW_GetOrigin(infoPtr, &pt);
6516 else if (uView == LVS_ICON)
6518 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6519 pt.y -= ICON_TOP_PADDING;
6522 /* save the old position */
6523 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
6524 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
6526 /* Is the position changing? */
6527 if (pt.x == old.x && pt.y == old.y) return TRUE;
6529 /* FIXME: shouldn't we invalidate, as the item moved? */
6531 /* Allocating a POINTER for every item is too resource intensive,
6532 * so we'll keep the (x,y) in different arrays */
6533 if (DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)pt.x) &&
6534 DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)pt.y) )
6535 return TRUE;
6537 ERR("We should never fail here (nItem=%d, pt=%s), please report.\n",
6538 nItem, debugpoint(&pt));
6539 return FALSE;
6542 /***
6543 * DESCRIPTION:
6544 * Sets the state of one or many items.
6546 * PARAMETER(S):
6547 * [I] infoPtr : valid pointer to the listview structure
6548 * [I]INT : item index
6549 * [I] LPLVITEM : item or subitem info
6551 * RETURN:
6552 * SUCCESS : TRUE
6553 * FAILURE : FALSE
6555 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6557 BOOL bResult = TRUE;
6558 LVITEMW lvItem;
6560 lvItem.iItem = nItem;
6561 lvItem.iSubItem = 0;
6562 lvItem.mask = LVIF_STATE;
6563 lvItem.state = lpLVItem->state;
6564 lvItem.stateMask = lpLVItem->stateMask;
6565 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6567 if (nItem == -1)
6569 /* apply to all items */
6570 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6571 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6573 else
6574 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6576 return bResult;
6579 /***
6580 * DESCRIPTION:
6581 * Sets the text of an item or subitem.
6583 * PARAMETER(S):
6584 * [I] hwnd : window handle
6585 * [I] nItem : item index
6586 * [I] lpLVItem : item or subitem info
6587 * [I] isW : TRUE if input is Unicode
6589 * RETURN:
6590 * SUCCESS : TRUE
6591 * FAILURE : FALSE
6593 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6595 LVITEMW lvItem;
6597 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6599 lvItem.iItem = nItem;
6600 lvItem.iSubItem = lpLVItem->iSubItem;
6601 lvItem.mask = LVIF_TEXT;
6602 lvItem.pszText = lpLVItem->pszText;
6603 lvItem.cchTextMax = lpLVItem->cchTextMax;
6605 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6607 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6610 /***
6611 * DESCRIPTION:
6612 * Set item index that marks the start of a multiple selection.
6614 * PARAMETER(S):
6615 * [I] infoPtr : valid pointer to the listview structure
6616 * [I] INT : index
6618 * RETURN:
6619 * Index number or -1 if there is no selection mark.
6621 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6623 INT nOldIndex = infoPtr->nSelectionMark;
6625 TRACE("(nIndex=%d)\n", nIndex);
6627 infoPtr->nSelectionMark = nIndex;
6629 return nOldIndex;
6632 /***
6633 * DESCRIPTION:
6634 * Sets the text background color.
6636 * PARAMETER(S):
6637 * [I] infoPtr : valid pointer to the listview structure
6638 * [I] COLORREF : text background color
6640 * RETURN:
6641 * SUCCESS : TRUE
6642 * FAILURE : FALSE
6644 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6646 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6648 if (infoPtr->clrTextBk != clrTextBk)
6650 infoPtr->clrTextBk = clrTextBk;
6651 LISTVIEW_InvalidateList(infoPtr);
6654 return TRUE;
6657 /***
6658 * DESCRIPTION:
6659 * Sets the text foreground color.
6661 * PARAMETER(S):
6662 * [I] infoPtr : valid pointer to the listview structure
6663 * [I] COLORREF : text color
6665 * RETURN:
6666 * SUCCESS : TRUE
6667 * FAILURE : FALSE
6669 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6671 TRACE("(clrText=%lx)\n", clrText);
6673 if (infoPtr->clrText != clrText)
6675 infoPtr->clrText = clrText;
6676 LISTVIEW_InvalidateList(infoPtr);
6679 return TRUE;
6682 /* LISTVIEW_SetToolTips */
6683 /* LISTVIEW_SetUnicodeFormat */
6684 /* LISTVIEW_SetWorkAreas */
6686 /***
6687 * DESCRIPTION:
6688 * Callback internally used by LISTVIEW_SortItems()
6690 * PARAMETER(S):
6691 * [I] LPVOID : first LISTVIEW_ITEM to compare
6692 * [I] LPVOID : second LISTVIEW_ITEM to compare
6693 * [I] LPARAM : HWND of control
6695 * RETURN:
6696 * if first comes before second : negative
6697 * if first comes after second : positive
6698 * if first and second are equivalent : zero
6700 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6702 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6703 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6704 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6706 /* Forward the call to the client defined callback */
6707 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6710 /***
6711 * DESCRIPTION:
6712 * Sorts the listview items.
6714 * PARAMETER(S):
6715 * [I] infoPtr : valid pointer to the listview structure
6716 * [I] WPARAM : application-defined value
6717 * [I] LPARAM : pointer to comparision callback
6719 * RETURN:
6720 * SUCCESS : TRUE
6721 * FAILURE : FALSE
6723 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6725 UINT lStyle = infoPtr->dwStyle;
6726 HDPA hdpaSubItems;
6727 LISTVIEW_ITEM *lpItem;
6728 LPVOID selectionMarkItem;
6729 LVITEMW item;
6730 int i;
6732 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6734 if (lStyle & LVS_OWNERDATA) return FALSE;
6736 if (!infoPtr->hdpaItems) return FALSE;
6738 /* if there are 0 or 1 items, there is no need to sort */
6739 if (infoPtr->nItemCount < 2) return TRUE;
6741 if (infoPtr->nFocusedItem >= 0)
6743 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6744 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6745 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6747 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
6748 /* clear the lpItem->state for non-selected ones */
6749 /* remove the selection ranges */
6751 infoPtr->pfnCompare = pfnCompare;
6752 infoPtr->lParamSort = lParamSort;
6753 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6755 /* Adjust selections and indices so that they are the way they should
6756 * be after the sort (otherwise, the list items move around, but
6757 * whatever is at the item's previous original position will be
6758 * selected instead)
6760 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6761 for (i=0; i < infoPtr->nItemCount; i++)
6763 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6764 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6766 if (lpItem->state & LVIS_SELECTED)
6768 item.state = LVIS_SELECTED;
6769 item.stateMask = LVIS_SELECTED;
6770 LISTVIEW_SetItemState(infoPtr, i, &item);
6772 if (lpItem->state & LVIS_FOCUSED)
6774 infoPtr->nFocusedItem = i;
6775 lpItem->state &= ~LVIS_FOCUSED;
6778 if (selectionMarkItem != NULL)
6779 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6780 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6782 /* align the items */
6783 LISTVIEW_AlignTop(infoPtr);
6785 /* refresh the display */
6786 LISTVIEW_InvalidateList(infoPtr); /* FIXME: display should not change for [SMALL]ICON view */
6788 return TRUE;
6791 /***
6792 * DESCRIPTION:
6793 * Updates an items or rearranges the listview control.
6795 * PARAMETER(S):
6796 * [I] infoPtr : valid pointer to the listview structure
6797 * [I] INT : item index
6799 * RETURN:
6800 * SUCCESS : TRUE
6801 * FAILURE : FALSE
6803 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
6805 LONG lStyle = infoPtr->dwStyle;
6806 UINT uView = lStyle & LVS_TYPEMASK;
6808 TRACE("(nItem=%d)\n", nItem);
6810 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6812 /* rearrange with default alignment style */
6813 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
6814 LISTVIEW_Arrange(infoPtr, 0);
6815 else
6816 LISTVIEW_InvalidateItem(infoPtr, nItem);
6818 return TRUE;
6822 /***
6823 * DESCRIPTION:
6824 * Creates the listview control.
6826 * PARAMETER(S):
6827 * [I] hwnd : window handle
6828 * [I] lpcs : the create parameters
6830 * RETURN:
6831 * Success: 0
6832 * Failure: -1
6834 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
6836 LISTVIEW_INFO *infoPtr;
6837 UINT uView = lpcs->style & LVS_TYPEMASK;
6838 LOGFONTW logFont;
6840 TRACE("(lpcs=%p)\n", lpcs);
6842 /* initialize info pointer */
6843 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
6844 if (!infoPtr) return -1;
6846 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
6848 infoPtr->hwndSelf = hwnd;
6849 infoPtr->dwStyle = lpcs->style;
6850 /* determine the type of structures to use */
6851 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
6852 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
6854 /* initialize color information */
6855 infoPtr->clrBk = CLR_NONE;
6856 infoPtr->clrText = comctl32_color.clrWindowText;
6857 infoPtr->clrTextBk = CLR_DEFAULT;
6858 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
6860 /* set default values */
6861 infoPtr->nFocusedItem = -1;
6862 infoPtr->nSelectionMark = -1;
6863 infoPtr->nHotItem = -1;
6864 infoPtr->bRedraw = TRUE;
6865 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
6866 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
6867 infoPtr->nEditLabelItem = -1;
6869 /* get default font (icon title) */
6870 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
6871 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
6872 infoPtr->hFont = infoPtr->hDefaultFont;
6873 LISTVIEW_SaveTextMetrics(infoPtr);
6875 /* create header */
6876 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
6877 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
6878 0, 0, 0, 0, hwnd, (HMENU)0,
6879 lpcs->hInstance, NULL);
6881 /* set header unicode format */
6882 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
6884 /* set header font */
6885 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
6886 (LPARAM)TRUE);
6888 if (uView == LVS_ICON)
6890 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
6891 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
6893 else if (uView == LVS_REPORT)
6895 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
6897 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6899 else
6901 /* set HDS_HIDDEN flag to hide the header bar */
6902 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
6903 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
6907 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6908 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6910 else
6912 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6913 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6916 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
6917 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
6919 /* display unsupported listview window styles */
6920 LISTVIEW_UnsupportedStyles(lpcs->style);
6922 /* allocate memory for the data structure */
6923 infoPtr->hdpaItems = DPA_Create(10);
6924 infoPtr->hdpaPosX = DPA_Create(10);
6925 infoPtr->hdpaPosY = DPA_Create(10);
6927 /* allocate memory for the selection ranges */
6928 infoPtr->hdpaSelectionRanges = DPA_Create(10);
6930 /* initialize size of items */
6931 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6932 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6934 /* initialize the hover time to -1(indicating the default system hover time) */
6935 infoPtr->dwHoverTime = -1;
6937 return 0;
6940 /***
6941 * DESCRIPTION:
6942 * Erases the background of the listview control.
6944 * PARAMETER(S):
6945 * [I] infoPtr : valid pointer to the listview structure
6946 * [I] hdc : device context handle
6948 * RETURN:
6949 * SUCCESS : TRUE
6950 * FAILURE : FALSE
6952 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
6954 RECT rc;
6956 TRACE("(hdc=%x)\n", hdc);
6958 if (!GetClipBox(hdc, &rc)) return FALSE;
6960 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
6964 /***
6965 * DESCRIPTION:
6966 * Helper function for LISTVIEW_[HV]Scroll *only*.
6967 * Performs vertical/horizontal scrolling by a give amount.
6969 * PARAMETER(S):
6970 * [I] infoPtr : valid pointer to the listview structure
6971 * [I] dx : amount of horizontal scroll
6972 * [I] dy : amount of vertical scroll
6974 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6976 /* now we can scroll the list */
6977 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
6978 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
6979 /* if we have focus, adjust rect */
6980 OffsetRect(&infoPtr->rcFocus, dx, dy);
6981 UpdateWindow(infoPtr->hwndSelf);
6984 /***
6985 * DESCRIPTION:
6986 * Performs vertical scrolling.
6988 * PARAMETER(S):
6989 * [I] infoPtr : valid pointer to the listview structure
6990 * [I] nScrollCode : scroll code
6991 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
6992 * [I] hScrollWnd : scrollbar control window handle
6994 * RETURN:
6995 * Zero
6997 * NOTES:
6998 * SB_LINEUP/SB_LINEDOWN:
6999 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7000 * for LVS_REPORT is 1 line
7001 * for LVS_LIST cannot occur
7004 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7005 INT nScrollDiff, HWND hScrollWnd)
7007 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7008 INT nOldScrollPos, nNewScrollPos;
7009 SCROLLINFO scrollInfo;
7010 BOOL is_an_icon;
7012 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7014 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7016 scrollInfo.cbSize = sizeof(SCROLLINFO);
7017 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7019 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7021 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7023 nOldScrollPos = scrollInfo.nPos;
7024 switch (nScrollCode)
7026 case SB_INTERNAL:
7027 break;
7029 case SB_LINEUP:
7030 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7031 break;
7033 case SB_LINEDOWN:
7034 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7035 break;
7037 case SB_PAGEUP:
7038 nScrollDiff = -scrollInfo.nPage;
7039 break;
7041 case SB_PAGEDOWN:
7042 nScrollDiff = scrollInfo.nPage;
7043 break;
7045 case SB_THUMBPOSITION:
7046 case SB_THUMBTRACK:
7047 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7048 break;
7050 default:
7051 nScrollDiff = 0;
7054 /* quit right away if pos isn't changing */
7055 if (nScrollDiff == 0) return 0;
7057 /* calculate new position, and handle overflows */
7058 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7059 if (nScrollDiff > 0) {
7060 if (nNewScrollPos < nOldScrollPos ||
7061 nNewScrollPos > scrollInfo.nMax)
7062 nNewScrollPos = scrollInfo.nMax;
7063 } else {
7064 if (nNewScrollPos > nOldScrollPos ||
7065 nNewScrollPos < scrollInfo.nMin)
7066 nNewScrollPos = scrollInfo.nMin;
7069 /* set the new position, and reread in case it changed */
7070 scrollInfo.fMask = SIF_POS;
7071 scrollInfo.nPos = nNewScrollPos;
7072 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7074 /* carry on only if it really changed */
7075 if (nNewScrollPos == nOldScrollPos) return 0;
7077 /* now adjust to client coordinates */
7078 nScrollDiff = nOldScrollPos - nNewScrollPos;
7079 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7081 /* and scroll the window */
7082 scroll_list(infoPtr, 0, nScrollDiff);
7084 return 0;
7087 /***
7088 * DESCRIPTION:
7089 * Performs horizontal scrolling.
7091 * PARAMETER(S):
7092 * [I] infoPtr : valid pointer to the listview structure
7093 * [I] nScrollCode : scroll code
7094 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7095 * [I] hScrollWnd : scrollbar control window handle
7097 * RETURN:
7098 * Zero
7100 * NOTES:
7101 * SB_LINELEFT/SB_LINERIGHT:
7102 * for LVS_ICON, LVS_SMALLICON 1 pixel
7103 * for LVS_REPORT is 1 pixel
7104 * for LVS_LIST is 1 column --> which is a 1 because the
7105 * scroll is based on columns not pixels
7108 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7109 INT nScrollDiff, HWND hScrollWnd)
7111 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7112 INT nOldScrollPos, nNewScrollPos;
7113 SCROLLINFO scrollInfo;
7115 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7117 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7119 scrollInfo.cbSize = sizeof(SCROLLINFO);
7120 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7122 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7124 nOldScrollPos = scrollInfo.nPos;
7126 switch (nScrollCode)
7128 case SB_INTERNAL:
7129 break;
7131 case SB_LINELEFT:
7132 nScrollDiff = -1;
7133 break;
7135 case SB_LINERIGHT:
7136 nScrollDiff = 1;
7137 break;
7139 case SB_PAGELEFT:
7140 nScrollDiff = -scrollInfo.nPage;
7141 break;
7143 case SB_PAGERIGHT:
7144 nScrollDiff = scrollInfo.nPage;
7145 break;
7147 case SB_THUMBPOSITION:
7148 case SB_THUMBTRACK:
7149 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7150 break;
7152 default:
7153 nScrollDiff = 0;
7156 /* quit right away if pos isn't changing */
7157 if (nScrollDiff == 0) return 0;
7159 /* calculate new position, and handle overflows */
7160 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7161 if (nScrollDiff > 0) {
7162 if (nNewScrollPos < nOldScrollPos ||
7163 nNewScrollPos > scrollInfo.nMax)
7164 nNewScrollPos = scrollInfo.nMax;
7165 } else {
7166 if (nNewScrollPos > nOldScrollPos ||
7167 nNewScrollPos < scrollInfo.nMin)
7168 nNewScrollPos = scrollInfo.nMin;
7171 /* set the new position, and reread in case it changed */
7172 scrollInfo.fMask = SIF_POS;
7173 scrollInfo.nPos = nNewScrollPos;
7174 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7176 /* carry on only if it really changed */
7177 if (nNewScrollPos == nOldScrollPos) return 0;
7179 if(uView == LVS_REPORT)
7180 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7182 /* now adjust to client coordinates */
7183 nScrollDiff = nOldScrollPos - nNewScrollPos;
7184 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7186 /* and scroll the window */
7187 scroll_list(infoPtr, nScrollDiff, 0);
7189 return 0;
7192 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7194 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7195 INT gcWheelDelta = 0;
7196 UINT pulScrollLines = 3;
7197 SCROLLINFO scrollInfo;
7199 TRACE("(wheelDelta=%d)\n", wheelDelta);
7201 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7202 gcWheelDelta -= wheelDelta;
7204 scrollInfo.cbSize = sizeof(SCROLLINFO);
7205 scrollInfo.fMask = SIF_POS;
7207 switch(uView)
7209 case LVS_ICON:
7210 case LVS_SMALLICON:
7212 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7213 * should be fixed in the future.
7215 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7216 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7217 scrollInfo.nPos + (gcWheelDelta < 0) ?
7218 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7219 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7220 break;
7222 case LVS_REPORT:
7223 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7225 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7227 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7228 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7229 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7232 break;
7234 case LVS_LIST:
7235 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7236 break;
7238 return 0;
7241 /***
7242 * DESCRIPTION:
7243 * ???
7245 * PARAMETER(S):
7246 * [I] infoPtr : valid pointer to the listview structure
7247 * [I] INT : virtual key
7248 * [I] LONG : key data
7250 * RETURN:
7251 * Zero
7253 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7255 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7256 INT nItem = -1;
7257 NMLVKEYDOWN nmKeyDown;
7259 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7261 /* send LVN_KEYDOWN notification */
7262 nmKeyDown.wVKey = nVirtualKey;
7263 nmKeyDown.flags = 0;
7264 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7266 switch (nVirtualKey)
7268 case VK_RETURN:
7269 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7271 notify(infoPtr, NM_RETURN);
7272 notify(infoPtr, LVN_ITEMACTIVATE);
7274 break;
7276 case VK_HOME:
7277 if (infoPtr->nItemCount > 0)
7278 nItem = 0;
7279 break;
7281 case VK_END:
7282 if (infoPtr->nItemCount > 0)
7283 nItem = infoPtr->nItemCount - 1;
7284 break;
7286 case VK_LEFT:
7287 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7288 break;
7290 case VK_UP:
7291 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7292 break;
7294 case VK_RIGHT:
7295 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7296 break;
7298 case VK_DOWN:
7299 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7300 break;
7302 case VK_PRIOR:
7303 if (uView == LVS_REPORT)
7304 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7305 else
7306 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7307 * LISTVIEW_GetCountPerRow(infoPtr);
7308 if(nItem < 0) nItem = 0;
7309 break;
7311 case VK_NEXT:
7312 if (uView == LVS_REPORT)
7313 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7314 else
7315 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7316 * LISTVIEW_GetCountPerRow(infoPtr);
7317 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7318 break;
7321 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7322 LISTVIEW_KeySelection(infoPtr, nItem);
7324 return 0;
7327 /***
7328 * DESCRIPTION:
7329 * Kills the focus.
7331 * PARAMETER(S):
7332 * [I] infoPtr : valid pointer to the listview structure
7334 * RETURN:
7335 * Zero
7337 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7339 TRACE("()\n");
7341 /* if we did not have the focus, there's nothing to do */
7342 if (!infoPtr->bFocus) return 0;
7344 /* send NM_KILLFOCUS notification */
7345 notify(infoPtr, NM_KILLFOCUS);
7347 /* if we have a focus rectagle, get rid of it */
7348 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7350 /* set window focus flag */
7351 infoPtr->bFocus = FALSE;
7353 /* invalidate the selected items before reseting focus flag */
7354 LISTVIEW_InvalidateSelectedItems(infoPtr);
7356 return 0;
7359 /***
7360 * DESCRIPTION:
7361 * Processes double click messages (left mouse button).
7363 * PARAMETER(S):
7364 * [I] infoPtr : valid pointer to the listview structure
7365 * [I] wKey : key flag
7366 * [I] pts : mouse coordinate
7368 * RETURN:
7369 * Zero
7371 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7373 LVHITTESTINFO htInfo;
7375 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7377 /* send NM_RELEASEDCAPTURE notification */
7378 notify(infoPtr, NM_RELEASEDCAPTURE);
7380 htInfo.pt.x = pts.x;
7381 htInfo.pt.y = pts.y;
7383 /* send NM_DBLCLK notification */
7384 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7385 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7387 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7388 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7390 return 0;
7393 /***
7394 * DESCRIPTION:
7395 * Processes mouse down messages (left mouse button).
7397 * PARAMETER(S):
7398 * [I] infoPtr : valid pointer to the listview structure
7399 * [I] wKey : key flag
7400 * [I] pts : mouse coordinate
7402 * RETURN:
7403 * Zero
7405 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7407 LVHITTESTINFO lvHitTestInfo;
7408 LONG lStyle = infoPtr->dwStyle;
7409 static BOOL bGroupSelect = TRUE;
7410 POINT pt = { pts.x, pts.y };
7411 INT nItem;
7413 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7415 /* send NM_RELEASEDCAPTURE notification */
7416 notify(infoPtr, NM_RELEASEDCAPTURE);
7418 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7420 /* set left button down flag */
7421 infoPtr->bLButtonDown = TRUE;
7423 lvHitTestInfo.pt.x = pts.x;
7424 lvHitTestInfo.pt.y = pts.y;
7426 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7427 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7428 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7430 if (lStyle & LVS_SINGLESEL)
7432 if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7433 && infoPtr->nEditLabelItem == -1)
7434 infoPtr->nEditLabelItem = nItem;
7435 else
7436 LISTVIEW_SetSelection(infoPtr, nItem);
7438 else
7440 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7442 if (bGroupSelect)
7443 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7444 else
7446 LVITEMW item;
7448 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7449 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7451 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7452 infoPtr->nSelectionMark = nItem;
7455 else if (wKey & MK_CONTROL)
7457 LVITEMW item;
7459 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7461 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7462 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7463 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7464 infoPtr->nSelectionMark = nItem;
7466 else if (wKey & MK_SHIFT)
7468 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7470 else
7472 BOOL was_selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
7474 /* set selection (clears other pre-existing selections) */
7475 LISTVIEW_SetSelection(infoPtr, nItem);
7477 if (was_selected && infoPtr->nEditLabelItem == -1)
7478 infoPtr->nEditLabelItem = nItem;
7482 else
7484 /* remove all selections */
7485 LISTVIEW_RemoveAllSelections(infoPtr);
7488 return 0;
7491 /***
7492 * DESCRIPTION:
7493 * Processes mouse up messages (left mouse button).
7495 * PARAMETER(S):
7496 * [I] infoPtr : valid pointer to the listview structure
7497 * [I] wKey : key flag
7498 * [I] pts : mouse coordinate
7500 * RETURN:
7501 * Zero
7503 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7505 LVHITTESTINFO lvHitTestInfo;
7507 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7509 if (!infoPtr->bLButtonDown) return 0;
7511 lvHitTestInfo.pt.x = pts.x;
7512 lvHitTestInfo.pt.y = pts.y;
7514 /* send NM_CLICK notification */
7515 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7516 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7518 /* set left button flag */
7519 infoPtr->bLButtonDown = FALSE;
7521 if(infoPtr->nEditLabelItem != -1)
7523 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem &&
7524 (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7525 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7526 infoPtr->nEditLabelItem = -1;
7529 return 0;
7532 /***
7533 * DESCRIPTION:
7534 * Destroys the listview control (called after WM_DESTROY).
7536 * PARAMETER(S):
7537 * [I] infoPtr : valid pointer to the listview structure
7539 * RETURN:
7540 * Zero
7542 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7544 LONG lStyle = infoPtr->dwStyle;
7546 TRACE("()\n");
7548 /* delete all items */
7549 LISTVIEW_DeleteAllItems(infoPtr);
7551 /* destroy data structure */
7552 DPA_Destroy(infoPtr->hdpaItems);
7553 DPA_Destroy(infoPtr->hdpaSelectionRanges);
7555 /* destroy image lists */
7556 if (!(lStyle & LVS_SHAREIMAGELISTS))
7558 /* FIXME: If the caller does a ImageList_Destroy and then we
7559 * do this code the area will be freed twice. Currently
7560 * this generates an "err:heap:HEAP_ValidateInUseArena
7561 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
7562 * has PREV_FREE flag" sometimes.
7564 * We will leak the memory till we figure out how to fix
7566 if (infoPtr->himlNormal)
7567 ImageList_Destroy(infoPtr->himlNormal);
7568 if (infoPtr->himlSmall)
7569 ImageList_Destroy(infoPtr->himlSmall);
7570 if (infoPtr->himlState)
7571 ImageList_Destroy(infoPtr->himlState);
7574 /* destroy font, bkgnd brush */
7575 infoPtr->hFont = 0;
7576 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7577 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7579 /* free listview info pointer*/
7580 COMCTL32_Free(infoPtr);
7582 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7583 return 0;
7586 /***
7587 * DESCRIPTION:
7588 * Handles notifications from children.
7590 * PARAMETER(S):
7591 * [I] infoPtr : valid pointer to the listview structure
7592 * [I] INT : control identifier
7593 * [I] LPNMHDR : notification information
7595 * RETURN:
7596 * Zero
7598 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7600 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7602 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7604 /* handle notification from header control */
7605 if (lpnmh->code == HDN_ENDTRACKW)
7607 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7608 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7610 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7612 /* Handle sorting by Header Column */
7613 NMLISTVIEW nmlv;
7615 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7616 nmlv.iItem = -1;
7617 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
7618 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7620 else if(lpnmh->code == NM_RELEASEDCAPTURE)
7622 /* Idealy this should be done in HDN_ENDTRACKA
7623 * but since SetItemBounds in Header.c is called after
7624 * the notification is sent, it is neccessary to handle the
7625 * update of the scroll bar here (Header.c works fine as it is,
7626 * no need to disturb it)
7628 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7629 LISTVIEW_UpdateScroll(infoPtr);
7630 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7635 return 0;
7638 /***
7639 * DESCRIPTION:
7640 * Determines the type of structure to use.
7642 * PARAMETER(S):
7643 * [I] infoPtr : valid pointer to the listview structureof the sender
7644 * [I] HWND : listview window handle
7645 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
7647 * RETURN:
7648 * Zero
7650 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7652 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7654 if (nCommand == NF_REQUERY)
7655 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7656 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7657 return 0;
7660 /***
7661 * DESCRIPTION:
7662 * Paints/Repaints the listview control.
7664 * PARAMETER(S):
7665 * [I] infoPtr : valid pointer to the listview structure
7666 * [I] HDC : device context handle
7668 * RETURN:
7669 * Zero
7671 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7673 TRACE("(hdc=%x)\n", hdc);
7675 if (hdc)
7676 LISTVIEW_Refresh(infoPtr, hdc);
7677 else
7679 PAINTSTRUCT ps;
7681 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7682 if (!hdc) return 1;
7683 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7684 LISTVIEW_Refresh(infoPtr, hdc);
7685 EndPaint(infoPtr->hwndSelf, &ps);
7688 return 0;
7691 /***
7692 * DESCRIPTION:
7693 * Processes double click messages (right mouse button).
7695 * PARAMETER(S):
7696 * [I] infoPtr : valid pointer to the listview structure
7697 * [I] wKey : key flag
7698 * [I] pts : mouse coordinate
7700 * RETURN:
7701 * Zero
7703 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7705 LVHITTESTINFO lvHitTestInfo;
7707 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7709 /* send NM_RELEASEDCAPTURE notification */
7710 notify(infoPtr, NM_RELEASEDCAPTURE);
7712 /* send NM_RDBLCLK notification */
7713 lvHitTestInfo.pt.x = pts.x;
7714 lvHitTestInfo.pt.y = pts.y;
7715 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7716 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
7718 return 0;
7721 /***
7722 * DESCRIPTION:
7723 * Processes mouse down messages (right mouse button).
7725 * PARAMETER(S):
7726 * [I] infoPtr : valid pointer to the listview structure
7727 * [I] wKey : key flag
7728 * [I] pts : mouse coordinate
7730 * RETURN:
7731 * Zero
7733 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7735 LVHITTESTINFO lvHitTestInfo;
7736 INT nItem;
7738 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7740 /* send NM_RELEASEDCAPTURE notification */
7741 notify(infoPtr, NM_RELEASEDCAPTURE);
7743 /* make sure the listview control window has the focus */
7744 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7746 /* set right button down flag */
7747 infoPtr->bRButtonDown = TRUE;
7749 /* determine the index of the selected item */
7750 lvHitTestInfo.pt.x = pts.x;
7751 lvHitTestInfo.pt.y = pts.y;
7752 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7754 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7756 LISTVIEW_SetItemFocus(infoPtr, nItem);
7757 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7758 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7759 LISTVIEW_SetSelection(infoPtr, nItem);
7761 else
7763 LISTVIEW_RemoveAllSelections(infoPtr);
7766 return 0;
7769 /***
7770 * DESCRIPTION:
7771 * Processes mouse up messages (right mouse button).
7773 * PARAMETER(S):
7774 * [I] infoPtr : valid pointer to the listview structure
7775 * [I] wKey : key flag
7776 * [I] pts : mouse coordinate
7778 * RETURN:
7779 * Zero
7781 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7783 LVHITTESTINFO lvHitTestInfo;
7784 POINT pt;
7786 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7788 if (!infoPtr->bRButtonDown) return 0;
7790 /* set button flag */
7791 infoPtr->bRButtonDown = FALSE;
7793 /* Send NM_RClICK notification */
7794 lvHitTestInfo.pt.x = pts.x;
7795 lvHitTestInfo.pt.y = pts.y;
7796 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7797 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
7799 /* Change to screen coordinate for WM_CONTEXTMENU */
7800 pt = lvHitTestInfo.pt;
7801 ClientToScreen(infoPtr->hwndSelf, &pt);
7803 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
7804 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
7805 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
7807 return 0;
7811 /***
7812 * DESCRIPTION:
7813 * Sets the cursor.
7815 * PARAMETER(S):
7816 * [I] infoPtr : valid pointer to the listview structure
7817 * [I] hwnd : window handle of window containing the cursor
7818 * [I] nHittest : hit-test code
7819 * [I] wMouseMsg : ideintifier of the mouse message
7821 * RETURN:
7822 * TRUE if cursor is set
7823 * FALSE otherwise
7825 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
7827 LVHITTESTINFO lvHitTestInfo;
7829 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
7831 if(!infoPtr->hHotCursor) return FALSE;
7833 GetCursorPos(&lvHitTestInfo.pt);
7834 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
7836 SetCursor(infoPtr->hHotCursor);
7838 return TRUE;
7841 /***
7842 * DESCRIPTION:
7843 * Sets the focus.
7845 * PARAMETER(S):
7846 * [I] infoPtr : valid pointer to the listview structure
7847 * [I] infoPtr : handle of previously focused window
7849 * RETURN:
7850 * Zero
7852 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
7854 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
7856 /* if we have the focus already, there's nothing to do */
7857 if (infoPtr->bFocus) return 0;
7859 /* send NM_SETFOCUS notification */
7860 notify(infoPtr, NM_SETFOCUS);
7862 /* set window focus flag */
7863 infoPtr->bFocus = TRUE;
7865 /* put the focus rect back on */
7866 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
7868 /* redraw all visible selected items */
7869 LISTVIEW_InvalidateSelectedItems(infoPtr);
7871 return 0;
7874 /***
7875 * DESCRIPTION:
7876 * Sets the font.
7878 * PARAMETER(S):
7879 * [I] infoPtr : valid pointer to the listview structure
7880 * [I] HFONT : font handle
7881 * [I] WORD : redraw flag
7883 * RETURN:
7884 * Zero
7886 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
7888 HFONT oldFont = infoPtr->hFont;
7890 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
7892 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
7893 if (infoPtr->hFont == oldFont) return 0;
7895 LISTVIEW_SaveTextMetrics(infoPtr);
7897 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
7898 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
7900 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
7902 return 0;
7905 /***
7906 * DESCRIPTION:
7907 * Message handling for WM_SETREDRAW.
7908 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
7910 * PARAMETER(S):
7911 * [I] infoPtr : valid pointer to the listview structure
7912 * [I] bRedraw: state of redraw flag
7914 * RETURN:
7915 * DefWinProc return value
7917 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
7919 infoPtr->bRedraw = bRedraw;
7920 if(bRedraw)
7921 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
7922 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
7923 return 0;
7926 /***
7927 * DESCRIPTION:
7928 * Resizes the listview control. This function processes WM_SIZE
7929 * messages. At this time, the width and height are not used.
7931 * PARAMETER(S):
7932 * [I] infoPtr : valid pointer to the listview structure
7933 * [I] WORD : new width
7934 * [I] WORD : new height
7936 * RETURN:
7937 * Zero
7939 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
7941 LONG lStyle = infoPtr->dwStyle;
7942 UINT uView = lStyle & LVS_TYPEMASK;
7944 TRACE("(width=%d, height=%d)\n", Width, Height);
7946 if (LISTVIEW_UpdateSize(infoPtr))
7948 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
7950 if (lStyle & LVS_ALIGNLEFT)
7951 LISTVIEW_AlignLeft(infoPtr);
7952 else
7953 LISTVIEW_AlignTop(infoPtr);
7956 LISTVIEW_UpdateScroll(infoPtr);
7958 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7961 return 0;
7964 /***
7965 * DESCRIPTION:
7966 * Sets the size information.
7968 * PARAMETER(S):
7969 * [I] infoPtr : valid pointer to the listview structure
7971 * RETURN:
7972 * Zero if no size change
7973 * 1 of size changed
7975 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
7977 LONG lStyle = infoPtr->dwStyle;
7978 UINT uView = lStyle & LVS_TYPEMASK;
7979 RECT rcList;
7980 RECT rcOld;
7982 GetClientRect(infoPtr->hwndSelf, &rcList);
7983 CopyRect(&rcOld,&(infoPtr->rcList));
7984 infoPtr->rcList.left = 0;
7985 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
7986 infoPtr->rcList.top = 0;
7987 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
7989 if (uView == LVS_LIST)
7991 /* Apparently the "LIST" style is supposed to have the same
7992 * number of items in a column even if there is no scroll bar.
7993 * Since if a scroll bar already exists then the bottom is already
7994 * reduced, only reduce if the scroll bar does not currently exist.
7995 * The "2" is there to mimic the native control. I think it may be
7996 * related to either padding or edges. (GLA 7/2002)
7998 if (!(lStyle & WS_HSCROLL))
8000 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8001 if (infoPtr->rcList.bottom > nHScrollHeight)
8002 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8004 else
8006 if (infoPtr->rcList.bottom > 2)
8007 infoPtr->rcList.bottom -= 2;
8010 else if (uView == LVS_REPORT)
8012 HDLAYOUT hl;
8013 WINDOWPOS wp;
8015 hl.prc = &rcList;
8016 hl.pwpos = &wp;
8017 Header_Layout(infoPtr->hwndHeader, &hl);
8019 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8021 if (!(LVS_NOCOLUMNHEADER & lStyle))
8022 infoPtr->rcList.top = max(wp.cy, 0);
8024 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8027 /***
8028 * DESCRIPTION:
8029 * Processes WM_STYLECHANGED messages.
8031 * PARAMETER(S):
8032 * [I] infoPtr : valid pointer to the listview structure
8033 * [I] WPARAM : window style type (normal or extended)
8034 * [I] LPSTYLESTRUCT : window style information
8036 * RETURN:
8037 * Zero
8039 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8040 LPSTYLESTRUCT lpss)
8042 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8043 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8044 RECT rcList = infoPtr->rcList;
8046 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8047 wStyleType, lpss->styleOld, lpss->styleNew);
8049 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8051 if (wStyleType == GWL_STYLE)
8053 infoPtr->dwStyle = lpss->styleNew;
8055 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8056 ((lpss->styleNew & WS_HSCROLL) == 0))
8057 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8059 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8060 ((lpss->styleNew & WS_VSCROLL) == 0))
8061 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8063 /* If switching modes, then start with no scroll bars and then
8064 * decide.
8066 if (uNewView != uOldView)
8068 if (uOldView == LVS_REPORT)
8069 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8071 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8072 SetRectEmpty(&infoPtr->rcFocus);
8075 if (uNewView == LVS_ICON)
8077 INT oldcx, oldcy;
8079 /* First readjust the iconSize and if necessary the iconSpacing */
8080 oldcx = infoPtr->iconSize.cx;
8081 oldcy = infoPtr->iconSize.cy;
8082 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8083 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8084 if (infoPtr->himlNormal != NULL)
8086 INT cx, cy;
8087 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
8088 infoPtr->iconSize.cx = cx;
8089 infoPtr->iconSize.cy = cy;
8091 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
8093 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8094 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8095 LISTVIEW_SetIconSpacing(infoPtr,0);
8098 /* Now update the full item width and height */
8099 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8100 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8101 if (lpss->styleNew & LVS_ALIGNLEFT)
8102 LISTVIEW_AlignLeft(infoPtr);
8103 else
8104 LISTVIEW_AlignTop(infoPtr);
8106 else if (uNewView == LVS_REPORT)
8108 HDLAYOUT hl;
8109 WINDOWPOS wp;
8111 hl.prc = &rcList;
8112 hl.pwpos = &wp;
8113 Header_Layout(infoPtr->hwndHeader, &hl);
8114 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8115 wp.flags);
8116 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8117 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
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 if (uNewView == LVS_LIST)
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);
8131 else
8133 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8134 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8135 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8136 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8137 if (lpss->styleNew & LVS_ALIGNLEFT)
8138 LISTVIEW_AlignLeft(infoPtr);
8139 else
8140 LISTVIEW_AlignTop(infoPtr);
8143 /* update the size of the client area */
8144 LISTVIEW_UpdateSize(infoPtr);
8146 /* add scrollbars if needed */
8147 LISTVIEW_UpdateScroll(infoPtr);
8149 /* invalidate client area + erase background */
8150 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8152 /* print the list of unsupported window styles */
8153 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8156 /* If they change the view and we have an active edit control
8157 we will need to kill the control since the redraw will
8158 misplace the edit control.
8160 if (infoPtr->bEditing &&
8161 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8162 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8164 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8167 return 0;
8170 /***
8171 * DESCRIPTION:
8172 * Window procedure of the listview control.
8175 static LRESULT WINAPI
8176 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8178 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8180 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8182 if (!infoPtr && (uMsg != WM_CREATE))
8183 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8185 if (infoPtr)
8187 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8190 switch (uMsg)
8192 case LVM_APPROXIMATEVIEWRECT:
8193 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8194 LOWORD(lParam), HIWORD(lParam));
8195 case LVM_ARRANGE:
8196 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8198 /* case LVN_CANCELEDITLABEL */
8200 /* case LVM_CREATEDRAGIMAGE: */
8202 case LVM_DELETEALLITEMS:
8203 return LISTVIEW_DeleteAllItems(infoPtr);
8205 case LVM_DELETECOLUMN:
8206 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8208 case LVM_DELETEITEM:
8209 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8211 case LVM_EDITLABELW:
8212 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8214 case LVM_EDITLABELA:
8215 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8217 /* case LVN_ENABLEGROUPVIEW: */
8219 case LVM_ENSUREVISIBLE:
8220 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8222 case LVM_FINDITEMW:
8223 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8225 case LVM_FINDITEMA:
8226 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8228 case LVM_GETBKCOLOR:
8229 return infoPtr->clrBk;
8231 /* case LVM_GETBKIMAGE: */
8233 case LVM_GETCALLBACKMASK:
8234 return infoPtr->uCallbackMask;
8236 case LVM_GETCOLUMNA:
8237 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8239 case LVM_GETCOLUMNW:
8240 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8242 case LVM_GETCOLUMNORDERARRAY:
8243 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8245 case LVM_GETCOLUMNWIDTH:
8246 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8248 case LVM_GETCOUNTPERPAGE:
8249 return LISTVIEW_GetCountPerPage(infoPtr);
8251 case LVM_GETEDITCONTROL:
8252 return (LRESULT)infoPtr->hwndEdit;
8254 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8255 return infoPtr->dwLvExStyle;
8257 case LVM_GETHEADER:
8258 return (LRESULT)infoPtr->hwndHeader;
8260 case LVM_GETHOTCURSOR:
8261 return (LRESULT)infoPtr->hHotCursor;
8263 case LVM_GETHOTITEM:
8264 return infoPtr->nHotItem;
8266 case LVM_GETHOVERTIME:
8267 return infoPtr->dwHoverTime;
8269 case LVM_GETIMAGELIST:
8270 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8272 /* case LVN_GETINSERTMARK: */
8274 /* case LVN_GETINSERTMARKCOLOR: */
8276 /* case LVN_GETINSERTMARKRECT: */
8278 case LVM_GETISEARCHSTRINGA:
8279 case LVM_GETISEARCHSTRINGW:
8280 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8281 return FALSE;
8283 case LVM_GETITEMA:
8284 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8286 case LVM_GETITEMW:
8287 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8289 case LVM_GETITEMCOUNT:
8290 return infoPtr->nItemCount;
8292 case LVM_GETITEMPOSITION:
8293 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8295 case LVM_GETITEMRECT:
8296 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8298 case LVM_GETITEMSPACING:
8299 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8301 case LVM_GETITEMSTATE:
8302 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8304 case LVM_GETITEMTEXTA:
8305 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8307 case LVM_GETITEMTEXTW:
8308 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8310 case LVM_GETNEXTITEM:
8311 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8313 case LVM_GETNUMBEROFWORKAREAS:
8314 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8315 return 1;
8317 case LVM_GETORIGIN:
8318 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8320 /* case LVN_GETOUTLINECOLOR: */
8322 /* case LVM_GETSELECTEDCOLUMN: */
8324 case LVM_GETSELECTEDCOUNT:
8325 return LISTVIEW_GetSelectedCount(infoPtr);
8327 case LVM_GETSELECTIONMARK:
8328 return infoPtr->nSelectionMark;
8330 case LVM_GETSTRINGWIDTHA:
8331 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8333 case LVM_GETSTRINGWIDTHW:
8334 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8336 case LVM_GETSUBITEMRECT:
8337 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8339 case LVM_GETTEXTBKCOLOR:
8340 return infoPtr->clrTextBk;
8342 case LVM_GETTEXTCOLOR:
8343 return infoPtr->clrText;
8345 /* case LVN_GETTILEINFO: */
8347 /* case LVN_GETTILEVIEWINFO: */
8349 case LVM_GETTOOLTIPS:
8350 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8351 return FALSE;
8353 case LVM_GETTOPINDEX:
8354 return LISTVIEW_GetTopIndex(infoPtr);
8356 /*case LVM_GETUNICODEFORMAT:
8357 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8358 return FALSE;*/
8360 case LVM_GETVIEWRECT:
8361 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8363 case LVM_GETWORKAREAS:
8364 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8365 return FALSE;
8367 /* case LVN_HASGROUP: */
8369 case LVM_HITTEST:
8370 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8372 case LVM_INSERTCOLUMNA:
8373 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8375 case LVM_INSERTCOLUMNW:
8376 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8378 /* case LVN_INSERTGROUP: */
8380 /* case LVN_INSERTGROUPSORTED: */
8382 case LVM_INSERTITEMA:
8383 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8385 case LVM_INSERTITEMW:
8386 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8388 /* case LVN_INSERTMARKHITTEST: */
8390 /* case LVN_ISGROUPVIEWENABLED: */
8392 /* case LVN_MAPIDTOINDEX: */
8394 /* case LVN_INEDXTOID: */
8396 /* case LVN_MOVEGROUP: */
8398 /* case LVN_MOVEITEMTOGROUP: */
8400 case LVM_REDRAWITEMS:
8401 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8403 /* case LVN_REMOVEALLGROUPS: */
8405 /* case LVN_REMOVEGROUP: */
8407 case LVM_SCROLL:
8408 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8410 case LVM_SETBKCOLOR:
8411 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8413 /* case LVM_SETBKIMAGE: */
8415 case LVM_SETCALLBACKMASK:
8416 infoPtr->uCallbackMask = (UINT)wParam;
8417 return TRUE;
8419 case LVM_SETCOLUMNA:
8420 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8422 case LVM_SETCOLUMNW:
8423 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8425 case LVM_SETCOLUMNORDERARRAY:
8426 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8428 case LVM_SETCOLUMNWIDTH:
8429 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8431 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8432 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8434 /* case LVN_SETGROUPINFO: */
8436 /* case LVN_SETGROUPMETRICS: */
8438 case LVM_SETHOTCURSOR:
8439 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8441 case LVM_SETHOTITEM:
8442 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8444 case LVM_SETHOVERTIME:
8445 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8447 case LVM_SETICONSPACING:
8448 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8450 case LVM_SETIMAGELIST:
8451 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8453 /* case LVN_SETINFOTIP: */
8455 /* case LVN_SETINSERTMARK: */
8457 /* case LVN_SETINSERTMARKCOLOR: */
8459 case LVM_SETITEMA:
8460 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8462 case LVM_SETITEMW:
8463 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8465 case LVM_SETITEMCOUNT:
8466 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8468 case LVM_SETITEMPOSITION:
8470 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8471 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8474 case LVM_SETITEMPOSITION32:
8475 if (lParam == 0) return FALSE;
8476 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8478 case LVM_SETITEMSTATE:
8479 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8481 case LVM_SETITEMTEXTA:
8482 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8484 case LVM_SETITEMTEXTW:
8485 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8487 /* case LVN_SETOUTLINECOLOR: */
8489 /* case LVN_SETSELECTEDCOLUMN: */
8491 case LVM_SETSELECTIONMARK:
8492 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8494 case LVM_SETTEXTBKCOLOR:
8495 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8497 case LVM_SETTEXTCOLOR:
8498 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8500 /* case LVN_SETTILEINFO: */
8502 /* case LVN_SETTILEVIEWINFO: */
8504 /* case LVN_SETTILEWIDTH: */
8506 /* case LVM_SETTOOLTIPS: */
8508 /* case LVM_SETUNICODEFORMAT: */
8510 /* case LVN_SETVIEW: */
8512 /* case LVM_SETWORKAREAS: */
8514 /* case LVN_SORTGROUPS: */
8516 case LVM_SORTITEMS:
8517 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8519 case LVM_SUBITEMHITTEST:
8520 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8522 case LVM_UPDATE:
8523 return LISTVIEW_Update(infoPtr, (INT)wParam);
8525 case WM_CHAR:
8526 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8528 case WM_COMMAND:
8529 return LISTVIEW_Command(infoPtr, wParam, lParam);
8531 case WM_CREATE:
8532 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8534 case WM_ERASEBKGND:
8535 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8537 case WM_GETDLGCODE:
8538 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8540 case WM_GETFONT:
8541 return infoPtr->hFont;
8543 case WM_HSCROLL:
8544 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8546 case WM_KEYDOWN:
8547 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8549 case WM_KILLFOCUS:
8550 return LISTVIEW_KillFocus(infoPtr);
8552 case WM_LBUTTONDBLCLK:
8553 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8555 case WM_LBUTTONDOWN:
8556 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8558 case WM_LBUTTONUP:
8559 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8561 case WM_MOUSEMOVE:
8562 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8564 case WM_MOUSEHOVER:
8565 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8567 case WM_NCDESTROY:
8568 return LISTVIEW_NCDestroy(infoPtr);
8570 case WM_NOTIFY:
8571 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8573 case WM_NOTIFYFORMAT:
8574 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8576 case WM_PAINT:
8577 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8579 case WM_RBUTTONDBLCLK:
8580 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8582 case WM_RBUTTONDOWN:
8583 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8585 case WM_RBUTTONUP:
8586 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8588 case WM_SETCURSOR:
8589 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8590 return TRUE;
8591 goto fwd_msg;
8593 case WM_SETFOCUS:
8594 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8596 case WM_SETFONT:
8597 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8599 case WM_SETREDRAW:
8600 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8602 case WM_SIZE:
8603 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8605 case WM_STYLECHANGED:
8606 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8608 case WM_SYSCOLORCHANGE:
8609 COMCTL32_RefreshSysColors();
8610 return 0;
8612 /* case WM_TIMER: */
8614 case WM_VSCROLL:
8615 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8617 case WM_MOUSEWHEEL:
8618 if (wParam & (MK_SHIFT | MK_CONTROL))
8619 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8620 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8622 case WM_WINDOWPOSCHANGED:
8623 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8624 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8625 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8626 LISTVIEW_UpdateSize(infoPtr);
8627 LISTVIEW_UpdateScroll(infoPtr);
8629 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8631 /* case WM_WININICHANGE: */
8633 default:
8634 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8635 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8637 fwd_msg:
8638 /* call default window procedure */
8639 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8642 return 0;
8645 /***
8646 * DESCRIPTION:
8647 * Registers the window class.
8649 * PARAMETER(S):
8650 * None
8652 * RETURN:
8653 * None
8655 void LISTVIEW_Register(void)
8657 WNDCLASSW wndClass;
8659 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8660 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8661 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8662 wndClass.cbClsExtra = 0;
8663 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8664 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8665 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8666 wndClass.lpszClassName = WC_LISTVIEWW;
8667 RegisterClassW(&wndClass);
8670 /***
8671 * DESCRIPTION:
8672 * Unregisters the window class.
8674 * PARAMETER(S):
8675 * None
8677 * RETURN:
8678 * None
8680 void LISTVIEW_Unregister(void)
8682 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8685 /***
8686 * DESCRIPTION:
8687 * Handle any WM_COMMAND messages
8689 * PARAMETER(S):
8691 * RETURN:
8693 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8695 switch (HIWORD(wParam))
8697 case EN_UPDATE:
8700 * Adjust the edit window size
8702 WCHAR buffer[1024];
8703 HDC hdc = GetDC(infoPtr->hwndEdit);
8704 HFONT hFont, hOldFont = 0;
8705 RECT rect;
8706 SIZE sz;
8707 int len;
8709 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8710 GetWindowRect(infoPtr->hwndEdit, &rect);
8712 /* Select font to get the right dimension of the string */
8713 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8714 if(hFont != 0)
8716 hOldFont = SelectObject(hdc, hFont);
8719 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8721 TEXTMETRICW textMetric;
8723 /* Add Extra spacing for the next character */
8724 GetTextMetricsW(hdc, &textMetric);
8725 sz.cx += (textMetric.tmMaxCharWidth * 2);
8727 SetWindowPos (
8728 infoPtr->hwndEdit,
8729 HWND_TOP,
8732 sz.cx,
8733 rect.bottom - rect.top,
8734 SWP_DRAWFRAME|SWP_NOMOVE);
8736 if(hFont != 0)
8737 SelectObject(hdc, hOldFont);
8739 ReleaseDC(infoPtr->hwndSelf, hdc);
8741 break;
8744 default:
8745 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8748 return 0;
8752 /***
8753 * DESCRIPTION:
8754 * Subclassed edit control windproc function
8756 * PARAMETER(S):
8758 * RETURN:
8760 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
8761 WPARAM wParam, LPARAM lParam, BOOL isW)
8763 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8764 static BOOL bIgnoreKillFocus = FALSE;
8765 BOOL cancel = FALSE;
8767 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8768 hwnd, uMsg, wParam, lParam, isW);
8770 switch (uMsg)
8772 case WM_GETDLGCODE:
8773 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8775 case WM_KILLFOCUS:
8776 if(bIgnoreKillFocus) return TRUE;
8777 break;
8779 case WM_DESTROY:
8781 WNDPROC editProc = infoPtr->EditWndProc;
8782 infoPtr->EditWndProc = 0;
8783 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
8784 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
8787 case WM_KEYDOWN:
8788 if (VK_ESCAPE == (INT)wParam)
8790 cancel = TRUE;
8791 break;
8793 else if (VK_RETURN == (INT)wParam)
8794 break;
8796 default:
8797 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
8800 if (infoPtr->bEditing)
8802 LPWSTR buffer = NULL;
8804 if (!cancel)
8806 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
8808 if (len)
8810 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
8812 if (isW) GetWindowTextW(hwnd, buffer, len+1);
8813 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
8817 /* Processing LVN_ENDLABELEDIT message could kill the focus */
8818 /* eg. Using a messagebox */
8819 bIgnoreKillFocus = TRUE;
8820 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
8822 if (buffer) COMCTL32_Free(buffer);
8824 bIgnoreKillFocus = FALSE;
8827 SendMessageW(hwnd, WM_CLOSE, 0, 0);
8828 return TRUE;
8831 /***
8832 * DESCRIPTION:
8833 * Subclassed edit control windproc function
8835 * PARAMETER(S):
8837 * RETURN:
8839 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8841 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
8844 /***
8845 * DESCRIPTION:
8846 * Subclassed edit control windproc function
8848 * PARAMETER(S):
8850 * RETURN:
8852 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8854 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
8857 /***
8858 * DESCRIPTION:
8859 * Creates a subclassed edit cotrol
8861 * PARAMETER(S):
8863 * RETURN:
8865 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
8866 INT x, INT y, INT width, INT height, BOOL isW)
8868 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
8869 HWND hedit;
8870 SIZE sz;
8871 HDC hdc;
8872 HDC hOldFont=0;
8873 TEXTMETRICW textMetric;
8874 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
8876 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
8878 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
8879 hdc = GetDC(infoPtr->hwndSelf);
8881 /* Select the font to get appropriate metric dimensions */
8882 if(infoPtr->hFont != 0)
8883 hOldFont = SelectObject(hdc, infoPtr->hFont);
8885 /*Get String Lenght in pixels */
8886 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
8888 /*Add Extra spacing for the next character */
8889 GetTextMetricsW(hdc, &textMetric);
8890 sz.cx += (textMetric.tmMaxCharWidth * 2);
8892 if(infoPtr->hFont != 0)
8893 SelectObject(hdc, hOldFont);
8895 ReleaseDC(infoPtr->hwndSelf, hdc);
8896 if (isW)
8897 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8898 else
8899 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8901 if (!hedit) return 0;
8903 infoPtr->EditWndProc = (WNDPROC)
8904 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
8905 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
8907 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
8909 return hedit;