Simplify the report mode, and item drawing considerably.
[wine.git] / dlls / comctl32 / listview.c
blobf573b8d83c26a545283e3b295bf70558dc7f36b1
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_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
55 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
56 * does *not* invoke DefWindowProc
57 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
58 * processing for "USEDOUBLECLICKTIME".
62 #include "config.h"
63 #include "wine/port.h"
65 #include <ctype.h>
66 #include <string.h>
67 #include <stdlib.h>
68 #include <stdio.h>
70 #include "winbase.h"
71 #include "winnt.h"
72 #include "heap.h"
73 #include "commctrl.h"
74 #include "comctl32.h"
75 #include "wine/debug.h"
77 WINE_DEFAULT_DEBUG_CHANNEL(listview);
79 typedef struct tagCOLUMNCACHE
81 RECT rc;
82 UINT align;
83 } COLUMNCACHE, *LPCOLUMNCACHE;
85 typedef struct tagITEMHDR
87 LPWSTR pszText;
88 INT iImage;
89 } ITEMHDR, *LPITEMHDR;
91 typedef struct tagLISTVIEW_SUBITEM
93 ITEMHDR hdr;
94 INT iSubItem;
95 } LISTVIEW_SUBITEM;
97 typedef struct tagLISTVIEW_ITEM
99 ITEMHDR hdr;
100 UINT state;
101 LPARAM lParam;
102 INT iIndent;
103 BOOL valid;
104 } LISTVIEW_ITEM;
106 typedef struct tagRANGE
108 INT lower;
109 INT upper;
110 } RANGE;
112 typedef struct tagITERATOR
114 INT nItem;
115 RANGE range;
116 HDPA ranges;
117 INT index;
118 } ITERATOR;
120 typedef struct tagLISTVIEW_INFO
122 HWND hwndSelf;
123 HBRUSH hBkBrush;
124 COLORREF clrBk;
125 COLORREF clrText;
126 COLORREF clrTextBk;
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 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
158 BOOL bRedraw;
159 BOOL bFocus;
160 INT nFocusedItem;
161 RECT rcFocus;
162 DWORD dwStyle; /* the cached window GWL_STYLE */
163 DWORD dwLvExStyle; /* extended listview style */
164 INT nItemCount;
165 HDPA hdpaItems;
166 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
167 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
168 PFNLVCOMPARE pfnCompare;
169 LPARAM lParamSort;
170 HWND hwndEdit;
171 BOOL bEditing;
172 WNDPROC EditWndProc;
173 INT nEditLabelItem;
174 DWORD dwHoverTime;
176 DWORD lastKeyPressTimestamp;
177 WPARAM charCode;
178 INT nSearchParamLength;
179 WCHAR szSearchParam[ MAX_PATH ];
180 BOOL bIsDrawing;
181 } LISTVIEW_INFO;
184 * constants
186 /* How many we debug buffer to allocate */
187 #define DEBUG_BUFFERS 20
188 /* The size of a single debug bbuffer */
189 #define DEBUG_BUFFER_SIZE 256
191 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
192 #define SB_INTERNAL -1
194 /* maximum size of a label */
195 #define DISP_TEXT_SIZE 512
197 /* padding for items in list and small icon display modes */
198 #define WIDTH_PADDING 12
200 /* padding for items in list, report and small icon display modes */
201 #define HEIGHT_PADDING 1
203 /* offset of items in report display mode */
204 #define REPORT_MARGINX 2
206 /* padding for icon in large icon display mode
207 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
208 * that HITTEST will see.
209 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
210 * ICON_TOP_PADDING - sum of the two above.
211 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
212 * LABEL_VERT_PADDING - between bottom of text and end of box
214 * ICON_LR_PADDING - additional width above icon size.
215 * ICON_LR_HALF - half of the above value
217 #define ICON_TOP_PADDING_NOTHITABLE 2
218 #define ICON_TOP_PADDING_HITABLE 2
219 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
220 #define ICON_BOTTOM_PADDING 4
221 #define LABEL_VERT_PADDING 7
222 #define ICON_LR_PADDING 16
223 #define ICON_LR_HALF (ICON_LR_PADDING/2)
225 /* default label width for items in list and small icon display modes */
226 #define DEFAULT_LABEL_WIDTH 40
228 /* default column width for items in list display mode */
229 #define DEFAULT_COLUMN_WIDTH 128
231 /* Size of "line" scroll for V & H scrolls */
232 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
234 /* Padding betwen image and label */
235 #define IMAGE_PADDING 2
237 /* Padding behind the label */
238 #define TRAILING_PADDING 5
240 /* Border for the icon caption */
241 #define CAPTION_BORDER 2
243 /* Standard DrawText flags */
244 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
245 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
246 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
248 /* The time in milisecods to reset the search in the list */
249 #define KEY_DELAY 450
251 /* Dump the LISTVIEW_INFO structure to the debug channel */
252 #define LISTVIEW_DUMP(iP) do { \
253 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
254 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
255 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
256 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
257 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
258 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
259 (iP->bFocus) ? "true" : "false"); \
260 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
261 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
262 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
263 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
264 iP->hwndSelf, \
265 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
266 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
267 } while(0)
271 * forward declarations
273 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
274 static BOOL LISTVIEW_GetItemMeasures(LISTVIEW_INFO *, INT, LPRECT, LPRECT, LPRECT, LPRECT);
275 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
276 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
277 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
278 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *);
279 static BOOL LISTVIEW_GetItemListOrigin(LISTVIEW_INFO *, INT, LPPOINT);
280 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
281 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
282 static INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *);
283 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *, INT, LPRECT);
284 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
285 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *, INT);
286 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
287 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
288 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
289 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
290 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, POINT);
291 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
292 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
293 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
294 static void LISTVIEW_UnsupportedStyles(LONG);
295 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
296 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
297 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
298 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
299 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
300 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
301 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
302 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
303 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
304 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
305 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
306 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
307 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
309 /******** Text handling functions *************************************/
311 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
312 * text string. The string may be ANSI or Unicode, in which case
313 * the boolean isW tells us the type of the string.
315 * The name of the function tell what type of strings it expects:
316 * W: Unicode, T: ANSI/Unicode - function of isW
319 static inline BOOL is_textW(LPCWSTR text)
321 return text != NULL && text != LPSTR_TEXTCALLBACKW;
324 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
326 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
327 return is_textW(text);
330 static inline int textlenT(LPCWSTR text, BOOL isW)
332 return !is_textT(text, isW) ? 0 :
333 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
336 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
338 if (isDestW)
339 if (isSrcW) lstrcpynW(dest, src, max);
340 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
341 else
342 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
343 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
346 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
348 LPWSTR wstr = (LPWSTR)text;
350 if (!isW && is_textT(text, isW))
352 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
353 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
354 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
356 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
357 return wstr;
360 static inline void textfreeT(LPWSTR wstr, BOOL isW)
362 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
366 * dest is a pointer to a Unicode string
367 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
369 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
371 BOOL bResult = TRUE;
373 if (src == LPSTR_TEXTCALLBACKW)
375 if (is_textW(*dest)) COMCTL32_Free(*dest);
376 *dest = LPSTR_TEXTCALLBACKW;
378 else
380 LPWSTR pszText = textdupTtoW(src, isW);
381 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
382 bResult = Str_SetPtrW(dest, pszText);
383 textfreeT(pszText, isW);
385 return bResult;
389 * compares a Unicode to a Unicode/ANSI text string
391 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
393 if (!aw) return bt ? -1 : 0;
394 if (!bt) return aw ? 1 : 0;
395 if (aw == LPSTR_TEXTCALLBACKW)
396 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
397 if (bt != LPSTR_TEXTCALLBACKW)
399 LPWSTR bw = textdupTtoW(bt, isW);
400 int r = bw ? lstrcmpW(aw, bw) : 1;
401 textfreeT(bw, isW);
402 return r;
405 return 1;
408 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
410 int res;
412 n = min(min(n, strlenW(s1)), strlenW(s2));
413 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
414 return res ? res - sizeof(WCHAR) : res;
417 /******** Debugging functions *****************************************/
419 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
421 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
422 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
425 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
427 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
428 n = min(textlenT(text, isW), n);
429 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
432 static char* debug_getbuf()
434 static int index = 0;
435 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
436 return buffers[index++ % DEBUG_BUFFERS];
439 static inline char* debugpoint(const POINT* lppt)
441 if (lppt)
443 char* buf = debug_getbuf();
444 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
445 return buf;
446 } else return "(null)";
449 static inline char* debugrect(const RECT* rect)
451 if (rect)
453 char* buf = debug_getbuf();
454 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
455 rect->left, rect->top, rect->right, rect->bottom);
456 return buf;
457 } else return "(null)";
460 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
462 char* buf = debug_getbuf(), *text = buf;
463 int len, size = DEBUG_BUFFER_SIZE;
465 if (lpLVItem == NULL) return "(null)";
466 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
467 if (len == -1) goto end; buf += len; size -= len;
468 if (lpLVItem->mask & LVIF_STATE)
469 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
470 else len = 0;
471 if (len == -1) goto end; buf += len; size -= len;
472 if (lpLVItem->mask & LVIF_TEXT)
473 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
474 else len = 0;
475 if (len == -1) goto end; buf += len; size -= len;
476 if (lpLVItem->mask & LVIF_IMAGE)
477 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
478 else len = 0;
479 if (len == -1) goto end; buf += len; size -= len;
480 if (lpLVItem->mask & LVIF_PARAM)
481 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
482 else len = 0;
483 if (len == -1) goto end; buf += len; size -= len;
484 if (lpLVItem->mask & LVIF_INDENT)
485 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
486 else len = 0;
487 if (len == -1) goto end; buf += len; size -= len;
488 goto undo;
489 end:
490 buf = text + strlen(text);
491 undo:
492 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
493 return text;
496 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
498 char* buf = debug_getbuf(), *text = buf;
499 int len, size = DEBUG_BUFFER_SIZE;
501 if (lpColumn == NULL) return "(null)";
502 len = snprintf(buf, size, "{");
503 if (len == -1) goto end; buf += len; size -= len;
504 if (lpColumn->mask & LVCF_SUBITEM)
505 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
506 else len = 0;
507 if (len == -1) goto end; buf += len; size -= len;
508 if (lpColumn->mask & LVCF_FMT)
509 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
510 else len = 0;
511 if (len == -1) goto end; buf += len; size -= len;
512 if (lpColumn->mask & LVCF_WIDTH)
513 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
514 else len = 0;
515 if (len == -1) goto end; buf += len; size -= len;
516 if (lpColumn->mask & LVCF_TEXT)
517 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
518 else len = 0;
519 if (len == -1) goto end; buf += len; size -= len;
520 if (lpColumn->mask & LVCF_IMAGE)
521 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
522 else len = 0;
523 if (len == -1) goto end; buf += len; size -= len;
524 if (lpColumn->mask & LVCF_ORDER)
525 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
526 else len = 0;
527 if (len == -1) goto end; buf += len; size -= len;
528 goto undo;
529 end:
530 buf = text + strlen(text);
531 undo:
532 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
533 return text;
537 /******** Notification functions i************************************/
539 static inline BOOL notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
541 pnmh->hwndFrom = infoPtr->hwndSelf;
542 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
543 pnmh->code = code;
544 return (BOOL)SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
545 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
548 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code)
550 NMHDR nmh;
551 return notify_hdr(infoPtr, code, &nmh);
554 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
556 notify(infoPtr, LVN_ITEMACTIVATE);
559 static inline BOOL notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
561 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
564 static BOOL notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
566 NMLISTVIEW nmlv;
568 ZeroMemory(&nmlv, sizeof(nmlv));
569 nmlv.iItem = lvht->iItem;
570 nmlv.iSubItem = lvht->iSubItem;
571 nmlv.ptAction = lvht->pt;
572 return notify_listview(infoPtr, NM_RCLICK, &nmlv);
575 static int get_ansi_notification(INT unicodeNotificationCode)
577 switch (unicodeNotificationCode)
579 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
580 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
581 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
582 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
583 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
584 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
586 ERR("unknown notification %x\n", unicodeNotificationCode);
587 return unicodeNotificationCode;
591 Send notification. depends on dispinfoW having same
592 structure as dispinfoA.
593 infoPtr : listview struct
594 notificationCode : *Unicode* notification code
595 pdi : dispinfo structure (can be unicode or ansi)
596 isW : TRUE if dispinfo is Unicode
598 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
600 BOOL bResult = FALSE;
601 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
602 INT realNotifCode;
603 INT cchTempBufMax = 0, savCchTextMax = 0;
604 LPWSTR pszTempBuf = NULL, savPszText = NULL;
606 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
608 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
609 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
612 if (convertToAnsi || convertToUnicode)
614 if (notificationCode != LVN_GETDISPINFOW)
616 cchTempBufMax = convertToUnicode ?
617 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
618 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
620 else
622 cchTempBufMax = pdi->item.cchTextMax;
623 *pdi->item.pszText = 0; /* make sure we don't process garbage */
626 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
627 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
628 if (!pszTempBuf) return FALSE;
629 if (convertToUnicode)
630 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
631 pszTempBuf, cchTempBufMax);
632 else
633 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
634 cchTempBufMax, NULL, NULL);
635 savCchTextMax = pdi->item.cchTextMax;
636 savPszText = pdi->item.pszText;
637 pdi->item.pszText = pszTempBuf;
638 pdi->item.cchTextMax = cchTempBufMax;
641 if (infoPtr->notifyFormat == NFR_ANSI)
642 realNotifCode = get_ansi_notification(notificationCode);
643 else
644 realNotifCode = notificationCode;
645 bResult = notify_hdr(infoPtr, realNotifCode, (LPNMHDR)pdi);
647 if (convertToUnicode || convertToAnsi)
649 if (convertToUnicode) /* note : pointer can be changed by app ! */
650 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
651 savCchTextMax, NULL, NULL);
652 else
653 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
654 savPszText, savCchTextMax);
655 pdi->item.pszText = savPszText; /* restores our buffer */
656 pdi->item.cchTextMax = savCchTextMax;
657 HeapFree(GetProcessHeap(), 0, pszTempBuf);
659 return bResult;
662 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc, LPRECT rcBounds, LVITEMW *lpLVItem)
664 BOOL isSelected;
666 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
667 lpnmlvcd->nmcd.hdc = hdc;
668 lpnmlvcd->nmcd.rc = *rcBounds;
669 if (lpLVItem)
671 lpnmlvcd->nmcd.dwItemSpec = lpLVItem->iItem;
672 lpnmlvcd->iSubItem = lpLVItem->iSubItem;
673 if (lpLVItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
674 if (lpLVItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
675 if (lpLVItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
676 lpnmlvcd->nmcd.lItemlParam = lpLVItem->lParam;
679 isSelected = lpnmlvcd->nmcd.uItemState & CDIS_SELECTED;
680 /* subitems are selected only in full-row-select, report mode */
681 if ( lpnmlvcd->iSubItem != 0 &&
682 !((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
683 (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) )
684 isSelected = FALSE;
685 if (isSelected && infoPtr->bFocus)
687 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
688 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
690 else if (isSelected && (infoPtr->dwStyle & LVS_SHOWSELALWAYS))
692 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
693 lpnmlvcd->clrText = comctl32_color.clrBtnText;
695 else
697 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
698 lpnmlvcd->clrText = infoPtr->clrText;
702 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
704 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
705 return notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
708 /******** Item iterator functions **********************************/
710 static BOOL ranges_add(HDPA ranges, RANGE range);
711 static BOOL ranges_del(HDPA ranges, RANGE range);
712 static void ranges_dump(HDPA ranges);
714 static inline BOOL iterator_next(ITERATOR* i)
716 if (i->nItem == -1)
718 if (i->ranges) goto pickarange;
719 return (i->nItem = i->range.lower) != -1;
721 i->nItem++;
722 if (i->nItem <= i->range.upper)
723 return TRUE;
725 pickarange:
726 if (i->ranges && i->index < i->ranges->nItemCount)
728 i->range = *(RANGE*)DPA_GetPtr(i->ranges, i->index++);
729 return (i->nItem = i->range.lower) != -1;
731 i->nItem = i->range.lower = i->range.upper = -1;
732 return FALSE;
735 static RANGE iterator_range(ITERATOR* i)
737 RANGE range;
739 if (!i->ranges) return i->range;
741 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges, 0)).lower;
742 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges, i->ranges->nItemCount - 1)).upper;
743 return range;
746 static inline void iterator_destroy(ITERATOR* i)
748 if (i->ranges) DPA_Destroy(i->ranges);
751 static inline BOOL iterator_empty(ITERATOR* i)
753 ZeroMemory(i, sizeof(*i));
754 i->nItem = i->range.lower = i->range.upper = -1;
755 return TRUE;
758 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
760 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
761 INT lower, upper;
762 RECT frame = *lprc;
763 POINT Origin;
765 /* in case we fail, we want to return an empty iterator */
766 if (!iterator_empty(i)) return FALSE;
768 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
770 OffsetRect(&frame, -Origin.x, -Origin.y);
772 if (uView == LVS_ICON || uView == LVS_SMALLICON)
774 /* FIXME: we got to do better then this */
775 i->range.lower = 0;
776 i->range.upper = infoPtr->nItemCount - 1;
777 return TRUE;
779 else if (uView == LVS_REPORT)
781 if (frame.left >= infoPtr->nItemWidth) return TRUE;
782 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
784 lower = frame.top / infoPtr->nItemHeight;
785 upper = (frame.bottom - 1) / infoPtr->nItemHeight;
787 else
789 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
790 if (frame.top >= infoPtr->nItemHeight * nPerCol) return TRUE;
791 lower = (frame.left / infoPtr->nItemWidth) * nPerCol + frame.top / infoPtr->nItemHeight;
792 upper = ((frame.right - 1) / infoPtr->nItemWidth) * nPerCol + (frame.bottom - 1) / infoPtr->nItemHeight;
795 if (upper < 0 || lower >= infoPtr->nItemCount) return TRUE;
796 lower = max(lower, 0);
797 upper = min(upper, infoPtr->nItemCount - 1);
798 if (upper < lower) return TRUE;
799 i->range.lower = lower;
800 i->range.upper = upper;
802 return TRUE;
805 static BOOL iterator_clippeditems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
807 POINT Origin, Position;
808 RECT rcItem, rcClip;
809 INT nItem, rgntype;
811 rgntype = GetClipBox(hdc, &rcClip);
812 if (rgntype == NULLREGION) return iterator_empty(i);
813 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
814 if (rgntype == SIMPLEREGION) return TRUE;
816 /* if we can't deal with the region, we'll just go with the simple range */
817 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return TRUE;
818 if (!(i->ranges = DPA_Create(10))) return TRUE;
819 if (!ranges_add(i->ranges, i->range))
821 DPA_Destroy(i->ranges);
822 i->ranges = 0;
823 return TRUE;
826 /* no delete the invisible items from the list */
827 for (nItem = i->range.lower; nItem <= i->range.upper; nItem++)
829 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) continue;
830 rcItem.left = Position.x + Origin.x;
831 rcItem.top = Position.y + Origin.y;
832 rcItem.right = rcItem.left + infoPtr->nItemWidth;
833 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
834 if (!RectVisible(hdc, &rcItem))
836 RANGE item_range = { nItem, nItem };
837 ranges_del(i->ranges, item_range);
841 return TRUE;
844 static inline BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr)
846 return iterator_frameditems(i, infoPtr, &infoPtr->rcList);
849 /******** Misc helper functions ************************************/
851 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
852 WPARAM wParam, LPARAM lParam, BOOL isW)
854 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
855 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
858 /******** Internal API functions ************************************/
860 /* The Invalidate* are macros, so we preserve the caller location */
861 #define LISTVIEW_InvalidateRect(infoPtr, rect) while(infoPtr->bRedraw) { \
862 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
863 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
864 break;\
867 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
868 RECT rcItem; \
869 rcItem.left = LVIR_BOUNDS; \
870 if(LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) \
871 LISTVIEW_InvalidateRect(infoPtr, &rcItem); \
872 } while (0)
874 #define LISTVIEW_InvalidateList(infoPtr)\
875 LISTVIEW_InvalidateRect(infoPtr, NULL)
877 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
879 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
882 /***
883 * DESCRIPTION:
884 * Retrieves the number of items that can fit vertically in the client area.
886 * PARAMETER(S):
887 * [I] infoPtr : valid pointer to the listview structure
889 * RETURN:
890 * Number of items per row.
892 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
894 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
896 return max(nListWidth/infoPtr->nItemWidth, 1);
899 /***
900 * DESCRIPTION:
901 * Retrieves the number of items that can fit horizontally in the client
902 * area.
904 * PARAMETER(S):
905 * [I] infoPtr : valid pointer to the listview structure
907 * RETURN:
908 * Number of items per column.
910 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
912 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
914 return max(nListHeight / infoPtr->nItemHeight, 1);
918 /*************************************************************************
919 * LISTVIEW_ProcessLetterKeys
921 * Processes keyboard messages generated by pressing the letter keys
922 * on the keyboard.
923 * What this does is perform a case insensitive search from the
924 * current position with the following quirks:
925 * - If two chars or more are pressed in quick succession we search
926 * for the corresponding string (e.g. 'abc').
927 * - If there is a delay we wipe away the current search string and
928 * restart with just that char.
929 * - If the user keeps pressing the same character, whether slowly or
930 * fast, so that the search string is entirely composed of this
931 * character ('aaaaa' for instance), then we search for first item
932 * that starting with that character.
933 * - If the user types the above character in quick succession, then
934 * we must also search for the corresponding string ('aaaaa'), and
935 * go to that string if there is a match.
937 * PARAMETERS
938 * [I] hwnd : handle to the window
939 * [I] charCode : the character code, the actual character
940 * [I] keyData : key data
942 * RETURNS
944 * Zero.
946 * BUGS
948 * - The current implementation has a list of characters it will
949 * accept and it ignores averything else. In particular it will
950 * ignore accentuated characters which seems to match what
951 * Windows does. But I'm not sure it makes sense to follow
952 * Windows there.
953 * - We don't sound a beep when the search fails.
955 * SEE ALSO
957 * TREEVIEW_ProcessLetterKeys
959 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
961 INT nItem;
962 INT endidx,idx;
963 LVITEMW item;
964 WCHAR buffer[MAX_PATH];
965 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
967 /* simple parameter checking */
968 if (!charCode || !keyData) return 0;
970 /* only allow the valid WM_CHARs through */
971 if (!isalnum(charCode) &&
972 charCode != '.' && charCode != '`' && charCode != '!' &&
973 charCode != '@' && charCode != '#' && charCode != '$' &&
974 charCode != '%' && charCode != '^' && charCode != '&' &&
975 charCode != '*' && charCode != '(' && charCode != ')' &&
976 charCode != '-' && charCode != '_' && charCode != '+' &&
977 charCode != '=' && charCode != '\\'&& charCode != ']' &&
978 charCode != '}' && charCode != '[' && charCode != '{' &&
979 charCode != '/' && charCode != '?' && charCode != '>' &&
980 charCode != '<' && charCode != ',' && charCode != '~')
981 return 0;
983 /* if there's one item or less, there is no where to go */
984 if (infoPtr->nItemCount <= 1) return 0;
986 /* update the search parameters */
987 infoPtr->lastKeyPressTimestamp = GetTickCount();
988 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
989 if (infoPtr->nSearchParamLength < MAX_PATH)
990 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
991 if (infoPtr->charCode != charCode)
992 infoPtr->charCode = charCode = 0;
993 } else {
994 infoPtr->charCode=charCode;
995 infoPtr->szSearchParam[0]=charCode;
996 infoPtr->nSearchParamLength=1;
997 /* Redundant with the 1 char string */
998 charCode=0;
1001 /* and search from the current position */
1002 nItem=-1;
1003 if (infoPtr->nFocusedItem >= 0) {
1004 endidx=infoPtr->nFocusedItem;
1005 idx=endidx;
1006 /* if looking for single character match,
1007 * then we must always move forward
1009 if (infoPtr->nSearchParamLength == 1)
1010 idx++;
1011 } else {
1012 endidx=infoPtr->nItemCount;
1013 idx=0;
1015 do {
1016 if (idx == infoPtr->nItemCount) {
1017 if (endidx == infoPtr->nItemCount || endidx == 0)
1018 break;
1019 idx=0;
1022 /* get item */
1023 item.mask = LVIF_TEXT;
1024 item.iItem = idx;
1025 item.iSubItem = 0;
1026 item.pszText = buffer;
1027 item.cchTextMax = MAX_PATH;
1028 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1030 /* check for a match */
1031 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1032 nItem=idx;
1033 break;
1034 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1035 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1036 /* This would work but we must keep looking for a longer match */
1037 nItem=idx;
1039 idx++;
1040 } while (idx != endidx);
1042 if (nItem != -1)
1043 LISTVIEW_KeySelection(infoPtr, nItem);
1045 return 0;
1048 /*************************************************************************
1049 * LISTVIEW_UpdateHeaderSize [Internal]
1051 * Function to resize the header control
1053 * PARAMS
1054 * hwnd [I] handle to a window
1055 * nNewScrollPos [I] Scroll Pos to Set
1057 * RETURNS
1058 * nothing
1060 * NOTES
1062 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1064 RECT winRect;
1065 POINT point[2];
1067 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1069 GetWindowRect(infoPtr->hwndHeader, &winRect);
1070 point[0].x = winRect.left;
1071 point[0].y = winRect.top;
1072 point[1].x = winRect.right;
1073 point[1].y = winRect.bottom;
1075 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1076 point[0].x = -nNewScrollPos;
1077 point[1].x += nNewScrollPos;
1079 SetWindowPos(infoPtr->hwndHeader,0,
1080 point[0].x,point[0].y,point[1].x,point[1].y,
1081 SWP_NOZORDER | SWP_NOACTIVATE);
1084 /***
1085 * DESCRIPTION:
1086 * Update the scrollbars. This functions should be called whenever
1087 * the content, size or view changes.
1089 * PARAMETER(S):
1090 * [I] infoPtr : valid pointer to the listview structure
1092 * RETURN:
1093 * None
1095 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1097 LONG lStyle = infoPtr->dwStyle;
1098 UINT uView = lStyle & LVS_TYPEMASK;
1099 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1100 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1101 SCROLLINFO scrollInfo;
1103 if (lStyle & LVS_NOSCROLL) return;
1105 scrollInfo.cbSize = sizeof(SCROLLINFO);
1107 if (uView == LVS_LIST)
1109 /* update horizontal scrollbar */
1110 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1111 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1113 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1114 infoPtr->nItemCount, nCountPerColumn, nCountPerRow);
1116 scrollInfo.nMin = 0;
1117 scrollInfo.nMax = infoPtr->nItemCount / nCountPerColumn;
1118 if((infoPtr->nItemCount % nCountPerColumn) == 0)
1119 scrollInfo.nMax--;
1120 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1121 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1122 scrollInfo.nPage = nCountPerRow;
1123 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1124 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1125 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1127 else if (uView == LVS_REPORT)
1129 BOOL test;
1131 /* update vertical scrollbar */
1132 scrollInfo.nMin = 0;
1133 scrollInfo.nMax = infoPtr->nItemCount - 1;
1134 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1135 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1136 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1137 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1138 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1139 scrollInfo.nMax, scrollInfo.nPage, test);
1140 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1141 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1143 /* update horizontal scrollbar */
1144 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1145 scrollInfo.fMask = SIF_POS;
1146 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1147 || infoPtr->nItemCount == 0)
1149 scrollInfo.nPos = 0;
1151 scrollInfo.nMin = 0;
1152 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1153 scrollInfo.nPage = nListWidth;
1154 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1155 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1156 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1157 scrollInfo.nMax, scrollInfo.nPage, test);
1158 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1159 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1161 /* Update the Header Control */
1162 scrollInfo.fMask = SIF_POS;
1163 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1164 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1167 else
1169 RECT rcView;
1171 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1173 INT nViewWidth = rcView.right - rcView.left;
1174 INT nViewHeight = rcView.bottom - rcView.top;
1176 /* Update Horizontal Scrollbar */
1177 scrollInfo.fMask = SIF_POS;
1178 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1179 || infoPtr->nItemCount == 0)
1181 scrollInfo.nPos = 0;
1183 scrollInfo.nMin = 0;
1184 scrollInfo.nMax = max(nViewWidth, 0)-1;
1185 scrollInfo.nPage = nListWidth;
1186 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1187 TRACE("LVS_ICON/SMALLICON Horz.\n");
1188 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1190 /* Update Vertical Scrollbar */
1191 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1192 scrollInfo.fMask = SIF_POS;
1193 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1194 || infoPtr->nItemCount == 0)
1196 scrollInfo.nPos = 0;
1198 scrollInfo.nMin = 0;
1199 scrollInfo.nMax = max(nViewHeight,0)-1;
1200 scrollInfo.nPage = nListHeight;
1201 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1202 TRACE("LVS_ICON/SMALLICON Vert.\n");
1203 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1209 /***
1210 * DESCRIPTION:
1211 * Shows/hides the focus rectangle.
1213 * PARAMETER(S):
1214 * [I] infoPtr : valid pointer to the listview structure
1215 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1217 * RETURN:
1218 * None
1220 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1222 HDC hdc;
1224 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1226 if (infoPtr->nFocusedItem < 0) return;
1228 /* we need some gymnastics in ICON mode to handle large items */
1229 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1231 RECT rcBox;
1233 if (!LISTVIEW_GetItemMeasures(infoPtr, infoPtr->nFocusedItem, &rcBox, 0, 0, 0))
1234 return;
1235 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1237 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1238 return;
1242 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1244 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
1246 DRAWITEMSTRUCT dis;
1247 LVITEMW item;
1249 item.iItem = infoPtr->nFocusedItem;
1250 item.iSubItem = 0;
1251 item.mask = LVIF_PARAM;
1252 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1254 ZeroMemory(&dis, sizeof(dis));
1255 dis.CtlType = ODT_LISTVIEW;
1256 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1257 dis.itemID = item.iItem;
1258 dis.itemAction = ODA_FOCUS;
1259 if (fShow) dis.itemState |= ODS_FOCUS;
1260 dis.hwndItem = infoPtr->hwndSelf;
1261 dis.hDC = hdc;
1262 if (!LISTVIEW_GetItemMeasures(infoPtr, dis.itemID, &dis.rcItem, 0, 0, 0)) goto done;
1263 dis.itemData = item.lParam;
1265 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1267 else
1269 DrawFocusRect(hdc, &infoPtr->rcFocus);
1271 done:
1272 ReleaseDC(infoPtr->hwndSelf, hdc);
1275 /***
1276 * Invalidates all visible selected items.
1278 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1280 ITERATOR i;
1282 iterator_visibleitems(&i, infoPtr);
1283 while(iterator_next(&i))
1285 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1286 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1288 iterator_destroy(&i);
1292 /***
1293 * DESCRIPTION:
1294 * Prints a message for unsupported window styles.
1295 * A kind of TODO list for window styles.
1297 * PARAMETER(S):
1298 * [I] LONG : window style
1300 * RETURN:
1301 * None
1303 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1305 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1306 FIXME(" LVS_NOSCROLL\n");
1308 if (lStyle & LVS_NOLABELWRAP)
1309 FIXME(" LVS_NOLABELWRAP\n");
1311 if (lStyle & LVS_SORTASCENDING)
1312 FIXME(" LVS_SORTASCENDING\n");
1314 if (lStyle & LVS_SORTDESCENDING)
1315 FIXME(" LVS_SORTDESCENDING\n");
1319 /***
1320 * DESCRIPTION: [INTERNAL]
1321 * Computes an item's (left,top) corner, relative to rcView.
1322 * That is, the position has NOT been made relative to the Origin.
1323 * This is deliberate, to avoid computing the Origin over, and
1324 * over again, when this function is call in a loop. Instead,
1325 * one ca factor the computation of the Origin before the loop,
1326 * and offset the value retured by this function, on every iteration.
1328 * PARAMETER(S):
1329 * [I] infoPtr : valid pointer to the listview structure
1330 * [I] nItem : item number
1331 * [O] lpptOrig : item top, left corner
1333 * RETURN:
1334 * TRUE if computations OK
1335 * FALSE otherwise
1337 static BOOL LISTVIEW_GetItemListOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1339 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1341 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1343 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1344 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1346 else if (uView == LVS_LIST)
1348 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1349 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1350 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1352 else /* LVS_REPORT */
1354 lpptPosition->x = REPORT_MARGINX;
1355 lpptPosition->y = nItem * infoPtr->nItemHeight;
1358 return TRUE;
1361 /***
1362 * DESCRIPTION: [INTERNAL]
1363 * Compute the rectangles of an item. This is to localize all
1364 * the computations in one place. If you are not interested in some
1365 * of these values, simply pass in a NULL -- the fucntion is smart
1366 * enough to compute only what's necessary. The function computes
1367 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1368 * one, the BOX rectangle. This rectangle is very cheap to compute,
1369 * and is guaranteed to contain all the other rectangles. Computing
1370 * the ICON rect is also cheap, but all the others are potentaily
1371 * expensive. This gives an easy and effective optimization when
1372 * searching (like point inclusion, or rectangle intersection):
1373 * first test against the BOX, and if TRUE, test agains the desired
1374 * rectangle. These optimizations are coded in:
1375 * LISTVIEW_PtInRect, and LISTVIEW_IntersectRect
1376 * use them wherever is appropriate.
1378 * PARAMETER(S):
1379 * [I] infoPtr : valid pointer to the listview structure
1380 * [I] nItem : item number
1381 * [O] lprcBox : ptr to Box rectangle
1382 * The internal LVIR_BOX rectangle
1383 * [O] lprcBounds : ptr to Bounds rectangle
1384 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1385 * [O] lprcIcon : ptr to Icon rectangle
1386 * Same as LVM_GETITEMRECT with LVIR_ICON
1387 * [O] lprcLabel : ptr to Label rectangle
1388 * Same as LVM_GETITEMRECT with LVIR_LABEL
1390 * RETURN:
1391 * TRUE if computations OK
1392 * FALSE otherwise
1394 static BOOL LISTVIEW_GetItemMeasures(LISTVIEW_INFO *infoPtr, INT nItem,
1395 LPRECT lprcBox, LPRECT lprcBounds,
1396 LPRECT lprcIcon, LPRECT lprcLabel)
1398 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1399 BOOL doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1400 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1401 RECT Box, Icon, Label;
1402 POINT Position, Origin;
1403 LVITEMW lvItem;
1405 /* Be smart and try to figure out the minimum we have to do */
1406 if (lprcBounds)
1408 if (uView == LVS_REPORT) doIcon = TRUE;
1409 else doLabel = TRUE;
1411 if (uView == LVS_ICON && infoPtr->bFocus &&
1412 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
1413 LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1414 oversizedBox = doLabel = TRUE;
1415 if (lprcLabel) doLabel = TRUE;
1416 if (doLabel || lprcIcon) doIcon = TRUE;
1418 /* get what we need from the item before hand, so we make
1419 * only one request. This can speed up things, if data
1420 * is stored on the app side */
1421 if (doLabel || (doIcon && uView == LVS_REPORT))
1423 lvItem.mask = 0;
1424 if (doIcon) lvItem.mask |= LVIF_INDENT;
1425 if (doLabel) lvItem.mask |= LVIF_TEXT;
1426 lvItem.iItem = nItem;
1427 lvItem.iSubItem = 0;
1428 lvItem.pszText = szDispText;
1429 lvItem.cchTextMax = DISP_TEXT_SIZE;
1430 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
1433 /************************************************************/
1434 /* compute the box rectangle (it should be cheap to do) */
1435 /************************************************************/
1436 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) return FALSE;
1437 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
1438 Box.left = Position.x + Origin.x;
1439 Box.top = Position.y + Origin.y;
1440 Box.right = Box.left + infoPtr->nItemWidth;
1441 Box.bottom = Box.top + infoPtr->nItemHeight;
1443 /************************************************************/
1444 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1445 /************************************************************/
1446 if (doIcon)
1448 if (uView == LVS_ICON)
1450 Icon.left = Box.left;
1451 if (infoPtr->himlNormal)
1452 Icon.left += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2 - ICON_LR_HALF;
1453 Icon.top = Box.top;
1454 Icon.right = Icon.left;
1455 Icon.bottom = Icon.top;
1456 if (infoPtr->himlNormal)
1458 Icon.right += infoPtr->iconSize.cx + ICON_LR_PADDING;
1459 Icon.bottom += infoPtr->iconSize.cy + ICON_TOP_PADDING;
1462 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1464 /* do indent */
1465 Icon.left = Box.left;
1466 if (uView == LVS_REPORT) Icon.left += infoPtr->iconSize.cx * lvItem.iIndent;
1467 if (infoPtr->himlState) Icon.left += infoPtr->iconStateSize.cx;
1468 Icon.top = Box.top;
1469 Icon.right = Icon.left;
1470 if (infoPtr->himlSmall) Icon.right += infoPtr->iconSize.cx;
1471 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1473 if(lprcIcon) *lprcIcon = Icon;
1474 TRACE("hwnd=%x, item=%d, icon=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Icon));
1477 /************************************************************/
1478 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1479 /************************************************************/
1480 if (doLabel)
1482 SIZE labelSize = { 0, 0 };
1484 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
1486 labelSize.cx = infoPtr->nItemWidth;
1487 labelSize.cy = infoPtr->nItemHeight;
1489 else if (is_textT(lvItem.pszText, TRUE))
1491 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1492 HDC hdc = GetDC(infoPtr->hwndSelf);
1493 HFONT hOldFont = SelectObject(hdc, hFont);
1494 UINT uFormat;
1495 RECT rcText;
1497 /* compute rough rectangle where the label will go */
1498 SetRectEmpty(&rcText);
1499 rcText.right = infoPtr->nItemWidth - TRAILING_PADDING;
1500 rcText.bottom = infoPtr->nItemHeight;
1501 if (uView == LVS_ICON)
1502 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1504 /* now figure out the flags */
1505 if (uView == LVS_ICON)
1506 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1507 else
1508 uFormat = LV_SL_DT_FLAGS;
1510 DrawTextW (hdc, lvItem.pszText, -1, &rcText, uFormat | DT_CALCRECT);
1512 labelSize.cx = min(rcText.right - rcText.left + TRAILING_PADDING, infoPtr->nItemWidth);
1513 labelSize.cy = rcText.bottom - rcText.top;
1515 SelectObject(hdc, hOldFont);
1516 ReleaseDC(infoPtr->hwndSelf, hdc);
1519 if (uView == LVS_ICON)
1521 Label.left = Box.left + (infoPtr->iconSpacing.cx - labelSize.cx) / 2;
1522 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1523 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1524 Label.right = Label.left + labelSize.cx;
1525 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
1526 Label.bottom = Label.top + infoPtr->nItemHeight;
1527 else
1529 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1531 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1532 labelSize.cy /= infoPtr->ntmHeight;
1533 labelSize.cy = max(labelSize.cy, 1);
1534 labelSize.cy *= infoPtr->ntmHeight;
1536 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1539 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1541 Label.left = Icon.right;
1542 Label.top = Box.top;
1543 Label.right = Label.left + labelSize.cx;
1544 if (infoPtr->himlSmall) Label.right += IMAGE_PADDING;
1545 if (Label.right > Box.right) Label.right = Box.right;
1546 Label.bottom = Label.top + infoPtr->nItemHeight;
1549 if (lprcLabel) *lprcLabel = Label;
1550 TRACE("hwnd=%x, item=%d, label=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Label));
1553 /***********************************************************/
1554 /* compute bounds box for the item (ala LVM_GETITEMRECT) */
1555 /***********************************************************/
1556 if (lprcBounds)
1558 if (uView == LVS_REPORT)
1560 lprcBounds->left = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT ? Box.left : Icon.left;
1561 lprcBounds->top = Box.top;
1562 lprcBounds->right = min(lprcBounds->left + infoPtr->nItemWidth, Box.right) - REPORT_MARGINX;
1563 if (lprcBounds->right < lprcBounds->left) lprcBounds->right = lprcBounds->left;
1564 lprcBounds->bottom = lprcBounds->top + infoPtr->nItemHeight;
1566 else
1567 UnionRect(lprcBounds, &Icon, &Label);
1568 TRACE("hwnd=%x, item=%d, bounds=%s\n", infoPtr->hwndSelf, nItem, debugrect(lprcBounds));
1571 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1572 else if (lprcBox) *lprcBox = Box;
1573 TRACE("hwnd=%x, item=%d, box=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Box));
1575 return TRUE;
1578 /***
1579 * DESCRIPTION:
1580 * Aligns the items with the top edge of the window.
1582 * PARAMETER(S):
1583 * [I] infoPtr : valid pointer to the listview structure
1585 * RETURN:
1586 * None
1588 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1590 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1591 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1592 POINT ptItem;
1593 RECT rcView;
1594 INT i, off_x=0, off_y=0;
1596 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1598 /* Since SetItemPosition uses upper-left of icon, and for
1599 style=LVS_ICON the icon is not left adjusted, get the offset */
1600 if (uView == LVS_ICON)
1602 off_y = ICON_TOP_PADDING;
1603 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1605 ptItem.x = off_x;
1606 ptItem.y = off_y;
1607 ZeroMemory(&rcView, sizeof(RECT));
1608 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1609 off_x, off_y,
1610 infoPtr->rcList.left, infoPtr->rcList.right);
1612 if (nListWidth > infoPtr->nItemWidth)
1614 for (i = 0; i < infoPtr->nItemCount; i++)
1616 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1618 ptItem.x = off_x;
1619 ptItem.y += infoPtr->nItemHeight;
1622 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1623 ptItem.x += infoPtr->nItemWidth;
1624 rcView.right = max(rcView.right, ptItem.x);
1627 rcView.right -= off_x;
1628 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1630 else
1632 for (i = 0; i < infoPtr->nItemCount; i++)
1634 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1635 ptItem.y += infoPtr->nItemHeight;
1638 rcView.right = infoPtr->nItemWidth;
1639 rcView.bottom = ptItem.y-off_y;
1642 infoPtr->rcView = rcView;
1646 /***
1647 * DESCRIPTION:
1648 * Aligns the items with the left edge of the window.
1650 * PARAMETER(S):
1651 * [I] infoPtr : valid pointer to the listview structure
1653 * RETURN:
1654 * None
1656 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1658 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1659 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1660 POINT ptItem;
1661 RECT rcView;
1662 INT i, off_x=0, off_y=0;
1664 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1666 /* Since SetItemPosition uses upper-left of icon, and for
1667 style=LVS_ICON the icon is not left adjusted, get the offset */
1668 if (uView == LVS_ICON)
1670 off_y = ICON_TOP_PADDING;
1671 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1673 ptItem.x = off_x;
1674 ptItem.y = off_y;
1675 ZeroMemory(&rcView, sizeof(RECT));
1676 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1678 if (nListHeight > infoPtr->nItemHeight)
1680 for (i = 0; i < infoPtr->nItemCount; i++)
1682 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1684 ptItem.y = off_y;
1685 ptItem.x += infoPtr->nItemWidth;
1688 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1689 ptItem.y += infoPtr->nItemHeight;
1690 rcView.bottom = max(rcView.bottom, ptItem.y);
1693 rcView.right = ptItem.x + infoPtr->nItemWidth;
1695 else
1697 for (i = 0; i < infoPtr->nItemCount; i++)
1699 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1700 ptItem.x += infoPtr->nItemWidth;
1703 rcView.bottom = infoPtr->nItemHeight;
1704 rcView.right = ptItem.x;
1707 infoPtr->rcView = rcView;
1712 /***
1713 * DESCRIPTION:
1714 * Retrieves the bounding rectangle of all the items.
1716 * PARAMETER(S):
1717 * [I] infoPtr : valid pointer to the listview structure
1718 * [O] lprcView : bounding rectangle
1720 * RETURN:
1721 * SUCCESS : TRUE
1722 * FAILURE : FALSE
1724 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1726 POINT ptOrigin;
1728 TRACE("(lprcView=%p)\n", lprcView);
1730 if (!lprcView) return FALSE;
1732 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return FALSE;
1734 *lprcView = infoPtr->rcView;
1735 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
1737 TRACE("lprcView=%s\n", debugrect(lprcView));
1739 return TRUE;
1742 /***
1743 * DESCRIPTION:
1744 * Retrieves the subitem pointer associated with the subitem index.
1746 * PARAMETER(S):
1747 * [I] HDPA : DPA handle for a specific item
1748 * [I] INT : index of subitem
1750 * RETURN:
1751 * SUCCESS : subitem pointer
1752 * FAILURE : NULL
1754 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1755 INT nSubItem)
1757 LISTVIEW_SUBITEM *lpSubItem;
1758 INT i;
1760 /* we should binary search here if need be */
1761 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1763 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1764 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
1765 return lpSubItem;
1768 return NULL;
1772 /***
1773 * DESCRIPTION:
1774 * Calculates the width of a specific item.
1776 * PARAMETER(S):
1777 * [I] infoPtr : valid pointer to the listview structure
1778 * [I] nItem : item to calculate width, or -1 for max of all
1780 * RETURN:
1781 * Returns the width of an item width an item.
1783 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr, INT nItem)
1785 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1786 INT nItemWidth = 0, i;
1788 if (uView == LVS_ICON)
1789 nItemWidth = infoPtr->iconSpacing.cx;
1790 else if (uView == LVS_REPORT)
1792 INT nHeaderItemCount;
1793 RECT rcHeaderItem;
1795 /* calculate width of header */
1796 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1797 for (i = 0; i < nHeaderItemCount; i++)
1798 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem))
1799 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1801 else
1803 INT nLabelWidth;
1805 if (infoPtr->nItemCount == 0) return DEFAULT_COLUMN_WIDTH;
1807 /* get width of string */
1808 if (nItem == -1)
1810 for (i = 0; i < infoPtr->nItemCount; i++)
1812 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
1813 nItemWidth = max(nItemWidth, nLabelWidth);
1816 else
1817 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1818 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
1819 nItemWidth += WIDTH_PADDING;
1820 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
1821 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
1822 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1825 return max(nItemWidth, 1);
1828 /***
1829 * DESCRIPTION:
1830 * Calculates the max width of any item in the list.
1832 * PARAMETER(S):
1833 * [I] infoPtr : valid pointer to the listview structure
1834 * [I] LONG : window style
1836 * RETURN:
1837 * Returns item width.
1839 static inline INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *infoPtr)
1841 return LISTVIEW_CalculateItemWidth(infoPtr, -1);
1844 /***
1845 * DESCRIPTION:
1846 * Retrieves and saves important text metrics info for the current
1847 * Listview font.
1849 * PARAMETER(S):
1850 * [I] infoPtr : valid pointer to the listview structure
1853 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
1855 TEXTMETRICW tm;
1856 HDC hdc = GetDC(infoPtr->hwndSelf);
1857 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1858 INT oldHeight, oldACW;
1860 GetTextMetricsW(hdc, &tm);
1862 oldHeight = infoPtr->ntmHeight;
1863 oldACW = infoPtr->ntmAveCharWidth;
1864 infoPtr->ntmHeight = tm.tmHeight;
1865 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1867 SelectObject(hdc, hOldFont);
1868 ReleaseDC(infoPtr->hwndSelf, hdc);
1869 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1870 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1874 /***
1875 * DESCRIPTION:
1876 * Calculates the height of an item.
1878 * PARAMETER(S):
1879 * [I] infoPtr : valid pointer to the listview structure
1881 * RETURN:
1882 * Returns item height.
1884 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *infoPtr)
1886 INT nItemHeight;
1888 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
1889 nItemHeight = infoPtr->iconSpacing.cy;
1890 else
1892 nItemHeight = infoPtr->ntmHeight;
1893 if (infoPtr->himlState)
1894 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
1895 if (infoPtr->himlSmall)
1896 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
1897 if (infoPtr->himlState || infoPtr->himlSmall)
1898 nItemHeight += HEIGHT_PADDING;
1900 return nItemHeight;
1903 #if 0
1904 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
1906 ERR("Selections are:\n");
1907 ranges_dump(infoPtr->hdpaSelectionRanges);
1909 #endif
1911 /***
1912 * DESCRIPTION:
1913 * A compare function for ranges
1915 * PARAMETER(S)
1916 * [I] range1 : pointer to range 1;
1917 * [I] range2 : pointer to range 2;
1918 * [I] flags : flags
1920 * RETURNS:
1921 * >0 : if Item 1 > Item 2
1922 * <0 : if Item 2 > Item 1
1923 * 0 : if Item 1 == Item 2
1925 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
1927 if (((RANGE*)range1)->upper < ((RANGE*)range2)->lower)
1928 return -1;
1929 if (((RANGE*)range2)->upper < ((RANGE*)range1)->lower)
1930 return 1;
1931 return 0;
1934 static void ranges_dump(HDPA ranges)
1936 INT i;
1938 for (i = 0; i < ranges->nItemCount; i++)
1940 RANGE *selection = DPA_GetPtr(ranges, i);
1941 ERR(" [%d - %d]\n", selection->lower, selection->upper);
1945 static inline BOOL ranges_contain(HDPA ranges, INT nItem)
1947 RANGE srchrng = { nItem, nItem };
1949 return DPA_Search(ranges, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
1952 static BOOL ranges_shift(HDPA ranges, INT nItem, INT delta)
1954 RANGE srchrng, *chkrng;
1955 INT index;
1957 srchrng.upper = nItem;
1958 srchrng.lower = nItem;
1960 index = DPA_Search(ranges, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
1961 if (index == -1) return TRUE;
1963 for (;index < ranges->nItemCount; index++)
1965 chkrng = DPA_GetPtr(ranges, index);
1966 if (chkrng->lower >= nItem && chkrng->lower + delta >= 0)
1967 chkrng->lower += delta;
1968 if (chkrng->upper >= nItem && chkrng->upper + delta >= 0)
1969 chkrng->upper += delta;
1971 return TRUE;
1974 static BOOL ranges_add(HDPA ranges, RANGE range)
1976 RANGE srchrgn;
1977 INT index;
1979 TRACE("range=(%i - %i)\n", range.lower, range.upper);
1980 if (TRACE_ON(listview)) ranges_dump(ranges);
1982 /* try find overlapping regions first */
1983 srchrgn.lower = range.lower - 1;
1984 srchrgn.upper = range.upper + 1;
1985 index = DPA_Search(ranges, &srchrgn, 0, ranges_cmp, 0, 0);
1987 if (index == -1)
1989 RANGE *newrgn;
1991 /* create the brand new range to insert */
1992 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
1993 if(!newrgn) return FALSE;
1994 *newrgn = range;
1996 /* figure out where to insert it */
1997 index = DPA_Search(ranges, newrgn, 0, ranges_cmp, 0, DPAS_INSERTAFTER);
1998 if (index == -1) index = 0;
2000 /* and get it over with */
2001 DPA_InsertPtr(ranges, index, newrgn);
2003 else
2005 RANGE *chkrgn, *mrgrgn;
2006 INT fromindex, mergeindex;
2008 chkrgn = DPA_GetPtr(ranges, index);
2009 if (!chkrgn) return FALSE;
2010 TRACE("Merge with index %i (%d - %d)\n",
2011 index, chkrgn->lower, chkrgn->upper);
2013 chkrgn->lower = min(range.lower, chkrgn->lower);
2014 chkrgn->upper = max(range.upper, chkrgn->upper);
2016 TRACE("New range %i (%d - %d)\n",
2017 index, chkrgn->lower, chkrgn->upper);
2019 /* merge now common anges */
2020 fromindex = 0;
2021 srchrgn.lower = chkrgn->lower - 1;
2022 srchrgn.upper = chkrgn->upper + 1;
2026 mergeindex = DPA_Search(ranges, &srchrgn, fromindex, ranges_cmp, 0, 0);
2027 if (mergeindex == -1) break;
2028 if (mergeindex == index)
2030 fromindex = index + 1;
2031 continue;
2034 TRACE("Merge with index %i\n", mergeindex);
2036 mrgrgn = DPA_GetPtr(ranges, mergeindex);
2037 if (!mrgrgn) return FALSE;
2039 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2040 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2041 COMCTL32_Free(mrgrgn);
2042 DPA_DeletePtr(ranges, mergeindex);
2043 if (mergeindex < index) index --;
2044 } while(1);
2047 return TRUE;
2050 static BOOL ranges_del(HDPA ranges, RANGE range)
2052 RANGE remrgn, tmprgn, *chkrgn;
2053 BOOL done = FALSE;
2054 INT index;
2056 TRACE("range: (%d - %d)\n", range.lower, range.upper);
2058 remrgn = range;
2061 index = DPA_Search(ranges, &remrgn, 0, ranges_cmp, 0, 0);
2062 if (index == -1) return TRUE;
2064 chkrgn = DPA_GetPtr(ranges, index);
2065 if (!chkrgn) return FALSE;
2067 TRACE("Matches range index %i (%d - %d)\n",
2068 index, chkrgn->lower, chkrgn->upper);
2070 /* case 1: Same range */
2071 if ( (chkrgn->upper == remrgn.upper) &&
2072 (chkrgn->lower == remrgn.lower) )
2074 DPA_DeletePtr(ranges, index);
2075 done = TRUE;
2077 /* case 2: engulf */
2078 else if ( (chkrgn->upper <= remrgn.upper) &&
2079 (chkrgn->lower >= remrgn.lower) )
2081 DPA_DeletePtr(ranges, index);
2083 /* case 3: overlap upper */
2084 else if ( (chkrgn->upper <= remrgn.upper) &&
2085 (chkrgn->lower < remrgn.lower) )
2087 chkrgn->upper = remrgn.lower - 1;
2089 /* case 4: overlap lower */
2090 else if ( (chkrgn->upper > remrgn.upper) &&
2091 (chkrgn->lower >= remrgn.lower) )
2093 chkrgn->lower = remrgn.upper + 1;
2095 /* case 5: fully internal */
2096 else
2098 RANGE *newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2099 if (!newrgn) return FALSE;
2100 tmprgn = *chkrgn;
2101 newrgn->lower = chkrgn->lower;
2102 newrgn->upper = remrgn.lower - 1;
2103 chkrgn->lower = remrgn.upper + 1;
2104 DPA_InsertPtr(ranges, index, newrgn);
2105 chkrgn = &tmprgn;
2108 while(!done);
2110 return TRUE;
2113 /***
2114 * Helper function for LISTVIEW_RemoveSelectionRange, and LISTVIEW_SetItem.
2116 static BOOL remove_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
2118 RANGE range = { lower, upper };
2119 LVITEMW lvItem;
2120 INT i;
2122 if (!ranges_del(infoPtr->hdpaSelectionRanges, range)) return FALSE;
2123 if (adj_sel_only) return TRUE;
2125 /* reset the selection on items */
2126 lvItem.state = 0;
2127 lvItem.stateMask = LVIS_SELECTED;
2128 for(i = lower; i <= upper; i++)
2129 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
2131 return TRUE;
2133 /***
2134 * Helper function for LISTVIEW_AddSelectionRange, and LISTVIEW_SetItem.
2136 static BOOL add_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
2138 RANGE range = { lower, upper };
2139 LVITEMW lvItem;
2140 INT i;
2142 if (!ranges_add(infoPtr->hdpaSelectionRanges, range)) return FALSE;
2143 if (adj_sel_only) return TRUE;
2145 /* set the selection on items */
2146 lvItem.state = LVIS_SELECTED;
2147 lvItem.stateMask = LVIS_SELECTED;
2148 for(i = lower; i <= upper; i++)
2149 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
2151 return TRUE;
2155 * DESCRIPTION:
2156 * Adds a selection range.
2158 * PARAMETER(S):
2159 * [I] infoPtr : valid pointer to the listview structure
2160 * [I] lower : lower item index
2161 * [I] upper : upper item index
2163 * RETURN:
2164 * Success: TRUE
2165 * Failure: FALSE
2167 static inline BOOL LISTVIEW_AddSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2169 return add_selection_range(infoPtr, lower, upper, FALSE);
2172 /***
2173 * DESCRIPTION:
2174 * Removes a range selections.
2176 * PARAMETER(S):
2177 * [I] infoPtr : valid pointer to the listview structure
2178 * [I] lower : lower item index
2179 * [I] upper : upper item index
2181 * RETURN:
2182 * Success: TRUE
2183 * Failure: FALSE
2185 static inline BOOL LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2187 return remove_selection_range(infoPtr, lower, upper, FALSE);
2190 /***
2191 * DESCRIPTION:
2192 * Removes all selection ranges
2194 * Parameters(s):
2195 * [I] infoPtr : valid pointer to the listview structure
2197 * RETURNS:
2198 * SUCCESS : TRUE
2199 * FAILURE : TRUE
2201 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr)
2203 RANGE *sel;
2205 if (infoPtr->bRemovingAllSelections) return TRUE;
2207 infoPtr->bRemovingAllSelections = TRUE;
2209 TRACE("()\n");
2213 sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, 0);
2214 if (sel) LISTVIEW_RemoveSelectionRange(infoPtr, sel->lower, sel->upper);
2216 while (infoPtr->hdpaSelectionRanges->nItemCount > 0);
2218 infoPtr->bRemovingAllSelections = FALSE;
2220 return TRUE;
2223 /***
2224 * DESCRIPTION:
2225 * Retrieves the number of items that are marked as selected.
2227 * PARAMETER(S):
2228 * [I] infoPtr : valid pointer to the listview structure
2230 * RETURN:
2231 * Number of items selected.
2233 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2235 INT i, nSelectedCount = 0;
2237 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2239 INT i;
2240 for (i = 0; i < infoPtr->nItemCount; i++)
2242 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2243 nSelectedCount++;
2246 else
2248 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
2250 RANGE *sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
2251 nSelectedCount += sel->upper - sel->lower + 1;
2255 TRACE("nSelectedCount=%d\n", nSelectedCount);
2256 return nSelectedCount;
2259 /***
2260 * DESCRIPTION:
2261 * Manages the item focus.
2263 * PARAMETER(S):
2264 * [I] infoPtr : valid pointer to the listview structure
2265 * [I] INT : item index
2267 * RETURN:
2268 * TRUE : focused item changed
2269 * FALSE : focused item has NOT changed
2271 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2273 INT oldFocus = infoPtr->nFocusedItem;
2274 LVITEMW lvItem;
2276 lvItem.state = LVIS_FOCUSED;
2277 lvItem.stateMask = LVIS_FOCUSED;
2278 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2280 return oldFocus != infoPtr->nFocusedItem;
2284 * DESCRIPTION:
2285 * Updates the various indices after an item has been inserted or deleted.
2287 * PARAMETER(S):
2288 * [I] infoPtr : valid pointer to the listview structure
2289 * [I] nItem : item index
2290 * [I] direction : Direction of shift, +1 or -1.
2292 * RETURN:
2293 * None
2295 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2297 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2299 ranges_shift(infoPtr->hdpaSelectionRanges, nItem, direction);
2301 /* Note that the following will fail if direction != +1 and -1 */
2302 if (infoPtr->nSelectionMark > nItem)
2303 infoPtr->nSelectionMark += direction;
2304 else if (infoPtr->nSelectionMark == nItem)
2306 if (direction > 0)
2307 infoPtr->nSelectionMark += direction;
2308 else if (infoPtr->nSelectionMark >= infoPtr->nItemCount)
2309 infoPtr->nSelectionMark = infoPtr->nItemCount - 1;
2312 if (infoPtr->nFocusedItem > nItem)
2313 infoPtr->nFocusedItem += direction;
2314 else if (infoPtr->nFocusedItem == nItem)
2316 if (direction > 0)
2317 infoPtr->nFocusedItem += direction;
2318 else
2320 if (infoPtr->nFocusedItem >= infoPtr->nItemCount)
2321 infoPtr->nFocusedItem = infoPtr->nItemCount - 1;
2322 if (infoPtr->nFocusedItem >= 0)
2323 LISTVIEW_SetItemFocus(infoPtr, infoPtr->nFocusedItem);
2326 /* But we are not supposed to modify nHotItem! */
2331 * DESCRIPTION:
2332 * Adds a block of selections.
2334 * PARAMETER(S):
2335 * [I] infoPtr : valid pointer to the listview structure
2336 * [I] INT : item index
2338 * RETURN:
2339 * None
2341 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2343 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2344 INT nLast = max(infoPtr->nSelectionMark, nItem);
2345 INT i;
2346 LVITEMW item;
2348 if (nFirst == -1)
2349 nFirst = nItem;
2351 item.state = LVIS_SELECTED;
2352 item.stateMask = LVIS_SELECTED;
2354 /* FIXME: this is not correct LVS_OWNERDATA
2355 * See docu for LVN_ITEMCHANGED. Is there something similar for
2356 * RemoveGroupSelection (is there such a thing?)?
2358 for (i = nFirst; i <= nLast; i++)
2359 LISTVIEW_SetItemState(infoPtr,i,&item);
2361 LISTVIEW_SetItemFocus(infoPtr, nItem);
2362 infoPtr->nSelectionMark = nItem;
2366 /***
2367 * DESCRIPTION:
2368 * Sets a single group selection.
2370 * PARAMETER(S):
2371 * [I] infoPtr : valid pointer to the listview structure
2372 * [I] INT : item index
2374 * RETURN:
2375 * None
2377 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2379 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2380 INT i;
2381 LVITEMW item;
2382 POINT ptItem;
2383 RECT rcSel;
2385 LISTVIEW_RemoveAllSelections(infoPtr);
2387 item.state = LVIS_SELECTED;
2388 item.stateMask = LVIS_SELECTED;
2390 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2392 INT nFirst, nLast;
2394 if (infoPtr->nSelectionMark == -1)
2395 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2396 else
2398 nFirst = min(infoPtr->nSelectionMark, nItem);
2399 nLast = max(infoPtr->nSelectionMark, nItem);
2401 for (i = nFirst; i <= nLast; i++)
2402 LISTVIEW_SetItemState(infoPtr, i, &item);
2404 else
2406 RECT rcItem, rcSelMark;
2407 ITERATOR i;
2409 rcItem.left = LVIR_BOUNDS;
2410 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2411 rcSelMark.left = LVIR_BOUNDS;
2412 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2413 UnionRect(&rcSel, &rcItem, &rcSelMark);
2414 iterator_frameditems(&i, infoPtr, &rcSel);
2415 while(iterator_next(&i))
2417 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
2418 if (PtInRect(&rcSel, ptItem))
2419 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
2421 iterator_destroy(&i);
2424 LISTVIEW_SetItemFocus(infoPtr, nItem);
2427 /***
2428 * DESCRIPTION:
2429 * Sets a single selection.
2431 * PARAMETER(S):
2432 * [I] infoPtr : valid pointer to the listview structure
2433 * [I] INT : item index
2435 * RETURN:
2436 * None
2438 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2440 LVITEMW lvItem;
2442 TRACE("nItem=%d\n", nItem);
2444 LISTVIEW_RemoveAllSelections(infoPtr);
2446 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2447 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2448 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2450 infoPtr->nSelectionMark = nItem;
2453 /***
2454 * DESCRIPTION:
2455 * Set selection(s) with keyboard.
2457 * PARAMETER(S):
2458 * [I] infoPtr : valid pointer to the listview structure
2459 * [I] INT : item index
2461 * RETURN:
2462 * SUCCESS : TRUE (needs to be repainted)
2463 * FAILURE : FALSE (nothing has changed)
2465 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2467 /* FIXME: pass in the state */
2468 LONG lStyle = infoPtr->dwStyle;
2469 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2470 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2471 BOOL bResult = FALSE;
2473 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2475 if (lStyle & LVS_SINGLESEL)
2477 bResult = TRUE;
2478 LISTVIEW_SetSelection(infoPtr, nItem);
2480 else
2482 if (wShift)
2484 bResult = TRUE;
2485 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2487 else if (wCtrl)
2489 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2491 else
2493 bResult = TRUE;
2494 LISTVIEW_SetSelection(infoPtr, nItem);
2497 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2500 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2501 return bResult;
2505 /***
2506 * DESCRIPTION:
2507 * Called when the mouse is being actively tracked and has hovered for a specified
2508 * amount of time
2510 * PARAMETER(S):
2511 * [I] infoPtr : valid pointer to the listview structure
2512 * [I] fwKeys : key indicator
2513 * [I] pts : mouse position
2515 * RETURN:
2516 * 0 if the message was processed, non-zero if there was an error
2518 * INFO:
2519 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2520 * over the item for a certain period of time.
2523 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2525 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2526 /* FIXME: select the item!!! */
2527 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2529 return 0;
2532 /***
2533 * DESCRIPTION:
2534 * Called whenever WM_MOUSEMOVE is received.
2536 * PARAMETER(S):
2537 * [I] infoPtr : valid pointer to the listview structure
2538 * [I] fwKeys : key indicator
2539 * [I] pts : mouse position
2541 * RETURN:
2542 * 0 if the message is processed, non-zero if there was an error
2544 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2546 TRACKMOUSEEVENT trackinfo;
2548 /* see if we are supposed to be tracking mouse hovering */
2549 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2550 /* fill in the trackinfo struct */
2551 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2552 trackinfo.dwFlags = TME_QUERY;
2553 trackinfo.hwndTrack = infoPtr->hwndSelf;
2554 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2556 /* see if we are already tracking this hwnd */
2557 _TrackMouseEvent(&trackinfo);
2559 if(!(trackinfo.dwFlags & TME_HOVER)) {
2560 trackinfo.dwFlags = TME_HOVER;
2562 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2563 _TrackMouseEvent(&trackinfo);
2567 return 0;
2571 /***
2572 * Tests wheather the item is assignable to a list with style lStyle
2574 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2576 if ( (lpLVItem->mask & LVIF_TEXT) &&
2577 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2578 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2580 return TRUE;
2583 /***
2584 * DESCRIPTION:
2585 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2587 * PARAMETER(S):
2588 * [I] infoPtr : valid pointer to the listview structure
2589 * [I] lpLVItem : valid pointer to new item atttributes
2590 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2592 * RETURN:
2593 * SUCCESS : TRUE
2594 * FAILURE : FALSE
2596 static BOOL set_owner_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2598 LONG lStyle = infoPtr->dwStyle;
2599 NMLISTVIEW nmlv;
2600 INT oldState;
2602 /* a virtual listview stores only the state for the main item */
2603 if (lpLVItem->iSubItem || !(lpLVItem->mask & LVIF_STATE)) return FALSE;
2605 oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, LVIS_FOCUSED | LVIS_SELECTED);
2606 TRACE("oldState=%x, newState=%x, uCallbackMask=%x\n",
2607 oldState, lpLVItem->state, infoPtr->uCallbackMask);
2609 /* we're done if we don't need to change anything we handle */
2610 if ( !((oldState ^ lpLVItem->state) & lpLVItem->stateMask &
2611 ~infoPtr->uCallbackMask & (LVIS_FOCUSED | LVIS_SELECTED))) return TRUE;
2614 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2615 * by LVS_OWERNDATA list controls
2618 /* if we handle the focus, and we're asked to change it, do it now */
2619 if ( lpLVItem->stateMask & LVIS_FOCUSED )
2621 if (lpLVItem->state & LVIS_FOCUSED)
2622 infoPtr->nFocusedItem = lpLVItem->iItem;
2623 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2624 infoPtr->nFocusedItem = -1;
2627 /* and the selection is the only other state a virtual list may hold */
2628 if (lpLVItem->stateMask & LVIS_SELECTED)
2630 if (lpLVItem->state & LVIS_SELECTED)
2632 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2633 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2635 else
2636 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2639 /* notify the parent now that things have changed */
2640 ZeroMemory(&nmlv, sizeof(nmlv));
2641 nmlv.iItem = lpLVItem->iItem;
2642 nmlv.uNewState = lpLVItem->state;
2643 nmlv.uOldState = oldState;
2644 nmlv.uChanged = LVIF_STATE;
2645 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2647 return TRUE;
2650 /***
2651 * DESCRIPTION:
2652 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2654 * PARAMETER(S):
2655 * [I] infoPtr : valid pointer to the listview structure
2656 * [I] lpLVItem : valid pointer to new item atttributes
2657 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2659 * RETURN:
2660 * SUCCESS : TRUE
2661 * FAILURE : FALSE
2663 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2665 LONG lStyle = infoPtr->dwStyle;
2666 UINT uView = lStyle & LVS_TYPEMASK;
2667 HDPA hdpaSubItems;
2668 LISTVIEW_ITEM *lpItem;
2669 NMLISTVIEW nmlv;
2670 UINT uChanged = 0;
2672 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2673 if (!hdpaSubItems && hdpaSubItems != (HDPA)-1) return FALSE;
2675 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2676 if (!lpItem) return FALSE;
2678 /* determine what fields will change */
2679 if ((lpLVItem->mask & LVIF_STATE) &&
2680 ((lpItem->state ^ lpLVItem->state) & lpLVItem->stateMask))
2681 uChanged |= LVIF_STATE;
2683 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2684 uChanged |= LVIF_IMAGE;
2686 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2687 uChanged |= LVIF_PARAM;
2689 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2690 uChanged |= LVIF_INDENT;
2692 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2693 uChanged |= LVIF_TEXT;
2695 if (!uChanged) return TRUE;
2697 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2698 nmlv.iItem = lpLVItem->iItem;
2699 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2700 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2701 nmlv.uChanged = uChanged;
2702 nmlv.lParam = lpItem->lParam;
2704 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2705 if(lpItem->valid && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
2706 return FALSE;
2708 /* copy information */
2709 if (lpLVItem->mask & LVIF_TEXT)
2710 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
2712 if (lpLVItem->mask & LVIF_IMAGE)
2713 lpItem->hdr.iImage = lpLVItem->iImage;
2715 if (lpLVItem->mask & LVIF_PARAM)
2716 lpItem->lParam = lpLVItem->lParam;
2718 if (lpLVItem->mask & LVIF_INDENT)
2719 lpItem->iIndent = lpLVItem->iIndent;
2721 if (uChanged & LVIF_STATE)
2723 lpItem->state &= ~lpLVItem->stateMask;
2724 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2725 if (nmlv.uNewState & LVIS_SELECTED)
2727 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2728 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2730 else if (lpLVItem->stateMask & LVIS_SELECTED)
2731 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2733 /* if we are asked to change focus, and we manage it, do it */
2734 if (nmlv.uNewState & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
2736 if (lpLVItem->state & LVIS_FOCUSED)
2738 infoPtr->nFocusedItem = lpLVItem->iItem;
2739 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
2741 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2742 infoPtr->nFocusedItem = -1;
2746 /* if LVS_LIST or LVS_SMALLICON, update the width of the items */
2747 if((uChanged & LVIF_TEXT) && ((uView == LVS_LIST) || (uView == LVS_SMALLICON)))
2749 int item_width = LISTVIEW_CalculateItemWidth(infoPtr, lpLVItem->iItem);
2750 if(item_width > infoPtr->nItemWidth) infoPtr->nItemWidth = item_width;
2753 /* if we're inserting the item, we're done */
2754 if (!lpItem->valid) return TRUE;
2756 /* send LVN_ITEMCHANGED notification */
2757 nmlv.lParam = lpItem->lParam;
2758 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2760 return TRUE;
2763 /***
2764 * DESCRIPTION:
2765 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2767 * PARAMETER(S):
2768 * [I] infoPtr : valid pointer to the listview structure
2769 * [I] lpLVItem : valid pointer to new subitem atttributes
2770 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2772 * RETURN:
2773 * SUCCESS : TRUE
2774 * FAILURE : FALSE
2776 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2778 HDPA hdpaSubItems;
2779 LISTVIEW_SUBITEM *lpSubItem;
2780 BOOL bModified = FALSE;
2782 /* set subitem only if column is present */
2783 if (Header_GetItemCount(infoPtr->hwndHeader) <= lpLVItem->iSubItem)
2784 return FALSE;
2786 /* First do some sanity checks */
2787 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
2788 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
2790 /* get the subitem structure, and create it if not there */
2791 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2792 if (!hdpaSubItems) return FALSE;
2794 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
2795 if (!lpSubItem)
2797 LISTVIEW_SUBITEM *tmpSubItem;
2798 INT i;
2800 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2801 if (!lpSubItem) return FALSE;
2802 /* we could binary search here, if need be...*/
2803 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2805 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2806 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
2808 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
2810 COMCTL32_Free(lpSubItem);
2811 return FALSE;
2813 lpSubItem->iSubItem = lpLVItem->iSubItem;
2814 bModified = TRUE;
2817 if (lpLVItem->mask & LVIF_IMAGE)
2818 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
2820 lpSubItem->hdr.iImage = lpLVItem->iImage;
2821 bModified = TRUE;
2824 if (lpLVItem->mask & LVIF_TEXT)
2825 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
2827 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
2828 bModified = TRUE;
2831 if (bModified && !infoPtr->bIsDrawing)
2833 RECT rect;
2835 rect.left = LVIR_BOUNDS;
2836 rect.top = lpLVItem->iSubItem;
2837 /* GetSubItemRect will fail in non-report mode, so there's
2838 * gonna be no invalidation then, yay! */
2839 if (LISTVIEW_GetSubItemRect(infoPtr, lpLVItem->iItem, &rect))
2840 LISTVIEW_InvalidateRect(infoPtr, &rect);
2843 return TRUE;
2846 /***
2847 * DESCRIPTION:
2848 * Sets item attributes.
2850 * PARAMETER(S):
2851 * [I] infoPtr : valid pointer to the listview structure
2852 * [I] LPLVITEM : new item atttributes
2853 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2855 * RETURN:
2856 * SUCCESS : TRUE
2857 * FAILURE : FALSE
2859 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2861 INT nOldFocus = infoPtr->nFocusedItem;
2862 LPWSTR pszText = NULL;
2863 BOOL bResult;
2865 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2867 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
2868 return FALSE;
2870 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
2871 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
2873 pszText = lpLVItem->pszText;
2874 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
2877 /* actually set the fields */
2878 if (infoPtr->dwStyle & LVS_OWNERDATA)
2879 bResult = set_owner_item(infoPtr, lpLVItem, TRUE);
2880 else
2882 /* sanity checks first */
2883 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
2885 if (lpLVItem->iSubItem)
2886 bResult = set_sub_item(infoPtr, lpLVItem, TRUE);
2887 else
2888 bResult = set_main_item(infoPtr, lpLVItem, TRUE);
2891 /* redraw item, if necessary */
2892 if (bResult && !infoPtr->bIsDrawing && lpLVItem->iSubItem == 0)
2894 if (nOldFocus != infoPtr->nFocusedItem && infoPtr->bFocus)
2895 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->rcFocus);
2897 /* this little optimization eliminates some nasty flicker */
2898 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
2899 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
2900 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
2902 RECT rect;
2904 rect.left = LVIR_BOUNDS;
2905 rect.top = 0;
2906 if (LISTVIEW_GetSubItemRect(infoPtr, lpLVItem->iItem, &rect))
2907 LISTVIEW_InvalidateRect(infoPtr, &rect);
2909 else
2910 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
2912 /* restore text */
2913 if (pszText)
2915 textfreeT(lpLVItem->pszText, isW);
2916 lpLVItem->pszText = pszText;
2919 return bResult;
2922 /***
2923 * DESCRIPTION:
2924 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2926 * PARAMETER(S):
2927 * [I] infoPtr : valid pointer to the listview structure
2929 * RETURN:
2930 * item index
2932 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
2934 LONG lStyle = infoPtr->dwStyle;
2935 UINT uView = lStyle & LVS_TYPEMASK;
2936 INT nItem = 0;
2937 SCROLLINFO scrollInfo;
2939 scrollInfo.cbSize = sizeof(SCROLLINFO);
2940 scrollInfo.fMask = SIF_POS;
2942 if (uView == LVS_LIST)
2944 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
2945 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
2947 else if (uView == LVS_REPORT)
2949 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2950 nItem = scrollInfo.nPos;
2952 else
2954 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2955 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
2958 TRACE("nItem=%d\n", nItem);
2960 return nItem;
2963 /* helper function for the drawing code */
2964 static void select_text_attr(LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
2966 if ( (lpnmlvcd->clrTextBk != CLR_DEFAULT) && (lpnmlvcd->clrTextBk != CLR_NONE) )
2968 SetBkMode(hdc, OPAQUE);
2969 SetBkColor(hdc, lpnmlvcd->clrTextBk);
2971 else
2972 SetBkMode(hdc, TRANSPARENT);
2974 SetTextColor(hdc, lpnmlvcd->clrText);
2977 /***
2978 * DESCRIPTION:
2979 * Erases the background of the given rectangle
2981 * PARAMETER(S):
2982 * [I] infoPtr : valid pointer to the listview structure
2983 * [I] hdc : device context handle
2984 * [I] lprcBox : clipping rectangle
2986 * RETURN:
2987 * Success: TRUE
2988 * Failure: FALSE
2990 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
2992 if (!infoPtr->hBkBrush) return FALSE;
2994 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
2996 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
2999 /***
3000 * DESCRIPTION:
3001 * Draws a subitem.
3003 * PARAMETER(S):
3004 * [I] infoPtr : valid pointer to the listview structure
3005 * [I] HDC : device context handle
3006 * [I] INT : item index
3007 * [I] INT : subitem index
3008 * [I] RECT * : clipping rectangle
3009 * [I] cdmode : custom draw mode
3011 * RETURN:
3012 * Success: TRUE
3013 * Failure: FALSE
3015 static BOOL LISTVIEW_DrawSubItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem,
3016 INT nSubItem, RECT rcItem, UINT align, DWORD cdmode)
3018 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3019 DWORD cditemmode = CDRF_DODEFAULT;
3020 NMLVCUSTOMDRAW nmlvcd;
3021 LVITEMW lvItem;
3023 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, rcItem=%s)\n",
3024 hdc, nItem, nSubItem, debugrect(&rcItem));
3026 /* get information needed for drawing the item */
3027 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3028 lvItem.iItem = nItem;
3029 lvItem.iSubItem = nSubItem;
3030 lvItem.cchTextMax = DISP_TEXT_SIZE;
3031 lvItem.pszText = szDispText;
3032 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3033 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3035 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcItem, &lvItem);
3036 if (cdmode & CDRF_NOTIFYITEMDRAW)
3037 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3038 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3040 if (lvItem.iImage) FIXME("Draw the image for the subitem\n");
3042 select_text_attr(infoPtr, hdc, &nmlvcd);
3043 DrawTextW(hdc, lvItem.pszText, -1, &rcItem, LV_SL_DT_FLAGS | align);
3045 postpaint:
3046 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3047 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3049 return TRUE;
3053 /***
3054 * DESCRIPTION:
3055 * Draws an item.
3057 * PARAMETER(S):
3058 * [I] infoPtr : valid pointer to the listview structure
3059 * [I] hdc : device context handle
3060 * [I] nItem : item index
3061 * [I] cdmode : custom draw mode
3063 * RETURN:
3064 * Success: TRUE
3065 * Failure: FALSE
3067 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, DWORD cdmode)
3069 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3070 DWORD cditemmode = CDRF_DODEFAULT;
3071 RECT* lprcFocus, rcSelect, rcBox, rcIcon, rcLabel;
3072 NMLVCUSTOMDRAW nmlvcd;
3073 LVITEMW lvItem;
3075 TRACE("(hdc=%x, nItem=%d)\n", hdc, nItem);
3077 /* get information needed for drawing the item */
3078 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3079 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3080 lvItem.iItem = nItem;
3081 lvItem.iSubItem = 0;
3082 lvItem.cchTextMax = DISP_TEXT_SIZE;
3083 lvItem.pszText = szDispText;
3084 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3085 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3087 /* now check if we need to update the focus rectangle */
3088 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3090 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, &rcBox, NULL, &rcIcon, &rcLabel)) return FALSE;
3092 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3093 if (cdmode & CDRF_NOTIFYITEMDRAW)
3094 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3095 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3097 /* state icons */
3098 if (infoPtr->himlState)
3100 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3101 RECT rcState = rcBox;
3103 rcState.left += infoPtr->iconSize.cx * lvItem.iIndent;
3104 if (uStateImage)
3105 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3106 rcState.left, rcState.top, ILD_NORMAL);
3109 /* small icons */
3110 if (infoPtr->himlSmall && lvItem.iImage >= 0)
3112 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3113 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3114 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3117 /* Don't bother painting item being edited */
3118 if (infoPtr->bEditing && lprcFocus) goto postpaint;
3120 select_text_attr(infoPtr, hdc, &nmlvcd);
3122 rcSelect = rcLabel;
3123 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3124 rcSelect.right = rcBox.right;
3126 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3127 if(lprcFocus) *lprcFocus = rcSelect;
3129 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, LV_SL_DT_FLAGS | DT_CENTER);
3131 postpaint:
3132 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3133 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3134 return TRUE;
3137 /***
3138 * DESCRIPTION:
3139 * Draws an item when in large icon display mode.
3141 * PARAMETER(S):
3142 * [I] infoPtr : valid pointer to the listview structure
3143 * [I] hdc : device context handle
3144 * [I] nItem : item index
3145 * [I] cdmode : custom draw mode
3147 * RETURN:
3148 * Success: TRUE
3149 * Failure: FALSE
3151 static BOOL LISTVIEW_DrawLargeItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, DWORD cdmode)
3153 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3154 DWORD cditemmode = CDRF_DODEFAULT;
3155 RECT rcBounds, rcIcon, rcLabel, *lprcFocus;
3156 NMLVCUSTOMDRAW nmlvcd;
3157 LVITEMW lvItem;
3158 UINT uFormat;
3160 TRACE("(hdc=%x, nItem=%d)\n", hdc, nItem);
3162 /* get information needed for drawing the item */
3163 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3164 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3165 lvItem.iItem = nItem;
3166 lvItem.iSubItem = 0;
3167 lvItem.pszText = szDispText;
3168 lvItem.cchTextMax = DISP_TEXT_SIZE;
3169 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3170 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3172 /* now check if we need to update the focus rectangle */
3173 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3175 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, &rcBounds, &rcIcon, &rcLabel)) return FALSE;
3177 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBounds, &lvItem);
3178 if (cdmode & CDRF_NOTIFYITEMDRAW)
3179 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3180 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3182 /* Set the item to the boundary box for now */
3183 TRACE("rcIcon=%s, rcLabel=%s\n", debugrect(&rcIcon), debugrect(&rcLabel));
3185 /* state icons */
3186 if (infoPtr->himlState)
3188 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3189 INT x = rcIcon.left - infoPtr->iconStateSize.cx + 10;
3190 INT y = rcIcon.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
3191 if (uStateImage > 0)
3192 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, x, y, ILD_NORMAL);
3195 /* draw the icon */
3196 if (infoPtr->himlNormal && lvItem.iImage >=0)
3197 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc,
3198 rcIcon.left + ICON_LR_HALF, rcIcon.top + ICON_TOP_PADDING,
3199 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3201 /* Draw the text below the icon */
3203 /* Don't bother painting item being edited */
3204 if ((infoPtr->bEditing && lprcFocus) || !lvItem.pszText || !lstrlenW(lvItem.pszText))
3206 if(lprcFocus) SetRectEmpty(lprcFocus);
3207 goto postpaint;
3210 select_text_attr(infoPtr, hdc, &nmlvcd);
3212 uFormat = lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
3214 /* draw label */
3216 /* I am sure of most of the uFormat values. However I am not sure about
3217 * whether we need or do not need the following:
3218 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3219 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3220 * We certainly do not need
3221 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3222 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3225 if (lvItem.state & LVIS_SELECTED)
3226 ExtTextOutW(hdc, rcLabel.left, rcLabel.top, ETO_OPAQUE, &rcLabel, 0, 0, 0);
3228 DrawTextW (hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3230 if(lprcFocus) *lprcFocus = rcLabel;
3232 postpaint:
3233 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3234 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3236 return TRUE;
3239 /***
3240 * DESCRIPTION:
3241 * Draws listview items when in owner draw mode.
3243 * PARAMETER(S):
3244 * [I] infoPtr : valid pointer to the listview structure
3245 * [I] hdc : device context handle
3247 * RETURN:
3248 * None
3250 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
3252 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3253 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3254 POINT Origin, Position;
3255 DRAWITEMSTRUCT dis;
3256 LVITEMW item;
3257 ITERATOR i;
3259 TRACE("()\n");
3261 ZeroMemory(&dis, sizeof(dis));
3263 /* Get scroll info once before loop */
3264 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3266 /* figure out what we need to draw */
3267 iterator_clippeditems(&i, infoPtr, hdc);
3269 /* send cache hint notification */
3270 if (infoPtr->dwStyle & LVS_OWNERDATA)
3272 RANGE range = iterator_range(&i);
3273 NMLVCACHEHINT nmlv;
3275 nmlv.iFrom = range.lower;
3276 nmlv.iTo = range.upper;
3277 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3280 /* iterate through the invalidated rows */
3281 while(iterator_next(&i))
3283 item.iItem = i.nItem;
3284 item.iSubItem = 0;
3285 item.mask = LVIF_PARAM | LVIF_STATE;
3286 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3287 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3289 dis.CtlType = ODT_LISTVIEW;
3290 dis.CtlID = uID;
3291 dis.itemID = item.iItem;
3292 dis.itemAction = ODA_DRAWENTIRE;
3293 dis.itemState = 0;
3294 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3295 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3296 dis.hwndItem = infoPtr->hwndSelf;
3297 dis.hDC = hdc;
3298 if (!LISTVIEW_GetItemListOrigin(infoPtr, dis.itemID, &Position)) continue;
3299 dis.rcItem.left = Position.x + Origin.x;
3300 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3301 dis.rcItem.top = Position.y + Origin.y;
3302 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3303 dis.itemData = item.lParam;
3305 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3306 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3308 iterator_destroy(&i);
3311 /***
3312 * DESCRIPTION:
3313 * Draws listview items when in report display mode.
3315 * PARAMETER(S):
3316 * [I] infoPtr : valid pointer to the listview structure
3317 * [I] hdc : device context handle
3318 * [I] cdmode : custom draw mode
3320 * RETURN:
3321 * None
3323 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3325 INT rgntype, nDrawPosY, j;
3326 INT nColumnCount, nFirstCol, nLastCol;
3327 RECT rcItem, rcClip;
3328 COLUMNCACHE *lpCols;
3329 LVCOLUMNW lvColumn;
3330 POINT ptOrig;
3331 ITERATOR i;
3333 TRACE("()\n");
3335 /* figure out what to draw */
3336 rgntype = GetClipBox(hdc, &rcClip);
3337 if (rgntype == NULLREGION) return;
3339 /* cache column info */
3340 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3341 lpCols = COMCTL32_Alloc(nColumnCount * sizeof(COLUMNCACHE));
3342 if (!lpCols) return;
3343 for (j = 0; j < nColumnCount; j++)
3345 Header_GetItemRect(infoPtr->hwndHeader, j, &lpCols[j].rc);
3346 TRACE("lpCols[%d].rc=%s\n", j, debugrect(&lpCols[j].rc));
3349 /* Get scroll info once before loop */
3350 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrig)) return;
3352 /* we now narrow the columns as well */
3353 nLastCol = nColumnCount - 1;
3354 for(nFirstCol = 0; nFirstCol < nColumnCount; nFirstCol++)
3355 if (lpCols[nFirstCol].rc.right + ptOrig.x >= rcClip.left) break;
3356 for(nLastCol = nColumnCount - 1; nLastCol >= 0; nLastCol--)
3357 if (lpCols[nLastCol].rc.left + ptOrig.x < rcClip.right) break;
3359 /* cache the per-column information before we start drawing */
3360 for (j = nFirstCol; j <= nLastCol; j++)
3362 lvColumn.mask = LVCF_FMT;
3363 LISTVIEW_GetColumnT(infoPtr, j, &lvColumn, TRUE);
3364 TRACE("lvColumn=%s\n", debuglvcolumn_t(&lvColumn, TRUE));
3365 lpCols[j].align = DT_LEFT;
3366 if (lvColumn.fmt & LVCFMT_RIGHT)
3367 lpCols[j].align = DT_RIGHT;
3368 else if (lvColumn.fmt & LVCFMT_CENTER)
3369 lpCols[j].align = DT_CENTER;
3372 /* figure out what we need to draw */
3373 iterator_clippeditems(&i, infoPtr, hdc);
3375 /* a last few bits before we start drawing */
3376 TRACE("Colums=(%di - %d)\n", nFirstCol, nLastCol);
3378 /* iterate through the invalidated rows */
3379 while(iterator_next(&i))
3381 nDrawPosY = i.nItem * infoPtr->nItemHeight;
3383 /* iterate through the invalidated columns */
3384 for (j = nFirstCol; j <= nLastCol; j++)
3386 rcItem = lpCols[j].rc;
3387 rcItem.left += REPORT_MARGINX;
3388 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3389 rcItem.top = nDrawPosY;
3390 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3392 /* Offset the Scroll Bar Pos */
3393 OffsetRect(&rcItem, ptOrig.x, ptOrig.y);
3395 if (rgntype == COMPLEXREGION && !RectVisible(hdc, &rcItem)) continue;
3397 if (j == 0)
3398 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, cdmode);
3399 else
3400 LISTVIEW_DrawSubItem(infoPtr, hdc, i.nItem, j, rcItem, lpCols[j].align, cdmode);
3403 iterator_destroy(&i);
3405 /* cleanup the mess */
3406 COMCTL32_Free(lpCols);
3409 /***
3410 * DESCRIPTION:
3411 * Draws listview items when in list display mode.
3413 * PARAMETER(S):
3414 * [I] infoPtr : valid pointer to the listview structure
3415 * [I] hdc : device context handle
3416 * [I] cdmode : custom draw mode
3418 * RETURN:
3419 * None
3421 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3423 POINT Origin, Position;
3424 ITERATOR i;
3426 /* Get scroll info once before loop */
3427 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3429 /* figure out what we need to draw */
3430 iterator_clippeditems(&i, infoPtr, hdc);
3432 while(iterator_next(&i))
3434 if (!LISTVIEW_GetItemListOrigin(infoPtr, i.nItem, &Position)) continue;
3436 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, cdmode);
3438 iterator_destroy(&i);
3441 /***
3442 * DESCRIPTION:
3443 * Draws listview items when in icon or small icon display mode.
3445 * PARAMETER(S):
3446 * [I] infoPtr : valid pointer to the listview structure
3447 * [I] hdc : device context handle
3448 * [I] cdmode : custom draw mode
3450 * RETURN:
3451 * None
3453 static void LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3455 POINT Origin, Position;
3456 RECT rcBox;
3457 ITERATOR i;
3459 /* Get scroll info once before loop */
3460 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3462 /* figure out what we need to draw */
3463 iterator_clippeditems(&i, infoPtr, hdc);
3465 while(iterator_next(&i))
3467 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_FOCUSED))
3468 continue;
3470 if (!LISTVIEW_GetItemListOrigin(infoPtr, i.nItem, &Position)) continue;
3471 LISTVIEW_DrawLargeItem(infoPtr, hdc, i.nItem, cdmode);
3473 iterator_destroy(&i);
3475 /* draw the focused item last, in case it's oversized */
3476 if (LISTVIEW_GetItemMeasures(infoPtr, infoPtr->nFocusedItem, &rcBox, 0, 0, 0) &&
3477 RectVisible(hdc, &rcBox))
3478 LISTVIEW_DrawLargeItem(infoPtr, hdc, infoPtr->nFocusedItem, cdmode);
3481 /***
3482 * DESCRIPTION:
3483 * Draws listview items.
3485 * PARAMETER(S):
3486 * [I] infoPtr : valid pointer to the listview structure
3487 * [I] HDC : device context handle
3489 * RETURN:
3490 * NoneX
3492 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3494 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3495 COLORREF oldBkColor, oldTextColor;
3496 NMLVCUSTOMDRAW nmlvcd;
3497 HFONT hOldFont;
3498 DWORD cdmode;
3499 INT oldBkMode;
3500 RECT rcClient;
3502 LISTVIEW_DUMP(infoPtr);
3504 infoPtr->bIsDrawing = TRUE;
3506 /* save dc values we're gonna trash while drawing */
3507 hOldFont = SelectObject(hdc, infoPtr->hFont);
3508 oldBkMode = GetBkMode(hdc);
3509 oldBkColor = GetBkColor(hdc);
3510 oldTextColor = GetTextColor(hdc);
3512 GetClientRect(infoPtr->hwndSelf, &rcClient);
3513 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, NULL);
3514 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3515 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3517 /* nothing to draw */
3518 if(infoPtr->nItemCount == 0) goto enddraw;
3520 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
3521 LISTVIEW_RefreshOwnerDraw(infoPtr, hdc);
3522 else if (uView == LVS_ICON)
3523 LISTVIEW_RefreshIcon(infoPtr, hdc, cdmode);
3524 else if (uView == LVS_REPORT)
3525 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3526 else /* LVS_LIST or LVS_SMALLICON */
3527 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3529 /* if we have a focus rect, draw it */
3530 if (infoPtr->bFocus && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
3531 DrawFocusRect(hdc, &infoPtr->rcFocus);
3533 enddraw:
3534 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3535 notify_customdraw(infoPtr, CDDS_POSTPAINT, &nmlvcd);
3537 /* unselect objects */
3538 SelectObject(hdc, hOldFont);
3539 SetBkMode(hdc, oldBkMode);
3540 SetBkColor(hdc, oldBkColor);
3541 SetTextColor(hdc, oldTextColor);
3542 infoPtr->bIsDrawing = FALSE;
3546 /***
3547 * DESCRIPTION:
3548 * Calculates the approximate width and height of a given number of items.
3550 * PARAMETER(S):
3551 * [I] infoPtr : valid pointer to the listview structure
3552 * [I] INT : number of items
3553 * [I] INT : width
3554 * [I] INT : height
3556 * RETURN:
3557 * Returns a DWORD. The width in the low word and the height in high word.
3559 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3560 WORD wWidth, WORD wHeight)
3562 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3563 INT nItemCountPerColumn = 1;
3564 INT nColumnCount = 0;
3565 DWORD dwViewRect = 0;
3567 if (nItemCount == -1)
3568 nItemCount = infoPtr->nItemCount;
3570 if (uView == LVS_LIST)
3572 if (wHeight == 0xFFFF)
3574 /* use current height */
3575 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3578 if (wHeight < infoPtr->nItemHeight)
3579 wHeight = infoPtr->nItemHeight;
3581 if (nItemCount > 0)
3583 if (infoPtr->nItemHeight > 0)
3585 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3586 if (nItemCountPerColumn == 0)
3587 nItemCountPerColumn = 1;
3589 if (nItemCount % nItemCountPerColumn != 0)
3590 nColumnCount = nItemCount / nItemCountPerColumn;
3591 else
3592 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3596 /* Microsoft padding magic */
3597 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3598 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3600 dwViewRect = MAKELONG(wWidth, wHeight);
3602 else if (uView == LVS_REPORT)
3603 FIXME("uView == LVS_REPORT: not implemented\n");
3604 else if (uView == LVS_SMALLICON)
3605 FIXME("uView == LVS_SMALLICON: not implemented\n");
3606 else if (uView == LVS_ICON)
3607 FIXME("uView == LVS_ICON: not implemented\n");
3609 return dwViewRect;
3612 /***
3613 * DESCRIPTION:
3614 * Arranges listview items in icon display mode.
3616 * PARAMETER(S):
3617 * [I] infoPtr : valid pointer to the listview structure
3618 * [I] INT : alignment code
3620 * RETURN:
3621 * SUCCESS : TRUE
3622 * FAILURE : FALSE
3624 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3626 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3627 BOOL bResult = FALSE;
3629 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3631 switch (nAlignCode)
3633 case LVA_ALIGNLEFT:
3634 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3635 break;
3636 case LVA_ALIGNTOP:
3637 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3638 break;
3639 case LVA_DEFAULT:
3640 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3641 break;
3642 case LVA_SNAPTOGRID:
3643 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3644 break;
3648 return bResult;
3651 /* << LISTVIEW_CreateDragImage >> */
3654 /***
3655 * DESCRIPTION:
3656 * Removes all listview items and subitems.
3658 * PARAMETER(S):
3659 * [I] infoPtr : valid pointer to the listview structure
3661 * RETURN:
3662 * SUCCESS : TRUE
3663 * FAILURE : FALSE
3665 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3667 LONG lStyle = infoPtr->dwStyle;
3668 UINT uView = lStyle & LVS_TYPEMASK;
3669 LISTVIEW_ITEM *lpItem;
3670 LISTVIEW_SUBITEM *lpSubItem;
3671 NMLISTVIEW nmlv;
3672 BOOL bSuppress;
3673 BOOL bResult = FALSE;
3674 HDPA hdpaSubItems;
3676 TRACE("()\n");
3678 LISTVIEW_RemoveAllSelections(infoPtr);
3679 infoPtr->nSelectionMark=-1;
3680 infoPtr->nFocusedItem=-1;
3681 SetRectEmpty(&infoPtr->rcFocus);
3682 /* But we are supposed to leave nHotItem as is! */
3684 if (lStyle & LVS_OWNERDATA)
3686 infoPtr->nItemCount = 0;
3687 LISTVIEW_InvalidateList(infoPtr);
3688 return TRUE;
3691 if (infoPtr->nItemCount > 0)
3693 INT i, j;
3695 /* send LVN_DELETEALLITEMS notification */
3696 /* verify if subsequent LVN_DELETEITEM notifications should be
3697 suppressed */
3698 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3699 nmlv.iItem = -1;
3700 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3702 for (i = 0; i < infoPtr->nItemCount; i++)
3704 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3705 if (hdpaSubItems != NULL)
3707 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3709 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3710 if (lpSubItem != NULL)
3712 /* free subitem string */
3713 if (is_textW(lpSubItem->hdr.pszText))
3714 COMCTL32_Free(lpSubItem->hdr.pszText);
3716 /* free subitem */
3717 COMCTL32_Free(lpSubItem);
3721 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3722 if (lpItem != NULL)
3724 if (!bSuppress)
3726 /* send LVN_DELETEITEM notification */
3727 nmlv.iItem = i;
3728 nmlv.lParam = lpItem->lParam;
3729 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3732 /* free item string */
3733 if (is_textW(lpItem->hdr.pszText))
3734 COMCTL32_Free(lpItem->hdr.pszText);
3736 /* free item */
3737 COMCTL32_Free(lpItem);
3740 DPA_Destroy(hdpaSubItems);
3744 /* reinitialize listview memory */
3745 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3746 infoPtr->nItemCount = 0;
3747 DPA_DeleteAllPtrs(infoPtr->hdpaPosX);
3748 DPA_DeleteAllPtrs(infoPtr->hdpaPosY);
3750 /* align items (set position of each item) */
3751 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3753 if (lStyle & LVS_ALIGNLEFT)
3755 LISTVIEW_AlignLeft(infoPtr);
3757 else
3759 LISTVIEW_AlignTop(infoPtr);
3763 LISTVIEW_UpdateScroll(infoPtr);
3765 LISTVIEW_InvalidateList(infoPtr);
3768 return bResult;
3771 /***
3772 * DESCRIPTION:
3773 * Removes a column from the listview control.
3775 * PARAMETER(S):
3776 * [I] infoPtr : valid pointer to the listview structure
3777 * [I] INT : column index
3779 * RETURN:
3780 * SUCCESS : TRUE
3781 * FAILURE : FALSE
3783 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3785 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3786 RECT rcCol, rcOld;
3788 TRACE("nColumn=%d\n", nColumn);
3790 if (nColumn <= 0) return FALSE;
3792 if (uView == LVS_REPORT)
3794 if (!Header_GetItemRect(infoPtr->hwndHeader, nColumn, &rcCol))
3795 return FALSE;
3797 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3798 return FALSE;
3801 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3803 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3804 HDPA hdpaSubItems;
3805 INT nItem, nSubItem, i;
3807 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3809 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3810 if (!hdpaSubItems) continue;
3811 nSubItem = 0;
3812 lpDelItem = 0;
3813 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3815 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3816 if (!lpSubItem) break;
3817 if (lpSubItem->iSubItem == nColumn)
3819 nSubItem = i;
3820 lpDelItem = lpSubItem;
3822 else if (lpSubItem->iSubItem > nColumn)
3824 lpSubItem->iSubItem--;
3828 /* if we found our subitem, zapp it */
3829 if (nSubItem > 0)
3831 /* free string */
3832 if (is_textW(lpDelItem->hdr.pszText))
3833 COMCTL32_Free(lpDelItem->hdr.pszText);
3835 /* free item */
3836 COMCTL32_Free(lpDelItem);
3838 /* free dpa memory */
3839 DPA_DeletePtr(hdpaSubItems, nSubItem);
3844 /* we need to worry about display issues in report mode only */
3845 if (uView != LVS_REPORT) return TRUE;
3847 /* if we have a focus, must first erase the focus rect */
3848 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
3850 /* Need to reset the item width when deleting a column */
3851 infoPtr->nItemWidth -= rcCol.right - rcCol.left;
3853 /* update scrollbar(s) */
3854 LISTVIEW_UpdateScroll(infoPtr);
3856 /* scroll to cover the deleted column, and invalidate for redraw */
3857 rcOld = infoPtr->rcList;
3858 rcOld.left = rcCol.left;
3859 ScrollWindowEx(infoPtr->hwndSelf, -(rcCol.right - rcCol.left), 0,
3860 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3862 /* we can restore focus now */
3863 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
3865 return TRUE;
3868 /***
3869 * DESCRIPTION:
3870 * Removes an item from the listview control.
3872 * PARAMETER(S):
3873 * [I] infoPtr : valid pointer to the listview structure
3874 * [I] INT : item index
3876 * RETURN:
3877 * SUCCESS : TRUE
3878 * FAILURE : FALSE
3880 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
3882 LONG lStyle = infoPtr->dwStyle;
3883 UINT uView = lStyle & LVS_TYPEMASK;
3884 LONG lCtrlId = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3885 NMLISTVIEW nmlv;
3886 BOOL bResult = FALSE;
3887 HDPA hdpaSubItems;
3888 LISTVIEW_ITEM *lpItem;
3889 LISTVIEW_SUBITEM *lpSubItem;
3890 INT i;
3891 LVITEMW item;
3893 TRACE("(nItem=%d)\n", nItem);
3896 /* First, send LVN_DELETEITEM notification. */
3897 memset(&nmlv, 0, sizeof (NMLISTVIEW));
3898 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
3899 nmlv.hdr.idFrom = lCtrlId;
3900 nmlv.hdr.code = LVN_DELETEITEM;
3901 nmlv.iItem = nItem;
3902 SendMessageW((infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)lCtrlId,
3903 (LPARAM)&nmlv);
3905 if (nItem == infoPtr->nFocusedItem)
3907 infoPtr->nFocusedItem = -1;
3908 SetRectEmpty(&infoPtr->rcFocus);
3911 /* remove it from the selection range */
3912 item.state = LVIS_SELECTED;
3913 item.stateMask = LVIS_SELECTED;
3914 LISTVIEW_SetItemState(infoPtr,nItem,&item);
3916 if (lStyle & LVS_OWNERDATA)
3918 infoPtr->nItemCount--;
3919 LISTVIEW_InvalidateList(infoPtr); /*FIXME: optimize */
3920 return TRUE;
3923 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3925 /* initialize memory */
3926 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3928 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
3929 if (hdpaSubItems != NULL)
3931 infoPtr->nItemCount--;
3932 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3934 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3935 if (lpSubItem != NULL)
3937 /* free item string */
3938 if (is_textW(lpSubItem->hdr.pszText))
3939 COMCTL32_Free(lpSubItem->hdr.pszText);
3941 /* free item */
3942 COMCTL32_Free(lpSubItem);
3946 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3947 if (lpItem != NULL)
3949 /* free item string */
3950 if (is_textW(lpItem->hdr.pszText))
3951 COMCTL32_Free(lpItem->hdr.pszText);
3953 /* free item */
3954 COMCTL32_Free(lpItem);
3957 bResult = DPA_Destroy(hdpaSubItems);
3958 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
3959 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
3962 LISTVIEW_ShiftIndices(infoPtr,nItem,-1);
3964 /* align items (set position of each item) */
3965 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
3967 if (lStyle & LVS_ALIGNLEFT)
3968 LISTVIEW_AlignLeft(infoPtr);
3969 else
3970 LISTVIEW_AlignTop(infoPtr);
3973 LISTVIEW_UpdateScroll(infoPtr);
3975 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
3978 return bResult;
3982 /***
3983 * DESCRIPTION:
3984 * Callback implementation for editlabel control
3986 * PARAMETER(S):
3987 * [I] infoPtr : valid pointer to the listview structure
3988 * [I] pszText : modified text
3989 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
3991 * RETURN:
3992 * SUCCESS : TRUE
3993 * FAILURE : FALSE
3995 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
3997 NMLVDISPINFOW dispInfo;
3999 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4001 infoPtr->bEditing = FALSE;
4003 ZeroMemory(&dispInfo, sizeof(dispInfo));
4004 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4005 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4006 dispInfo.item.iSubItem = 0;
4007 dispInfo.item.stateMask = ~0;
4008 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4009 dispInfo.item.pszText = pszText;
4010 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4012 /* Do we need to update the Item Text */
4013 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4014 if (!pszText) return TRUE;
4016 ZeroMemory(&dispInfo, sizeof(dispInfo));
4017 dispInfo.item.mask = LVIF_TEXT;
4018 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4019 dispInfo.item.iSubItem = 0;
4020 dispInfo.item.pszText = pszText;
4021 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4022 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4025 /***
4026 * DESCRIPTION:
4027 * Begin in place editing of specified list view item
4029 * PARAMETER(S):
4030 * [I] infoPtr : valid pointer to the listview structure
4031 * [I] INT : item index
4032 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4034 * RETURN:
4035 * SUCCESS : TRUE
4036 * FAILURE : FALSE
4038 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4040 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4041 NMLVDISPINFOW dispInfo;
4042 RECT rect;
4044 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4046 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4048 infoPtr->nEditLabelItem = nItem;
4050 /* Is the EditBox still there, if so remove it */
4051 if(infoPtr->hwndEdit != 0)
4053 SetFocus(infoPtr->hwndSelf);
4054 infoPtr->hwndEdit = 0;
4057 LISTVIEW_SetSelection(infoPtr, nItem);
4058 LISTVIEW_SetItemFocus(infoPtr, nItem);
4060 rect.left = LVIR_LABEL;
4061 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4063 ZeroMemory(&dispInfo, sizeof(dispInfo));
4064 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4065 dispInfo.item.iItem = nItem;
4066 dispInfo.item.iSubItem = 0;
4067 dispInfo.item.stateMask = ~0;
4068 dispInfo.item.pszText = szDispText;
4069 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4070 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4072 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4073 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4074 if (!infoPtr->hwndEdit) return 0;
4076 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4078 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4079 infoPtr->hwndEdit = 0;
4080 return 0;
4083 infoPtr->bEditing = TRUE;
4084 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4085 SetFocus(infoPtr->hwndEdit);
4086 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4087 return infoPtr->hwndEdit;
4091 /***
4092 * DESCRIPTION:
4093 * Ensures the specified item is visible, scrolling into view if necessary.
4095 * PARAMETER(S):
4096 * [I] infoPtr : valid pointer to the listview structure
4097 * [I] nItem : item index
4098 * [I] bPartial : partially or entirely visible
4100 * RETURN:
4101 * SUCCESS : TRUE
4102 * FAILURE : FALSE
4104 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4106 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4107 INT nScrollPosHeight = 0;
4108 INT nScrollPosWidth = 0;
4109 INT nHorzAdjust = 0;
4110 INT nVertAdjust = 0;
4111 INT nHorzDiff = 0;
4112 INT nVertDiff = 0;
4113 RECT rcItem;
4115 /* FIXME: ALWAYS bPartial == FALSE, FOR NOW! */
4117 rcItem.left = LVIR_BOUNDS;
4118 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4120 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4122 /* scroll left/right, but in LVS_REPORT mode */
4123 if (uView == LVS_LIST)
4124 nScrollPosWidth = infoPtr->nItemWidth;
4125 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4126 nScrollPosWidth = 1;
4128 if (rcItem.left < infoPtr->rcList.left)
4130 nHorzAdjust = -1;
4131 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4133 else
4135 nHorzAdjust = 1;
4136 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4140 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4142 /* scroll up/down, but not in LVS_LIST mode */
4143 if (uView == LVS_REPORT)
4144 nScrollPosHeight = infoPtr->nItemHeight;
4145 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4146 nScrollPosHeight = 1;
4148 if (rcItem.top < infoPtr->rcList.top)
4150 nVertAdjust = -1;
4151 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4153 else
4155 nVertAdjust = 1;
4156 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4160 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4162 if (nScrollPosWidth)
4164 INT diff = nHorzDiff / nScrollPosWidth;
4165 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4166 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4169 if (nScrollPosHeight)
4171 INT diff = nVertDiff / nScrollPosHeight;
4172 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4173 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4176 return TRUE;
4179 /***
4180 * DESCRIPTION:
4181 * Searches for an item with specific characteristics.
4183 * PARAMETER(S):
4184 * [I] hwnd : window handle
4185 * [I] nStart : base item index
4186 * [I] lpFindInfo : item information to look for
4188 * RETURN:
4189 * SUCCESS : index of item
4190 * FAILURE : -1
4192 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4193 LPLVFINDINFOW lpFindInfo)
4195 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4196 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4197 BOOL bWrap = FALSE, bNearest = FALSE;
4198 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4199 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4200 POINT Position, Destination;
4201 LVITEMW lvItem;
4203 if (!lpFindInfo || nItem < 0) return -1;
4205 lvItem.mask = 0;
4206 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4208 lvItem.mask |= LVIF_TEXT;
4209 lvItem.pszText = szDispText;
4210 lvItem.cchTextMax = DISP_TEXT_SIZE;
4213 if (lpFindInfo->flags & LVFI_WRAP)
4214 bWrap = TRUE;
4216 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4217 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4219 POINT Origin;
4221 FIXME("LVFI_NEARESTXY is slow.\n");
4222 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
4223 Destination.x = lpFindInfo->pt.x - Origin.x;
4224 Destination.y = lpFindInfo->pt.y - Origin.y;
4225 switch(lpFindInfo->vkDirection)
4227 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4228 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4229 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4230 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4231 case VK_HOME: Destination.x = Destination.y = 0; break;
4232 case VK_END: Destination.x = infoPtr->rcView.right; Destination.y = infoPtr->rcView.bottom; break;
4233 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4234 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4235 default: FIXME("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4237 bNearest = TRUE;
4240 /* if LVFI_PARAM is specified, all other flags are ignored */
4241 if (lpFindInfo->flags & LVFI_PARAM)
4243 lvItem.mask |= LVIF_PARAM;
4244 bNearest = FALSE;
4245 lvItem.mask &= ~LVIF_TEXT;
4248 again:
4249 for (; nItem < nLast; nItem++)
4251 lvItem.iItem = nItem;
4252 lvItem.iSubItem = 0;
4253 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4255 if (lvItem.mask & LVIF_PARAM && lpFindInfo->lParam == lvItem.lParam)
4256 return nItem;
4258 if (lvItem.mask & LVIF_TEXT)
4260 if (lpFindInfo->flags & LVFI_PARTIAL)
4262 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4264 else
4266 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4270 if (!bNearest) return nItem;
4272 /* This is very inefficient. To do a good job here,
4273 * we need a sorted array of (x,y) item positions */
4274 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) continue;
4276 /* compute the distance^2 to the destination */
4277 xdist = Destination.x - Position.x;
4278 ydist = Destination.y - Position.y;
4279 dist = xdist * xdist + ydist * ydist;
4281 /* remember the distance, and item if it's closer */
4282 if (dist < mindist)
4284 mindist = dist;
4285 nNearestItem = nItem;
4289 if (bWrap)
4291 nItem = 0;
4292 nLast = min(nStart + 1, infoPtr->nItemCount);
4293 bWrap = FALSE;
4294 goto again;
4297 return nNearestItem;
4300 /***
4301 * DESCRIPTION:
4302 * Searches for an item with specific characteristics.
4304 * PARAMETER(S):
4305 * [I] hwnd : window handle
4306 * [I] nStart : base item index
4307 * [I] lpFindInfo : item information to look for
4309 * RETURN:
4310 * SUCCESS : index of item
4311 * FAILURE : -1
4313 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4314 LPLVFINDINFOA lpFindInfo)
4316 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4317 LVFINDINFOW fiw;
4318 LRESULT res;
4320 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4321 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4322 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4323 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4324 return res;
4327 /***
4328 * DESCRIPTION:
4329 * Retrieves the background image of the listview control.
4331 * PARAMETER(S):
4332 * [I] infoPtr : valid pointer to the listview structure
4333 * [O] LPLVMKBIMAGE : background image attributes
4335 * RETURN:
4336 * SUCCESS : TRUE
4337 * FAILURE : FALSE
4339 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4340 /* { */
4341 /* FIXME (listview, "empty stub!\n"); */
4342 /* return FALSE; */
4343 /* } */
4345 /***
4346 * DESCRIPTION:
4347 * Retrieves column attributes.
4349 * PARAMETER(S):
4350 * [I] infoPtr : valid pointer to the listview structure
4351 * [I] INT : column index
4352 * [IO] LPLVCOLUMNW : column information
4353 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4354 * otherwise it is in fact a LPLVCOLUMNA
4356 * RETURN:
4357 * SUCCESS : TRUE
4358 * FAILURE : FALSE
4360 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4362 HDITEMW hdi;
4363 BOOL bResult = FALSE;
4365 if (lpColumn != NULL)
4368 /* initialize memory */
4369 ZeroMemory(&hdi, sizeof(hdi));
4371 if (lpColumn->mask & LVCF_FMT)
4372 hdi.mask |= HDI_FORMAT;
4374 if (lpColumn->mask & LVCF_WIDTH)
4375 hdi.mask |= HDI_WIDTH;
4377 if (lpColumn->mask & LVCF_TEXT)
4379 hdi.mask |= HDI_TEXT;
4380 hdi.cchTextMax = lpColumn->cchTextMax;
4381 hdi.pszText = lpColumn->pszText;
4384 if (lpColumn->mask & LVCF_IMAGE)
4385 hdi.mask |= HDI_IMAGE;
4387 if (lpColumn->mask & LVCF_ORDER)
4388 hdi.mask |= HDI_ORDER;
4390 if (isW)
4391 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4392 else
4393 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4395 if (bResult)
4397 if (lpColumn->mask & LVCF_FMT)
4399 lpColumn->fmt = 0;
4401 if (hdi.fmt & HDF_LEFT)
4402 lpColumn->fmt |= LVCFMT_LEFT;
4403 else if (hdi.fmt & HDF_RIGHT)
4404 lpColumn->fmt |= LVCFMT_RIGHT;
4405 else if (hdi.fmt & HDF_CENTER)
4406 lpColumn->fmt |= LVCFMT_CENTER;
4408 if (hdi.fmt & HDF_IMAGE)
4409 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4411 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4412 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4415 if (lpColumn->mask & LVCF_WIDTH)
4416 lpColumn->cx = hdi.cxy;
4418 if (lpColumn->mask & LVCF_IMAGE)
4419 lpColumn->iImage = hdi.iImage;
4421 if (lpColumn->mask & LVCF_ORDER)
4422 lpColumn->iOrder = hdi.iOrder;
4424 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4425 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4430 return bResult;
4434 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4436 INT i;
4438 if (!lpiArray)
4439 return FALSE;
4441 /* FIXME: little hack */
4442 for (i = 0; i < iCount; i++)
4443 lpiArray[i] = i;
4445 return TRUE;
4448 /***
4449 * DESCRIPTION:
4450 * Retrieves the column width.
4452 * PARAMETER(S):
4453 * [I] infoPtr : valid pointer to the listview structure
4454 * [I] int : column index
4456 * RETURN:
4457 * SUCCESS : column width
4458 * FAILURE : zero
4460 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4462 INT nColumnWidth = 0;
4463 HDITEMW hdi;
4465 TRACE("nColumn=%d\n", nColumn);
4467 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4469 case LVS_LIST:
4470 nColumnWidth = infoPtr->nItemWidth;
4471 break;
4472 case LVS_REPORT:
4473 hdi.mask = HDI_WIDTH;
4474 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4475 nColumnWidth = hdi.cxy;
4476 break;
4477 default:
4478 /* we don't have a 'column' in [SMALL]ICON mode */
4481 TRACE("nColumnWidth=%d\n", nColumnWidth);
4482 return nColumnWidth;
4485 /***
4486 * DESCRIPTION:
4487 * In list or report display mode, retrieves the number of items that can fit
4488 * vertically in the visible area. In icon or small icon display mode,
4489 * retrieves the total number of visible items.
4491 * PARAMETER(S):
4492 * [I] infoPtr : valid pointer to the listview structure
4494 * RETURN:
4495 * Number of fully visible items.
4497 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4499 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4500 INT nItemCount = 0;
4502 if (uView == LVS_LIST)
4504 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4506 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4507 LISTVIEW_GetCountPerColumn(infoPtr);
4510 else if (uView == LVS_REPORT)
4512 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4514 else
4516 nItemCount = infoPtr->nItemCount;
4519 return nItemCount;
4523 /***
4524 * DESCRIPTION:
4525 * Retrieves an image list handle.
4527 * PARAMETER(S):
4528 * [I] infoPtr : valid pointer to the listview structure
4529 * [I] nImageList : image list identifier
4531 * RETURN:
4532 * SUCCESS : image list handle
4533 * FAILURE : NULL
4535 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4537 HIMAGELIST himl = NULL;
4539 switch (nImageList)
4541 case LVSIL_NORMAL:
4542 himl = infoPtr->himlNormal;
4543 break;
4544 case LVSIL_SMALL:
4545 himl = infoPtr->himlSmall;
4546 break;
4547 case LVSIL_STATE:
4548 himl = infoPtr->himlState;
4549 break;
4552 return (LRESULT)himl;
4555 /* LISTVIEW_GetISearchString */
4557 /***
4558 * DESCRIPTION:
4559 * Retrieves item attributes.
4561 * PARAMETER(S):
4562 * [I] hwnd : window handle
4563 * [IO] lpLVItem : item info
4564 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4565 * if FALSE, the lpLVItem is a LPLVITEMA.
4567 * NOTE:
4568 * This is the internal 'GetItem' interface -- it tries to
4569 * be smart, and avoids text copies, if possible, by modifing
4570 * lpLVItem->pszText to point to the text string. Please note
4571 * that this is not always possible (e.g. OWNERDATA), so on
4572 * entry you *must* supply valid values for pszText, and cchTextMax.
4573 * The only difference to the documented interface is that upon
4574 * return, you should use *only* the lpLVItem->pszText, rather than
4575 * the buffer pointer you provided on input. Most code already does
4576 * that, so it's not a problem.
4577 * For the two cases when the text must be copied (that is,
4578 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4580 * RETURN:
4581 * SUCCESS : TRUE
4582 * FAILURE : FALSE
4584 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4586 NMLVDISPINFOW dispInfo;
4587 LISTVIEW_ITEM *lpItem;
4588 ITEMHDR* pItemHdr;
4589 HDPA hdpaSubItems;
4591 /* In the following:
4592 * lpLVItem describes the information requested by the user
4593 * lpItem is what we have
4594 * dispInfo is a structure we use to request the missing
4595 * information from the application
4598 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4600 if (!lpLVItem || (lpLVItem->iItem < 0) ||
4601 (lpLVItem->iItem >= infoPtr->nItemCount))
4602 return FALSE;
4604 /* a quick optimization if all we're asked is the focus state
4605 * these queries are worth optimising since they are common,
4606 * and can be answered in constant time, without the heavy accesses */
4607 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIF_STATE) &&
4608 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4610 lpLVItem->state = 0;
4611 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4612 lpLVItem->state |= LVIS_FOCUSED;
4613 return TRUE;
4616 ZeroMemory(&dispInfo, sizeof(dispInfo));
4618 /* if the app stores all the data, handle it separately */
4619 if (infoPtr->dwStyle & LVS_OWNERDATA)
4621 dispInfo.item.state = 0;
4623 /* if we need to callback, do it now */
4624 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4626 /* NOTE: copy only fields which we _know_ are initialized, some apps
4627 * depend on the uninitialized fields being 0 */
4628 dispInfo.item.mask = lpLVItem->mask;
4629 dispInfo.item.iItem = lpLVItem->iItem;
4630 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4631 if (lpLVItem->mask & LVIF_TEXT)
4633 dispInfo.item.pszText = lpLVItem->pszText;
4634 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4636 if (lpLVItem->mask & LVIF_STATE)
4637 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4638 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4639 dispInfo.item.stateMask = lpLVItem->stateMask;
4640 *lpLVItem = dispInfo.item;
4641 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4644 /* we store only a little state, so if we're not asked, we're done */
4645 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4647 /* if focus is handled by us, report it */
4648 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4650 lpLVItem->state &= ~LVIS_FOCUSED;
4651 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4652 lpLVItem->state |= LVIS_FOCUSED;
4655 /* and do the same for selection, if we handle it */
4656 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4658 lpLVItem->state &= ~LVIS_SELECTED;
4659 if (ranges_contain(infoPtr->hdpaSelectionRanges, lpLVItem->iItem))
4660 lpLVItem->state |= LVIS_SELECTED;
4663 return TRUE;
4666 /* find the item and subitem structures before we proceed */
4667 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4668 if (hdpaSubItems == NULL) return FALSE;
4670 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4671 return FALSE;
4673 if (lpLVItem->iSubItem)
4675 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4676 if(!lpSubItem) return FALSE;
4677 pItemHdr = &lpSubItem->hdr;
4679 else
4680 pItemHdr = &lpItem->hdr;
4682 /* Do we need to query the state from the app? */
4683 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4685 dispInfo.item.mask |= LVIF_STATE;
4686 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4689 /* Do we need to enquire about the image? */
4690 if ((lpLVItem->mask & LVIF_IMAGE) && (pItemHdr->iImage==I_IMAGECALLBACK))
4691 dispInfo.item.mask |= LVIF_IMAGE;
4693 /* Do we need to enquire about the text? */
4694 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4696 dispInfo.item.mask |= LVIF_TEXT;
4697 dispInfo.item.pszText = lpLVItem->pszText;
4698 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4699 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4700 *dispInfo.item.pszText = '\0';
4703 /* If we don't have all the requested info, query the application */
4704 if (dispInfo.item.mask != 0)
4706 dispInfo.item.iItem = lpLVItem->iItem;
4707 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4708 dispInfo.item.lParam = lpItem->lParam;
4709 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4710 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4713 /* Now, handle the iImage field */
4714 if (dispInfo.item.mask & LVIF_IMAGE)
4716 lpLVItem->iImage = dispInfo.item.iImage;
4717 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (pItemHdr->iImage==I_IMAGECALLBACK))
4718 pItemHdr->iImage = dispInfo.item.iImage;
4720 else if (lpLVItem->mask & LVIF_IMAGE)
4721 lpLVItem->iImage = pItemHdr->iImage;
4723 /* The pszText field */
4724 if (dispInfo.item.mask & LVIF_TEXT)
4726 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4727 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4729 lpLVItem->pszText = dispInfo.item.pszText;
4731 else if (lpLVItem->mask & LVIF_TEXT)
4733 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4734 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4737 /* if this is a subitem, we're done*/
4738 if (lpLVItem->iSubItem) return TRUE;
4740 /* Next is the lParam field */
4741 if (dispInfo.item.mask & LVIF_PARAM)
4743 lpLVItem->lParam = dispInfo.item.lParam;
4744 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4745 lpItem->lParam = dispInfo.item.lParam;
4747 else if (lpLVItem->mask & LVIF_PARAM)
4748 lpLVItem->lParam = lpItem->lParam;
4750 /* ... the state field (this one is different due to uCallbackmask) */
4751 if (lpLVItem->mask & LVIF_STATE)
4753 lpLVItem->state = lpItem->state;
4754 if (dispInfo.item.mask & LVIF_STATE)
4756 lpLVItem->state &= ~dispInfo.item.stateMask;
4757 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4759 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4761 lpLVItem->state &= ~LVIS_FOCUSED;
4762 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4763 lpLVItem->state |= LVIS_FOCUSED;
4765 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4767 lpLVItem->state &= ~LVIS_SELECTED;
4768 if (ranges_contain(infoPtr->hdpaSelectionRanges, lpLVItem->iItem))
4769 lpLVItem->state |= LVIS_SELECTED;
4773 /* and last, but not least, the indent field */
4774 if (lpLVItem->mask & LVIF_INDENT)
4775 lpLVItem->iIndent = lpItem->iIndent;
4777 return TRUE;
4780 /***
4781 * DESCRIPTION:
4782 * Retrieves item attributes.
4784 * PARAMETER(S):
4785 * [I] hwnd : window handle
4786 * [IO] lpLVItem : item info
4787 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4788 * if FALSE, the lpLVItem is a LPLVITEMA.
4790 * NOTE:
4791 * This is the external 'GetItem' interface -- it properly copies
4792 * the text in the provided buffer.
4794 * RETURN:
4795 * SUCCESS : TRUE
4796 * FAILURE : FALSE
4798 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4800 LPWSTR pszText;
4801 BOOL bResult;
4803 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4804 return FALSE;
4806 pszText = lpLVItem->pszText;
4807 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4808 if (bResult && lpLVItem->pszText != pszText)
4809 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4810 lpLVItem->pszText = pszText;
4812 return bResult;
4816 /***
4817 * DESCRIPTION:
4818 * Retrieves the position (upper-left) of the listview control item.
4819 * Note that for LVS_ICON style, the upper-left is that of the icon
4820 * and not the bounding box.
4822 * PARAMETER(S):
4823 * [I] infoPtr : valid pointer to the listview structure
4824 * [I] nItem : item index
4825 * [O] lpptPosition : coordinate information
4827 * RETURN:
4828 * SUCCESS : TRUE
4829 * FAILURE : FALSE
4831 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4833 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4834 POINT Origin;
4836 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4838 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4839 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, lpptPosition)) return FALSE;
4840 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
4842 if (uView == LVS_ICON)
4844 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
4845 lpptPosition->y += ICON_TOP_PADDING;
4847 lpptPosition->x += Origin.x;
4848 lpptPosition->y += Origin.y;
4850 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
4851 return TRUE;
4855 /***
4856 * DESCRIPTION:
4857 * Retrieves the bounding rectangle for a listview control item.
4859 * PARAMETER(S):
4860 * [I] infoPtr : valid pointer to the listview structure
4861 * [I] nItem : item index
4862 * [IO] lprc : bounding rectangle coordinates
4863 * lprc->left specifies the portion of the item for which the bounding
4864 * rectangle will be retrieved.
4866 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
4867 * including the icon and label.
4869 * * For LVS_ICON
4870 * * Experiment shows that native control returns:
4871 * * width = min (48, length of text line)
4872 * * .left = position.x - (width - iconsize.cx)/2
4873 * * .right = .left + width
4874 * * height = #lines of text * ntmHeight + icon height + 8
4875 * * .top = position.y - 2
4876 * * .bottom = .top + height
4877 * * separation between items .y = itemSpacing.cy - height
4878 * * .x = itemSpacing.cx - width
4879 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
4881 * * For LVS_ICON
4882 * * Experiment shows that native control returns:
4883 * * width = iconSize.cx + 16
4884 * * .left = position.x - (width - iconsize.cx)/2
4885 * * .right = .left + width
4886 * * height = iconSize.cy + 4
4887 * * .top = position.y - 2
4888 * * .bottom = .top + height
4889 * * separation between items .y = itemSpacing.cy - height
4890 * * .x = itemSpacing.cx - width
4891 * LVIR_LABEL Returns the bounding rectangle of the item text.
4893 * * For LVS_ICON
4894 * * Experiment shows that native control returns:
4895 * * width = text length
4896 * * .left = position.x - width/2
4897 * * .right = .left + width
4898 * * height = ntmH * linecount + 2
4899 * * .top = position.y + iconSize.cy + 6
4900 * * .bottom = .top + height
4901 * * separation between items .y = itemSpacing.cy - height
4902 * * .x = itemSpacing.cx - width
4903 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
4904 * rectangles, but excludes columns in report view.
4906 * RETURN:
4907 * SUCCESS : TRUE
4908 * FAILURE : FALSE
4910 * NOTES
4911 * Note that the bounding rectangle of the label in the LVS_ICON view depends
4912 * upon whether the window has the focus currently and on whether the item
4913 * is the one with the focus. Ensure that the control's record of which
4914 * item has the focus agrees with the items' records.
4916 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
4918 RECT label_rect;
4920 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
4922 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4924 switch(lprc->left)
4926 case LVIR_ICON:
4927 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, lprc, NULL)) return FALSE;
4928 break;
4930 case LVIR_LABEL:
4931 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, NULL, lprc)) return FALSE;
4932 break;
4934 case LVIR_BOUNDS:
4935 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, lprc, NULL, NULL)) return FALSE;
4936 break;
4938 case LVIR_SELECTBOUNDS:
4939 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, lprc, NULL, &label_rect)) return FALSE;
4940 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
4941 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) )
4942 lprc->right = label_rect.right;
4943 break;
4945 default:
4946 WARN("Unknown value: %d\n", lprc->left);
4947 return FALSE;
4950 TRACE(" rect=%s\n", debugrect(lprc));
4952 return TRUE;
4955 /***
4956 * DESCRIPTION:
4957 * Retrieves the spacing between listview control items.
4959 * PARAMETER(S):
4960 * [I] infoPtr : valid pointer to the listview structure
4961 * [IO] lprc : rectangle to receive the output
4962 * on input, lprc->top = nSubItem
4963 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
4965 * NOTE: this call is succeeds only for REPORT style listviews.
4966 * Because we can calculate things much faster in report mode,
4967 * we're gonna do the calculations inline here, instead of
4968 * calling functions that do heavy lifting.
4970 * RETURN:
4971 * TRUE: success
4972 * FALSE: failure
4974 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
4976 POINT ptPosition;
4977 INT nSubItem, flags;
4979 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
4981 nSubItem = lprc->top;
4982 flags = lprc->left;
4984 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, nSubItem);
4986 if (!Header_GetItemRect(infoPtr->hwndHeader, nSubItem, lprc)) return FALSE;
4987 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptPosition)) return FALSE;
4988 lprc->top = ptPosition.y;
4989 lprc->bottom = lprc->top + infoPtr->nItemHeight;
4991 switch(flags)
4993 case LVIR_ICON:
4994 FIXME("Unimplemented LVIR_ICON\n");
4995 return FALSE;
4996 case LVIR_LABEL:
4997 case LVIR_BOUNDS:
4998 /* nothing to do here, we're done */
4999 break;
5000 default:
5001 ERR("Unknown bounds=%d\n", lprc->left);
5002 return FALSE;
5004 return TRUE;
5008 /***
5009 * DESCRIPTION:
5010 * Retrieves the width of a label.
5012 * PARAMETER(S):
5013 * [I] infoPtr : valid pointer to the listview structure
5015 * RETURN:
5016 * SUCCESS : string width (in pixels)
5017 * FAILURE : zero
5019 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5021 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5022 LVITEMW lvItem;
5024 TRACE("(nItem=%d)\n", nItem);
5026 lvItem.mask = LVIF_TEXT;
5027 lvItem.iItem = nItem;
5028 lvItem.iSubItem = 0;
5029 lvItem.pszText = szDispText;
5030 lvItem.cchTextMax = DISP_TEXT_SIZE;
5031 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5033 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5036 /***
5037 * DESCRIPTION:
5038 * Retrieves the spacing between listview control items.
5040 * PARAMETER(S):
5041 * [I] infoPtr : valid pointer to the listview structure
5042 * [I] BOOL : flag for small or large icon
5044 * RETURN:
5045 * Horizontal + vertical spacing
5047 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5049 LONG lResult;
5051 if (!bSmall)
5053 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5055 else
5057 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5058 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5059 else
5060 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5062 return lResult;
5065 /***
5066 * DESCRIPTION:
5067 * Retrieves the state of a listview control item.
5069 * PARAMETER(S):
5070 * [I] infoPtr : valid pointer to the listview structure
5071 * [I] nItem : item index
5072 * [I] uMask : state mask
5074 * RETURN:
5075 * State specified by the mask.
5077 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5079 LVITEMW lvItem;
5081 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5083 lvItem.iItem = nItem;
5084 lvItem.iSubItem = 0;
5085 lvItem.mask = LVIF_STATE;
5086 lvItem.stateMask = uMask;
5087 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5089 return lvItem.state & uMask;
5092 /***
5093 * DESCRIPTION:
5094 * Retrieves the text of a listview control item or subitem.
5096 * PARAMETER(S):
5097 * [I] hwnd : window handle
5098 * [I] nItem : item index
5099 * [IO] lpLVItem : item information
5100 * [I] isW : TRUE if lpLVItem is Unicode
5102 * RETURN:
5103 * SUCCESS : string length
5104 * FAILURE : 0
5106 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5108 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5110 lpLVItem->mask = LVIF_TEXT;
5111 lpLVItem->iItem = nItem;
5112 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5114 return textlenT(lpLVItem->pszText, isW);
5117 /***
5118 * DESCRIPTION:
5119 * Searches for an item based on properties + relationships.
5121 * PARAMETER(S):
5122 * [I] infoPtr : valid pointer to the listview structure
5123 * [I] nItem : item index
5124 * [I] uFlags : relationship flag
5126 * FIXME:
5127 * This function is very, very inefficient! Needs work.
5129 * RETURN:
5130 * SUCCESS : item index
5131 * FAILURE : -1
5133 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5135 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5136 UINT uMask = 0;
5137 LVFINDINFOW lvFindInfo;
5138 INT nCountPerColumn;
5139 INT i;
5141 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5142 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5144 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5146 if (uFlags & LVNI_CUT)
5147 uMask |= LVIS_CUT;
5149 if (uFlags & LVNI_DROPHILITED)
5150 uMask |= LVIS_DROPHILITED;
5152 if (uFlags & LVNI_FOCUSED)
5153 uMask |= LVIS_FOCUSED;
5155 if (uFlags & LVNI_SELECTED)
5156 uMask |= LVIS_SELECTED;
5158 /* if we're asked for the focused item, that's only one,
5159 * so it's worth optimizing */
5160 if (uFlags & LVNI_FOCUSED)
5162 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5163 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5166 if (uFlags & LVNI_ABOVE)
5168 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5170 while (nItem >= 0)
5172 nItem--;
5173 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5174 return nItem;
5177 else
5179 lvFindInfo.flags = LVFI_NEARESTXY;
5180 lvFindInfo.vkDirection = VK_UP;
5181 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5182 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5184 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5185 return nItem;
5189 else if (uFlags & LVNI_BELOW)
5191 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5193 while (nItem < infoPtr->nItemCount)
5195 nItem++;
5196 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5197 return nItem;
5200 else
5202 lvFindInfo.flags = LVFI_NEARESTXY;
5203 lvFindInfo.vkDirection = VK_DOWN;
5204 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5205 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5207 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5208 return nItem;
5212 else if (uFlags & LVNI_TOLEFT)
5214 if (uView == LVS_LIST)
5216 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5217 while (nItem - nCountPerColumn >= 0)
5219 nItem -= nCountPerColumn;
5220 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5221 return nItem;
5224 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5226 lvFindInfo.flags = LVFI_NEARESTXY;
5227 lvFindInfo.vkDirection = VK_LEFT;
5228 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5229 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5231 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5232 return nItem;
5236 else if (uFlags & LVNI_TORIGHT)
5238 if (uView == LVS_LIST)
5240 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5241 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5243 nItem += nCountPerColumn;
5244 if ((ListView_GetItemState(infoPtr->hwndSelf, 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_RIGHT;
5252 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5253 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5255 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5256 return nItem;
5260 else
5262 nItem++;
5264 /* search by index */
5265 for (i = nItem; i < infoPtr->nItemCount; i++)
5267 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5268 return i;
5272 return -1;
5275 /* LISTVIEW_GetNumberOfWorkAreas */
5277 /***
5278 * DESCRIPTION:
5279 * Retrieves the origin coordinates when in icon or small icon display mode.
5281 * PARAMETER(S):
5282 * [I] infoPtr : valid pointer to the listview structure
5283 * [O] lpptOrigin : coordinate information
5285 * RETURN:
5286 * SUCCESS : TRUE
5287 * FAILURE : FALSE
5289 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5291 DWORD lStyle = infoPtr->dwStyle;
5292 UINT uView = lStyle & LVS_TYPEMASK;
5293 INT nHorzPos = 0, nVertPos = 0;
5294 SCROLLINFO scrollInfo;
5296 if (!lpptOrigin) return FALSE;
5298 scrollInfo.cbSize = sizeof(SCROLLINFO);
5299 scrollInfo.fMask = SIF_POS;
5301 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5302 nHorzPos = scrollInfo.nPos;
5303 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5304 nVertPos = scrollInfo.nPos;
5306 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5308 lpptOrigin->x = infoPtr->rcList.left;
5309 lpptOrigin->y = infoPtr->rcList.top;
5310 if (uView == LVS_LIST)
5311 nHorzPos *= infoPtr->nItemWidth;
5312 else if (uView == LVS_REPORT)
5313 nVertPos *= infoPtr->nItemHeight;
5315 lpptOrigin->x -= nHorzPos;
5316 lpptOrigin->y -= nVertPos;
5318 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5320 return TRUE;
5323 /***
5324 * DESCRIPTION:
5325 * Retrieves the width of a string.
5327 * PARAMETER(S):
5328 * [I] hwnd : window handle
5329 * [I] lpszText : text string to process
5330 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5332 * RETURN:
5333 * SUCCESS : string width (in pixels)
5334 * FAILURE : zero
5336 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5338 SIZE stringSize;
5340 stringSize.cx = 0;
5341 if (is_textT(lpszText, isW))
5343 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5344 HDC hdc = GetDC(infoPtr->hwndSelf);
5345 HFONT hOldFont = SelectObject(hdc, hFont);
5347 if (isW)
5348 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5349 else
5350 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5351 SelectObject(hdc, hOldFont);
5352 ReleaseDC(infoPtr->hwndSelf, hdc);
5354 return stringSize.cx;
5357 /***
5358 * DESCRIPTION:
5359 * Determines which listview item is located at the specified position.
5361 * PARAMETER(S):
5362 * [I] infoPtr : valid pointer to the listview structure
5363 * [IO] lpht : hit test information
5364 * [I] subitem : fill out iSubItem.
5365 * [I] select : return the index only if the hit selects the item
5367 * NOTE:
5368 * (mm 20001022): We must not allow iSubItem to be touched, for
5369 * an app might pass only a structure with space up to iItem!
5370 * (MS Office 97 does that for instance in the file open dialog)
5372 * RETURN:
5373 * SUCCESS : item index
5374 * FAILURE : -1
5376 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5378 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5379 RECT rcBounds, rcIcon, rcLabel;
5381 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5383 lpht->flags = 0;
5384 lpht->iItem = -1;
5385 if (subitem) lpht->iSubItem = 0;
5387 if (infoPtr->rcList.left > lpht->pt.x)
5388 lpht->flags |= LVHT_TOLEFT;
5389 else if (infoPtr->rcList.right < lpht->pt.x)
5390 lpht->flags |= LVHT_TORIGHT;
5392 if (infoPtr->rcList.top > lpht->pt.y)
5393 lpht->flags |= LVHT_ABOVE;
5394 else if (infoPtr->rcList.bottom < lpht->pt.y)
5395 lpht->flags |= LVHT_BELOW;
5397 if (lpht->flags) return -1;
5399 lpht->flags |= LVHT_NOWHERE;
5401 /* first deal with the large items */
5402 if (uView == LVS_ICON && (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
5403 PtInRect (&infoPtr->rcFocus, lpht->pt))
5405 lpht->iItem = infoPtr->nFocusedItem;
5407 else
5409 if (uView == LVS_ICON || uView == LVS_SMALLICON)
5411 RECT rcSearch;
5412 ITERATOR i;
5414 rcSearch.left = lpht->pt.x - infoPtr->nItemWidth;
5415 rcSearch.top = lpht->pt.y - infoPtr->nItemHeight;
5416 rcSearch.right = lpht->pt.x + 1;
5417 rcSearch.bottom = lpht->pt.y + 1;
5419 iterator_frameditems(&i, infoPtr, &rcSearch);
5420 while(iterator_next(&i))
5422 if (!LISTVIEW_GetItemMeasures(infoPtr, i.nItem, &rcBounds, 0, 0, 0)) continue;
5423 if (PtInRect(&rcBounds, lpht->pt)) break;
5425 lpht->iItem = i.nItem;
5426 iterator_destroy(&i);
5428 else
5430 POINT Origin, Position;
5431 INT nPerCol = (uView == LVS_REPORT) ? infoPtr->nItemCount : LISTVIEW_GetCountPerColumn(infoPtr);
5433 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
5434 Position.x = lpht->pt.x - Origin.x;
5435 Position.y = lpht->pt.y - Origin.y;
5436 TRACE("Position=%s, nPerCol=%d, nItemHeight=%d, nColHeight=%d\n",
5437 debugpoint(&Position), nPerCol, infoPtr->nItemHeight, nPerCol * infoPtr->nItemHeight);
5439 if (Position.y < nPerCol * infoPtr->nItemHeight)
5441 lpht->iItem = (Position.x / infoPtr->nItemWidth) * nPerCol + (Position.y / infoPtr->nItemHeight);
5442 TRACE("iItem=%d\n", lpht->iItem);
5443 if (lpht->iItem < 0 || lpht->iItem >= infoPtr->nItemCount) lpht->iItem = -1;
5448 if (lpht->iItem == -1) return -1;
5450 if (!LISTVIEW_GetItemMeasures(infoPtr, lpht->iItem, 0, &rcBounds, &rcIcon, &rcLabel)) return -1;
5452 if (!PtInRect(&rcBounds, lpht->pt)) return -1;
5454 if (PtInRect(&rcIcon, lpht->pt))
5455 lpht->flags |= LVHT_ONITEMICON;
5456 else if (PtInRect(&rcLabel, lpht->pt))
5457 lpht->flags |= LVHT_ONITEMLABEL;
5458 else if (infoPtr->himlState)
5460 /* FIXME: move this to GetItemMeasures */
5461 LVITEMW lvItem;
5463 lvItem.mask = LVIF_STATE;
5464 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5465 lvItem.iItem = lpht->iItem;
5466 lvItem.iSubItem = 0;
5467 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5469 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
5470 RECT rcState;
5472 if (uView == LVS_ICON)
5474 rcState.left = rcIcon.left - infoPtr->iconStateSize.cx + 10;
5475 rcState.top = rcIcon.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
5477 else
5479 rcState.left = rcIcon.left - infoPtr->iconStateSize.cx - IMAGE_PADDING;
5480 rcState.top = rcIcon.top;
5482 rcState.right = rcState.left + infoPtr->iconStateSize.cx;
5483 rcState.bottom = rcState.top + infoPtr->iconStateSize.cy;
5485 if (uStateImage > 0 && PtInRect(&rcState, lpht->pt))
5486 lpht->flags |= LVHT_ONITEMSTATEICON;
5489 if (lpht->flags & LVHT_ONITEM)
5490 lpht->flags &= ~LVHT_NOWHERE;
5492 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5494 INT j, nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5495 rcBounds.right = rcBounds.left;
5496 for (j = 0; j < nColumnCount; j++)
5498 rcBounds.left = rcBounds.right;
5499 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5500 if (PtInRect(&rcBounds, lpht->pt))
5502 lpht->iSubItem = j;
5503 break;
5508 if (!select || lpht->iItem == -1) return lpht->iItem;
5510 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5512 return lpht->flags & (LVHT_ONITEMICON | LVHT_ONITEMLABEL) ? lpht->iItem : -1;
5516 /***
5517 * DESCRIPTION:
5518 * Inserts a new column.
5520 * PARAMETER(S):
5521 * [I] infoPtr : valid pointer to the listview structure
5522 * [I] INT : column index
5523 * [I] LPLVCOLUMNW : column information
5525 * RETURN:
5526 * SUCCESS : new column index
5527 * FAILURE : -1
5529 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5530 LPLVCOLUMNW lpColumn, BOOL isW)
5532 RECT rcOld, rcCol;
5533 INT nNewColumn;
5534 HDITEMW hdi;
5536 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5538 if (!lpColumn) return -1;
5540 hdi.mask = hdi.fmt = 0;
5541 if (lpColumn->mask & LVCF_FMT)
5543 /* format member is valid */
5544 hdi.mask |= HDI_FORMAT;
5546 /* set text alignment (leftmost column must be left-aligned) */
5547 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5548 hdi.fmt |= HDF_LEFT;
5549 else if (lpColumn->fmt & LVCFMT_RIGHT)
5550 hdi.fmt |= HDF_RIGHT;
5551 else if (lpColumn->fmt & LVCFMT_CENTER)
5552 hdi.fmt |= HDF_CENTER;
5554 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5555 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5557 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5559 hdi.fmt |= HDF_IMAGE;
5560 hdi.iImage = I_IMAGECALLBACK;
5563 if (lpColumn->fmt & LVCFMT_IMAGE)
5564 ; /* FIXME: enable images for *(sub)items* this column */
5567 if (lpColumn->mask & LVCF_WIDTH)
5569 hdi.mask |= HDI_WIDTH;
5570 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5572 /* make it fill the remainder of the controls width */
5573 HDITEMW hdit;
5574 RECT rcHeader;
5575 INT item_index;
5577 /* get the width of every item except the current one */
5578 hdit.mask = HDI_WIDTH;
5579 hdi.cxy = 0;
5581 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5582 if (Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit)))
5583 hdi.cxy += hdit.cxy;
5585 /* retrieve the layout of the header */
5586 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5587 TRACE("start cxy=%d rcHeader=%s\n", hdi.cxy, debugrect(&rcHeader));
5589 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
5591 else
5592 hdi.cxy = lpColumn->cx;
5595 if (lpColumn->mask & LVCF_TEXT)
5597 hdi.mask |= HDI_TEXT | HDI_FORMAT;
5598 hdi.fmt |= HDF_STRING;
5599 hdi.pszText = lpColumn->pszText;
5600 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
5603 if (lpColumn->mask & LVCF_IMAGE)
5605 hdi.mask |= HDI_IMAGE;
5606 hdi.iImage = lpColumn->iImage;
5609 if (lpColumn->mask & LVCF_ORDER)
5611 hdi.mask |= HDI_ORDER;
5612 hdi.iOrder = lpColumn->iOrder;
5615 /* insert item in header control */
5616 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5617 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5618 (WPARAM)nColumn, (LPARAM)&hdi);
5619 if (nNewColumn == -1) return -1;
5620 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &rcCol)) return -1;
5622 /* now we have to actually adjust the data */
5623 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
5625 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
5626 HDPA hdpaSubItems;
5627 INT nItem, i;
5629 /* preallocate memory, so we can fail gracefully */
5630 if (nNewColumn == 0)
5632 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->nItemCount);
5633 if (!lpNewItems) return -1;
5634 for (i = 0; i < infoPtr->nItemCount; i++)
5635 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
5636 if (i != infoPtr->nItemCount)
5638 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5639 COMCTL32_Free(lpNewItems);
5640 return -1;
5644 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5646 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5647 if (!hdpaSubItems) continue;
5648 for (i = 1; i < hdpaSubItems->nItemCount; i++)
5650 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
5651 if (!lpSubItem) break;
5652 if (lpSubItem->iSubItem >= nNewColumn)
5653 lpSubItem->iSubItem++;
5656 /* if we found our subitem, zapp it */
5657 if (nNewColumn == 0)
5659 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
5660 lpSubItem = lpNewItems[nItem];
5661 lpSubItem->hdr = lpMainItem->hdr;
5662 lpSubItem->iSubItem = 1;
5663 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
5664 lpMainItem->iSubItem = 0;
5665 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
5669 COMCTL32_Free(lpNewItems);
5672 /* we don't have to worry abiut display issues in non-report mode */
5673 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return nNewColumn;
5675 /* if we have a focus, must first erase the focus rect */
5676 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
5678 /* Need to reset the item width when inserting a new column */
5679 infoPtr->nItemWidth += rcCol.right - rcCol.left;
5681 LISTVIEW_UpdateScroll(infoPtr);
5683 /* scroll to cover the deleted column, and invalidate for redraw */
5684 rcOld = infoPtr->rcList;
5685 rcOld.left = rcCol.left;
5686 ScrollWindowEx(infoPtr->hwndSelf, rcCol.right - rcCol.left, 0,
5687 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5689 /* we can restore focus now */
5690 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
5692 return nNewColumn;
5695 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5696 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5697 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5698 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5699 their own sort proc. when sending LVM_SORTITEMS.
5701 /* Platform SDK:
5702 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5704 LVS_SORTXXX must be specified,
5705 LVS_OWNERDRAW is not set,
5706 <item>.pszText is not LPSTR_TEXTCALLBACK.
5708 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5709 are sorted based on item text..."
5711 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5713 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5714 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5715 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5717 /* if we're sorting descending, negate the return value */
5718 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5721 /***
5722 * nESCRIPTION:
5723 * Inserts a new item in the listview control.
5725 * PARAMETER(S):
5726 * [I] infoPtr : valid pointer to the listview structure
5727 * [I] lpLVItem : item information
5728 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5730 * RETURN:
5731 * SUCCESS : new item index
5732 * FAILURE : -1
5734 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5736 LONG lStyle = infoPtr->dwStyle;
5737 UINT uView = lStyle & LVS_TYPEMASK;
5738 INT nItem = -1;
5739 HDPA hdpaSubItems;
5740 NMLISTVIEW nmlv;
5741 LISTVIEW_ITEM *lpItem;
5742 BOOL is_sorted;
5744 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5746 if (lStyle & LVS_OWNERDATA)
5748 nItem = infoPtr->nItemCount;
5749 infoPtr->nItemCount++;
5750 return nItem;
5753 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5754 if (!lpLVItem || lpLVItem->iSubItem) return -1;
5756 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5758 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5759 return -1;
5761 /* insert item in listview control data structure */
5762 if ( (hdpaSubItems = DPA_Create(8)) )
5763 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5764 if (nItem == -1) goto fail;
5766 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5767 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5769 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
5770 is_sorted ? infoPtr->nItemCount + 1 : lpLVItem->iItem,
5771 hdpaSubItems );
5772 if (nItem == -1) goto fail;
5773 infoPtr->nItemCount++;
5775 if (!LISTVIEW_SetItemT(infoPtr, lpLVItem, isW))
5776 goto undo;
5778 /* if we're sorted, sort the list, and update the index */
5779 if (is_sorted)
5781 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5782 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5783 if (nItem == -1)
5785 ERR("We can't find the item we just inserted, possible memory corruption.");
5786 /* we can't remove it from the list if we can't find it, so just fail */
5787 /* we don't deallocate memory here, as it will probably cause more problems */
5788 return -1;
5792 /* make room for the position, if we are in the right mode */
5793 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5795 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5796 goto undo;
5797 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5799 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5800 goto undo;
5804 /* Add the subitem list to the items array. Do this last in case we go to
5805 * fail during the above.
5807 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5809 lpItem->valid = TRUE;
5811 /* send LVN_INSERTITEM notification */
5812 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5813 nmlv.iItem = nItem;
5814 nmlv.lParam = lpItem->lParam;
5815 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5817 /* align items (set position of each item) */
5818 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5820 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
5821 else LISTVIEW_AlignTop(infoPtr);
5824 LISTVIEW_UpdateScroll(infoPtr);
5826 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
5828 TRACE(" <- %d\n", nItem);
5829 return nItem;
5831 undo:
5832 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5833 infoPtr->nItemCount--;
5834 fail:
5835 DPA_DeletePtr(hdpaSubItems, 0);
5836 DPA_Destroy (hdpaSubItems);
5837 COMCTL32_Free (lpItem);
5838 return -1;
5841 /***
5842 * DESCRIPTION:
5843 * Redraws a range of items.
5845 * PARAMETER(S):
5846 * [I] infoPtr : valid pointer to the listview structure
5847 * [I] INT : first item
5848 * [I] INT : last item
5850 * RETURN:
5851 * SUCCESS : TRUE
5852 * FAILURE : FALSE
5854 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
5856 INT i;
5858 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
5859 max(nFirst, nLast) >= infoPtr->nItemCount)
5860 return FALSE;
5862 for (i = nFirst; i <= nLast; i++)
5863 LISTVIEW_InvalidateItem(infoPtr, i);
5865 return TRUE;
5868 /***
5869 * DESCRIPTION:
5870 * Scroll the content of a listview.
5872 * PARAMETER(S):
5873 * [I] infoPtr : valid pointer to the listview structure
5874 * [I] INT : horizontal scroll amount in pixels
5875 * [I] INT : vertical scroll amount in pixels
5877 * RETURN:
5878 * SUCCESS : TRUE
5879 * FAILURE : FALSE
5881 * COMMENTS:
5882 * If the control is in report mode (LVS_REPORT) the control can
5883 * be scrolled only in line increments. "dy" will be rounded to the
5884 * nearest number of pixels that are a whole line. Ex: if line height
5885 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
5886 * is passed the the scroll will be 0. (per MSDN 7/2002)
5888 * For: (per experimentaion with native control and CSpy ListView)
5889 * LVS_ICON dy=1 = 1 pixel (vertical only)
5890 * dx ignored
5891 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
5892 * dx ignored
5893 * LVS_LIST dx=1 = 1 column (horizontal only)
5894 * but will only scroll 1 column per message
5895 * no matter what the value.
5896 * dy must be 0 or FALSE returned.
5897 * LVS_REPORT dx=1 = 1 pixel
5898 * dy= see above
5901 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
5903 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
5904 case LVS_REPORT:
5905 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
5906 dy /= infoPtr->nItemHeight;
5907 break;
5908 case LVS_LIST:
5909 if (dy != 0) return FALSE;
5910 break;
5911 default: /* icon */
5912 dx = 0;
5913 break;
5916 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
5917 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
5919 return TRUE;
5922 /***
5923 * DESCRIPTION:
5924 * Sets the background color.
5926 * PARAMETER(S):
5927 * [I] infoPtr : valid pointer to the listview structure
5928 * [I] COLORREF : background color
5930 * RETURN:
5931 * SUCCESS : TRUE
5932 * FAILURE : FALSE
5934 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
5936 TRACE("(clrBk=%lx)\n", clrBk);
5938 if(infoPtr->clrBk != clrBk) {
5939 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
5940 infoPtr->clrBk = clrBk;
5941 if (clrBk == CLR_NONE)
5942 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
5943 else
5944 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
5945 LISTVIEW_InvalidateList(infoPtr);
5948 return TRUE;
5951 /* LISTVIEW_SetBkImage */
5953 /***
5954 * DESCRIPTION:
5955 * Sets the attributes of a header item.
5957 * PARAMETER(S):
5958 * [I] infoPtr : valid pointer to the listview structure
5959 * [I] INT : column index
5960 * [I] LPLVCOLUMNW : column attributes
5961 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
5962 * otherwise it is in fact a LPLVCOLUMNA
5964 * RETURN:
5965 * SUCCESS : TRUE
5966 * FAILURE : FALSE
5968 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5969 LPLVCOLUMNW lpColumn, BOOL isW)
5971 BOOL bResult = FALSE;
5972 HDITEMW hdi, hdiget;
5974 if ((lpColumn != NULL) && (nColumn >= 0) &&
5975 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
5977 /* initialize memory */
5978 ZeroMemory(&hdi, sizeof(hdi));
5980 if (lpColumn->mask & LVCF_FMT)
5982 /* format member is valid */
5983 hdi.mask |= HDI_FORMAT;
5985 /* get current format first */
5986 hdiget.mask = HDI_FORMAT;
5987 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
5988 /* preserve HDF_STRING if present */
5989 hdi.fmt = hdiget.fmt & HDF_STRING;
5991 /* set text alignment (leftmost column must be left-aligned) */
5992 if (nColumn == 0)
5994 hdi.fmt |= HDF_LEFT;
5996 else
5998 if (lpColumn->fmt & LVCFMT_LEFT)
5999 hdi.fmt |= HDF_LEFT;
6000 else if (lpColumn->fmt & LVCFMT_RIGHT)
6001 hdi.fmt |= HDF_RIGHT;
6002 else if (lpColumn->fmt & LVCFMT_CENTER)
6003 hdi.fmt |= HDF_CENTER;
6006 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6007 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6009 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6010 hdi.fmt |= HDF_IMAGE;
6012 if (lpColumn->fmt & LVCFMT_IMAGE)
6014 hdi.fmt |= HDF_IMAGE;
6015 hdi.iImage = I_IMAGECALLBACK;
6019 if (lpColumn->mask & LVCF_WIDTH)
6021 hdi.mask |= HDI_WIDTH;
6022 hdi.cxy = lpColumn->cx;
6025 if (lpColumn->mask & LVCF_TEXT)
6027 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6028 hdi.pszText = lpColumn->pszText;
6029 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6030 hdi.fmt |= HDF_STRING;
6033 if (lpColumn->mask & LVCF_IMAGE)
6035 hdi.mask |= HDI_IMAGE;
6036 hdi.iImage = lpColumn->iImage;
6039 if (lpColumn->mask & LVCF_ORDER)
6041 hdi.mask |= HDI_ORDER;
6042 hdi.iOrder = lpColumn->iOrder;
6045 /* set header item attributes */
6046 if (isW)
6047 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6048 else
6049 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6052 return bResult;
6055 /***
6056 * DESCRIPTION:
6057 * Sets the column order array
6059 * PARAMETERS:
6060 * [I] infoPtr : valid pointer to the listview structure
6061 * [I] INT : number of elements in column order array
6062 * [I] INT : pointer to column order array
6064 * RETURN:
6065 * SUCCESS : TRUE
6066 * FAILURE : FALSE
6068 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6070 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6072 if (!lpiArray)
6073 return FALSE;
6075 return TRUE;
6079 /***
6080 * DESCRIPTION:
6081 * Sets the width of a column
6083 * PARAMETERS:
6084 * [I] infoPtr : valid pointer to the listview structure
6085 * [I] INT : column index
6086 * [I] INT : column width
6088 * RETURN:
6089 * SUCCESS : TRUE
6090 * FAILURE : FALSE
6092 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6094 HDITEMW hdi;
6095 LRESULT lret;
6096 LONG lStyle = infoPtr->dwStyle;
6097 UINT uView = lStyle & LVS_TYPEMASK;
6098 HDC hdc;
6099 HFONT header_font;
6100 HFONT old_font;
6101 SIZE size;
6102 WCHAR text_buffer[DISP_TEXT_SIZE];
6103 INT header_item_count;
6104 INT item_index;
6105 INT nLabelWidth;
6106 RECT rcHeader;
6107 LVITEMW lvItem;
6108 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6110 if (!infoPtr->hwndHeader) /* make sure we have a header */
6111 return (FALSE);
6113 /* set column width only if in report or list mode */
6114 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6115 return (FALSE);
6117 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6119 /* take care of invalid cx values */
6120 if((uView == LVS_REPORT) && (cx < -2))
6121 cx = LVSCW_AUTOSIZE;
6122 else if (uView == LVS_LIST && (cx < 1))
6123 return FALSE;
6125 /* resize all columns if in LVS_LIST mode */
6126 if(uView == LVS_LIST) {
6127 infoPtr->nItemWidth = cx;
6128 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6129 return TRUE;
6132 /* autosize based on listview items width */
6133 if(cx == LVSCW_AUTOSIZE)
6135 /* set the width of the column to the width of the widest item */
6136 if (iCol == 0 || uView == LVS_LIST)
6138 cx = 0;
6139 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6141 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6142 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6144 if (infoPtr->himlSmall)
6145 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6147 else
6149 lvItem.iSubItem = iCol;
6150 lvItem.mask = LVIF_TEXT;
6151 lvItem.pszText = szDispText;
6152 lvItem.cchTextMax = DISP_TEXT_SIZE;
6153 cx = 0;
6154 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6156 lvItem.iItem = item_index;
6157 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6158 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6159 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6162 cx += TRAILING_PADDING;
6163 } /* autosize based on listview header width */
6164 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6166 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6168 /* if iCol is the last column make it fill the remainder of the controls width */
6169 if(iCol == (header_item_count - 1)) {
6170 /* get the width of every item except the current one */
6171 hdi.mask = HDI_WIDTH;
6172 cx = 0;
6174 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6175 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6176 cx+=hdi.cxy;
6179 /* retrieve the layout of the header */
6180 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6182 cx = (rcHeader.right - rcHeader.left) - cx;
6184 else
6186 /* Despite what the MS docs say, if this is not the last
6187 column, then MS resizes the column to the width of the
6188 largest text string in the column, including headers
6189 and items. This is different from LVSCW_AUTOSIZE in that
6190 LVSCW_AUTOSIZE ignores the header string length.
6193 /* retrieve header font */
6194 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6196 /* retrieve header text */
6197 hdi.mask = HDI_TEXT;
6198 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6199 hdi.pszText = text_buffer;
6201 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6203 /* determine the width of the text in the header */
6204 hdc = GetDC(infoPtr->hwndSelf);
6205 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6207 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6209 SelectObject(hdc, old_font); /* restore the old font */
6210 ReleaseDC(infoPtr->hwndSelf, hdc);
6212 lvItem.iSubItem = iCol;
6213 lvItem.mask = LVIF_TEXT;
6214 lvItem.pszText = szDispText;
6215 lvItem.cchTextMax = DISP_TEXT_SIZE;
6216 cx = size.cx;
6217 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6219 lvItem.iItem = item_index;
6220 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6221 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6222 nLabelWidth += TRAILING_PADDING;
6223 /* While it is possible for subitems to have icons, even MS messes
6224 up the positioning, so I suspect no applications actually use
6225 them. */
6226 if (item_index == 0 && infoPtr->himlSmall)
6227 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6228 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6233 /* call header to update the column change */
6234 hdi.mask = HDI_WIDTH;
6236 hdi.cxy = cx;
6237 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6239 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6241 return lret;
6244 /***
6245 * DESCRIPTION:
6246 * Sets the extended listview style.
6248 * PARAMETERS:
6249 * [I] infoPtr : valid pointer to the listview structure
6250 * [I] DWORD : mask
6251 * [I] DWORD : style
6253 * RETURN:
6254 * SUCCESS : previous style
6255 * FAILURE : 0
6257 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6259 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6261 /* set new style */
6262 if (dwMask)
6263 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6264 else
6265 infoPtr->dwLvExStyle = dwStyle;
6267 return dwOldStyle;
6270 /***
6271 * DESCRIPTION:
6272 * Sets the new hot cursor used during hot tracking and hover selection.
6274 * PARAMETER(S):
6275 * [I] infoPtr : valid pointer to the listview structure
6276 * [I} hCurosr : the new hot cursor handle
6278 * RETURN:
6279 * Returns the previous hot cursor
6281 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6283 HCURSOR oldCursor = infoPtr->hHotCursor;
6284 infoPtr->hHotCursor = hCursor;
6285 return oldCursor;
6289 /***
6290 * DESCRIPTION:
6291 * Sets the hot item index.
6293 * PARAMETERS:
6294 * [I] infoPtr : valid pointer to the listview structure
6295 * [I] INT : index
6297 * RETURN:
6298 * SUCCESS : previous hot item index
6299 * FAILURE : -1 (no hot item)
6301 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6303 INT iOldIndex = infoPtr->nHotItem;
6304 infoPtr->nHotItem = iIndex;
6305 return iOldIndex;
6309 /***
6310 * DESCRIPTION:
6311 * Sets the amount of time the cursor must hover over an item before it is selected.
6313 * PARAMETER(S):
6314 * [I] infoPtr : valid pointer to the listview structure
6315 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6317 * RETURN:
6318 * Returns the previous hover time
6320 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6322 DWORD oldHoverTime = infoPtr->dwHoverTime;
6323 infoPtr->dwHoverTime = dwHoverTime;
6324 return oldHoverTime;
6327 /***
6328 * DESCRIPTION:
6329 * Sets spacing for icons of LVS_ICON style.
6331 * PARAMETER(S):
6332 * [I] infoPtr : valid pointer to the listview structure
6333 * [I] DWORD : MAKELONG(cx, cy)
6335 * RETURN:
6336 * MAKELONG(oldcx, oldcy)
6338 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6340 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6341 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6342 LONG lStyle = infoPtr->dwStyle;
6343 UINT uView = lStyle & LVS_TYPEMASK;
6345 TRACE("requested=(%d,%d)\n", cx, cy);
6347 /* this is supported only for LVS_ICON style */
6348 if (uView != LVS_ICON) return oldspacing;
6350 /* set to defaults, if instructed to */
6351 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6352 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6354 /* if 0 then compute width
6355 * FIXME: Should scan each item and determine max width of
6356 * icon or label, then make that the width */
6357 if (cx == 0)
6358 cx = infoPtr->iconSpacing.cx;
6360 /* if 0 then compute height */
6361 if (cy == 0)
6362 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6363 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6366 infoPtr->iconSpacing.cx = cx;
6367 infoPtr->iconSpacing.cy = cy;
6369 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6370 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6371 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6372 infoPtr->ntmHeight);
6374 /* these depend on the iconSpacing */
6375 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6376 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6378 return oldspacing;
6381 inline void update_icon_size(HIMAGELIST himl, SIZE *size)
6383 INT cx, cy;
6385 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6387 size->cx = cx;
6388 size->cy = cy;
6390 else
6391 size->cx = size->cy = 0;
6394 /***
6395 * DESCRIPTION:
6396 * Sets image lists.
6398 * PARAMETER(S):
6399 * [I] infoPtr : valid pointer to the listview structure
6400 * [I] INT : image list type
6401 * [I] HIMAGELIST : image list handle
6403 * RETURN:
6404 * SUCCESS : old image list
6405 * FAILURE : NULL
6407 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6409 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6410 INT oldHeight = infoPtr->nItemHeight;
6411 HIMAGELIST himlOld = 0;
6413 switch (nType)
6415 case LVSIL_NORMAL:
6416 himlOld = infoPtr->himlNormal;
6417 infoPtr->himlNormal = himl;
6418 if (uView == LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6419 LISTVIEW_SetIconSpacing(infoPtr, 0);
6420 break;
6422 case LVSIL_SMALL:
6423 himlOld = infoPtr->himlSmall;
6424 infoPtr->himlSmall = himl;
6425 if (uView != LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6426 break;
6428 case LVSIL_STATE:
6429 himlOld = infoPtr->himlState;
6430 infoPtr->himlState = himl;
6431 update_icon_size(himl, &infoPtr->iconStateSize);
6432 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6433 break;
6435 default:
6436 ERR("Unknown icon type=%d\n", nType);
6437 return NULL;
6440 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6441 if (infoPtr->nItemHeight != oldHeight)
6442 LISTVIEW_UpdateScroll(infoPtr);
6444 return himlOld;
6447 /***
6448 * DESCRIPTION:
6449 * Preallocates memory (does *not* set the actual count of items !)
6451 * PARAMETER(S):
6452 * [I] infoPtr : valid pointer to the listview structure
6453 * [I] INT : item count (projected number of items to allocate)
6454 * [I] DWORD : update flags
6456 * RETURN:
6457 * SUCCESS : TRUE
6458 * FAILURE : FALSE
6460 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6462 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6464 if (infoPtr->dwStyle & LVS_OWNERDATA)
6466 int precount,topvisible;
6468 TRACE("LVS_OWNERDATA is set!\n");
6469 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6470 FIXME("flags %s %s not implemented\n",
6471 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6472 : "",
6473 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6475 LISTVIEW_RemoveAllSelections(infoPtr);
6477 precount = infoPtr->nItemCount;
6478 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6479 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6481 infoPtr->nItemCount = nItems;
6482 infoPtr->nItemWidth = max(LISTVIEW_CalculateMaxWidth(infoPtr),
6483 DEFAULT_COLUMN_WIDTH);
6485 LISTVIEW_UpdateSize(infoPtr);
6486 LISTVIEW_UpdateScroll(infoPtr);
6488 if (min(precount,infoPtr->nItemCount) < topvisible)
6489 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6491 else
6493 /* According to MSDN for non-LVS_OWNERDATA this is just
6494 * a performance issue. The control allocates its internal
6495 * data structures for the number of items specified. It
6496 * cuts down on the number of memory allocations. Therefore
6497 * we will just issue a WARN here
6499 WARN("for non-ownerdata performance option not implemented.\n");
6502 return TRUE;
6505 /***
6506 * DESCRIPTION:
6507 * Sets the position of an item.
6509 * PARAMETER(S):
6510 * [I] infoPtr : valid pointer to the listview structure
6511 * [I] nItem : item index
6512 * [I] pt : coordinate
6514 * RETURN:
6515 * SUCCESS : TRUE
6516 * FAILURE : FALSE
6518 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6520 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6521 POINT old;
6523 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6525 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6526 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6528 /* This point value seems to be an undocumented feature.
6529 * The best guess is that it means either at the origin,
6530 * or at true beginning of the list. I will assume the origin. */
6531 if ((pt.x == -1) && (pt.y == -1))
6532 LISTVIEW_GetOrigin(infoPtr, &pt);
6533 else if (uView == LVS_ICON)
6535 pt.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
6536 pt.y -= ICON_TOP_PADDING;
6539 /* save the old position */
6540 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
6541 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
6543 /* Is the position changing? */
6544 if (pt.x == old.x && pt.y == old.y) return TRUE;
6546 /* FIXME: shouldn't we invalidate, as the item moved? */
6548 /* Allocating a POINTER for every item is too resource intensive,
6549 * so we'll keep the (x,y) in different arrays */
6550 if (DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)pt.x) &&
6551 DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)pt.y) )
6552 return TRUE;
6554 ERR("We should never fail here (nItem=%d, pt=%s), please report.\n",
6555 nItem, debugpoint(&pt));
6556 return FALSE;
6559 /***
6560 * DESCRIPTION:
6561 * Sets the state of one or many items.
6563 * PARAMETER(S):
6564 * [I] infoPtr : valid pointer to the listview structure
6565 * [I]INT : item index
6566 * [I] LPLVITEM : item or subitem info
6568 * RETURN:
6569 * SUCCESS : TRUE
6570 * FAILURE : FALSE
6572 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6574 BOOL bResult = TRUE;
6575 LVITEMW lvItem;
6577 lvItem.iItem = nItem;
6578 lvItem.iSubItem = 0;
6579 lvItem.mask = LVIF_STATE;
6580 lvItem.state = lpLVItem->state;
6581 lvItem.stateMask = lpLVItem->stateMask;
6582 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6584 if (nItem == -1)
6586 /* apply to all items */
6587 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6588 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6590 else
6591 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6593 return bResult;
6596 /***
6597 * DESCRIPTION:
6598 * Sets the text of an item or subitem.
6600 * PARAMETER(S):
6601 * [I] hwnd : window handle
6602 * [I] nItem : item index
6603 * [I] lpLVItem : item or subitem info
6604 * [I] isW : TRUE if input is Unicode
6606 * RETURN:
6607 * SUCCESS : TRUE
6608 * FAILURE : FALSE
6610 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6612 LVITEMW lvItem;
6614 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6616 lvItem.iItem = nItem;
6617 lvItem.iSubItem = lpLVItem->iSubItem;
6618 lvItem.mask = LVIF_TEXT;
6619 lvItem.pszText = lpLVItem->pszText;
6620 lvItem.cchTextMax = lpLVItem->cchTextMax;
6622 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6624 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6627 /***
6628 * DESCRIPTION:
6629 * Set item index that marks the start of a multiple selection.
6631 * PARAMETER(S):
6632 * [I] infoPtr : valid pointer to the listview structure
6633 * [I] INT : index
6635 * RETURN:
6636 * Index number or -1 if there is no selection mark.
6638 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6640 INT nOldIndex = infoPtr->nSelectionMark;
6642 TRACE("(nIndex=%d)\n", nIndex);
6644 infoPtr->nSelectionMark = nIndex;
6646 return nOldIndex;
6649 /***
6650 * DESCRIPTION:
6651 * Sets the text background color.
6653 * PARAMETER(S):
6654 * [I] infoPtr : valid pointer to the listview structure
6655 * [I] COLORREF : text background color
6657 * RETURN:
6658 * SUCCESS : TRUE
6659 * FAILURE : FALSE
6661 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6663 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6665 if (infoPtr->clrTextBk != clrTextBk)
6667 infoPtr->clrTextBk = clrTextBk;
6668 LISTVIEW_InvalidateList(infoPtr);
6671 return TRUE;
6674 /***
6675 * DESCRIPTION:
6676 * Sets the text foreground color.
6678 * PARAMETER(S):
6679 * [I] infoPtr : valid pointer to the listview structure
6680 * [I] COLORREF : text color
6682 * RETURN:
6683 * SUCCESS : TRUE
6684 * FAILURE : FALSE
6686 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6688 TRACE("(clrText=%lx)\n", clrText);
6690 if (infoPtr->clrText != clrText)
6692 infoPtr->clrText = clrText;
6693 LISTVIEW_InvalidateList(infoPtr);
6696 return TRUE;
6699 /* LISTVIEW_SetToolTips */
6700 /* LISTVIEW_SetUnicodeFormat */
6701 /* LISTVIEW_SetWorkAreas */
6703 /***
6704 * DESCRIPTION:
6705 * Callback internally used by LISTVIEW_SortItems()
6707 * PARAMETER(S):
6708 * [I] LPVOID : first LISTVIEW_ITEM to compare
6709 * [I] LPVOID : second LISTVIEW_ITEM to compare
6710 * [I] LPARAM : HWND of control
6712 * RETURN:
6713 * if first comes before second : negative
6714 * if first comes after second : positive
6715 * if first and second are equivalent : zero
6717 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6719 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6720 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6721 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6723 /* Forward the call to the client defined callback */
6724 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6727 /***
6728 * DESCRIPTION:
6729 * Sorts the listview items.
6731 * PARAMETER(S):
6732 * [I] infoPtr : valid pointer to the listview structure
6733 * [I] WPARAM : application-defined value
6734 * [I] LPARAM : pointer to comparision callback
6736 * RETURN:
6737 * SUCCESS : TRUE
6738 * FAILURE : FALSE
6740 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6742 UINT lStyle = infoPtr->dwStyle;
6743 HDPA hdpaSubItems;
6744 LISTVIEW_ITEM *lpItem;
6745 LPVOID selectionMarkItem;
6746 int i;
6748 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6750 if (lStyle & LVS_OWNERDATA) return FALSE;
6752 if (!infoPtr->hdpaItems) return FALSE;
6754 /* if there are 0 or 1 items, there is no need to sort */
6755 if (infoPtr->nItemCount < 2) return TRUE;
6757 if (infoPtr->nFocusedItem >= 0)
6759 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6760 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6761 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6764 infoPtr->pfnCompare = pfnCompare;
6765 infoPtr->lParamSort = lParamSort;
6766 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6768 /* Adjust selections and indices so that they are the way they should
6769 * be after the sort (otherwise, the list items move around, but
6770 * whatever is at the item's previous original position will be
6771 * selected instead)
6772 * FIXME: can't this be made more efficient?
6774 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6775 for (i=0; i < infoPtr->nItemCount; i++)
6777 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6778 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6780 if (lpItem->state & LVIS_SELECTED)
6781 LISTVIEW_AddSelectionRange(infoPtr, i, i);
6782 else
6783 LISTVIEW_RemoveSelectionRange(infoPtr, i, i);
6784 if (lpItem->state & LVIS_FOCUSED)
6786 infoPtr->nFocusedItem = i;
6787 lpItem->state &= ~LVIS_FOCUSED;
6790 if (selectionMarkItem != NULL)
6791 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6792 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6794 /* align the items */
6795 LISTVIEW_AlignTop(infoPtr);
6797 /* refresh the display */
6798 LISTVIEW_InvalidateList(infoPtr); /* FIXME: display should not change for [SMALL]ICON view */
6800 return TRUE;
6803 /***
6804 * DESCRIPTION:
6805 * Updates an items or rearranges the listview control.
6807 * PARAMETER(S):
6808 * [I] infoPtr : valid pointer to the listview structure
6809 * [I] INT : item index
6811 * RETURN:
6812 * SUCCESS : TRUE
6813 * FAILURE : FALSE
6815 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
6817 LONG lStyle = infoPtr->dwStyle;
6818 UINT uView = lStyle & LVS_TYPEMASK;
6820 TRACE("(nItem=%d)\n", nItem);
6822 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6824 /* rearrange with default alignment style */
6825 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
6826 LISTVIEW_Arrange(infoPtr, 0);
6827 else
6828 LISTVIEW_InvalidateItem(infoPtr, nItem);
6830 return TRUE;
6834 /***
6835 * DESCRIPTION:
6836 * Creates the listview control.
6838 * PARAMETER(S):
6839 * [I] hwnd : window handle
6840 * [I] lpcs : the create parameters
6842 * RETURN:
6843 * Success: 0
6844 * Failure: -1
6846 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
6848 LISTVIEW_INFO *infoPtr;
6849 UINT uView = lpcs->style & LVS_TYPEMASK;
6850 LOGFONTW logFont;
6852 TRACE("(lpcs=%p)\n", lpcs);
6854 /* initialize info pointer */
6855 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
6856 if (!infoPtr) return -1;
6858 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
6860 infoPtr->hwndSelf = hwnd;
6861 infoPtr->dwStyle = lpcs->style;
6862 /* determine the type of structures to use */
6863 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
6864 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
6866 /* initialize color information */
6867 infoPtr->clrBk = CLR_NONE;
6868 infoPtr->clrText = comctl32_color.clrWindowText;
6869 infoPtr->clrTextBk = CLR_DEFAULT;
6870 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
6872 /* set default values */
6873 infoPtr->nFocusedItem = -1;
6874 infoPtr->nSelectionMark = -1;
6875 infoPtr->nHotItem = -1;
6876 infoPtr->bRedraw = TRUE;
6877 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
6878 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
6879 infoPtr->nEditLabelItem = -1;
6881 /* get default font (icon title) */
6882 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
6883 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
6884 infoPtr->hFont = infoPtr->hDefaultFont;
6885 LISTVIEW_SaveTextMetrics(infoPtr);
6887 /* create header */
6888 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
6889 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
6890 0, 0, 0, 0, hwnd, (HMENU)0,
6891 lpcs->hInstance, NULL);
6893 /* set header unicode format */
6894 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
6896 /* set header font */
6897 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
6898 (LPARAM)TRUE);
6900 if (uView == LVS_ICON)
6902 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
6903 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
6905 else if (uView == LVS_REPORT)
6907 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
6909 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6911 else
6913 /* set HDS_HIDDEN flag to hide the header bar */
6914 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
6915 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
6919 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6920 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6922 else
6924 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6925 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6928 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
6929 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
6931 /* display unsupported listview window styles */
6932 LISTVIEW_UnsupportedStyles(lpcs->style);
6934 /* allocate memory for the data structure */
6935 infoPtr->hdpaItems = DPA_Create(10);
6936 infoPtr->hdpaPosX = DPA_Create(10);
6937 infoPtr->hdpaPosY = DPA_Create(10);
6939 /* allocate memory for the selection ranges */
6940 infoPtr->hdpaSelectionRanges = DPA_Create(10);
6942 /* initialize size of items */
6943 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6944 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6946 /* initialize the hover time to -1(indicating the default system hover time) */
6947 infoPtr->dwHoverTime = -1;
6949 return 0;
6952 /***
6953 * DESCRIPTION:
6954 * Erases the background of the listview control.
6956 * PARAMETER(S):
6957 * [I] infoPtr : valid pointer to the listview structure
6958 * [I] hdc : device context handle
6960 * RETURN:
6961 * SUCCESS : TRUE
6962 * FAILURE : FALSE
6964 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
6966 RECT rc;
6968 TRACE("(hdc=%x)\n", hdc);
6970 if (!GetClipBox(hdc, &rc)) return FALSE;
6972 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
6976 /***
6977 * DESCRIPTION:
6978 * Helper function for LISTVIEW_[HV]Scroll *only*.
6979 * Performs vertical/horizontal scrolling by a give amount.
6981 * PARAMETER(S):
6982 * [I] infoPtr : valid pointer to the listview structure
6983 * [I] dx : amount of horizontal scroll
6984 * [I] dy : amount of vertical scroll
6986 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6988 /* now we can scroll the list */
6989 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
6990 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
6991 /* if we have focus, adjust rect */
6992 OffsetRect(&infoPtr->rcFocus, dx, dy);
6993 UpdateWindow(infoPtr->hwndSelf);
6996 /***
6997 * DESCRIPTION:
6998 * Performs vertical scrolling.
7000 * PARAMETER(S):
7001 * [I] infoPtr : valid pointer to the listview structure
7002 * [I] nScrollCode : scroll code
7003 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7004 * [I] hScrollWnd : scrollbar control window handle
7006 * RETURN:
7007 * Zero
7009 * NOTES:
7010 * SB_LINEUP/SB_LINEDOWN:
7011 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7012 * for LVS_REPORT is 1 line
7013 * for LVS_LIST cannot occur
7016 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7017 INT nScrollDiff, HWND hScrollWnd)
7019 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7020 INT nOldScrollPos, nNewScrollPos;
7021 SCROLLINFO scrollInfo;
7022 BOOL is_an_icon;
7024 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7026 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7028 scrollInfo.cbSize = sizeof(SCROLLINFO);
7029 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7031 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7033 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7035 nOldScrollPos = scrollInfo.nPos;
7036 switch (nScrollCode)
7038 case SB_INTERNAL:
7039 break;
7041 case SB_LINEUP:
7042 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7043 break;
7045 case SB_LINEDOWN:
7046 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7047 break;
7049 case SB_PAGEUP:
7050 nScrollDiff = -scrollInfo.nPage;
7051 break;
7053 case SB_PAGEDOWN:
7054 nScrollDiff = scrollInfo.nPage;
7055 break;
7057 case SB_THUMBPOSITION:
7058 case SB_THUMBTRACK:
7059 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7060 break;
7062 default:
7063 nScrollDiff = 0;
7066 /* quit right away if pos isn't changing */
7067 if (nScrollDiff == 0) return 0;
7069 /* calculate new position, and handle overflows */
7070 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7071 if (nScrollDiff > 0) {
7072 if (nNewScrollPos < nOldScrollPos ||
7073 nNewScrollPos > scrollInfo.nMax)
7074 nNewScrollPos = scrollInfo.nMax;
7075 } else {
7076 if (nNewScrollPos > nOldScrollPos ||
7077 nNewScrollPos < scrollInfo.nMin)
7078 nNewScrollPos = scrollInfo.nMin;
7081 /* set the new position, and reread in case it changed */
7082 scrollInfo.fMask = SIF_POS;
7083 scrollInfo.nPos = nNewScrollPos;
7084 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7086 /* carry on only if it really changed */
7087 if (nNewScrollPos == nOldScrollPos) return 0;
7089 /* now adjust to client coordinates */
7090 nScrollDiff = nOldScrollPos - nNewScrollPos;
7091 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7093 /* and scroll the window */
7094 scroll_list(infoPtr, 0, nScrollDiff);
7096 return 0;
7099 /***
7100 * DESCRIPTION:
7101 * Performs horizontal scrolling.
7103 * PARAMETER(S):
7104 * [I] infoPtr : valid pointer to the listview structure
7105 * [I] nScrollCode : scroll code
7106 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7107 * [I] hScrollWnd : scrollbar control window handle
7109 * RETURN:
7110 * Zero
7112 * NOTES:
7113 * SB_LINELEFT/SB_LINERIGHT:
7114 * for LVS_ICON, LVS_SMALLICON 1 pixel
7115 * for LVS_REPORT is 1 pixel
7116 * for LVS_LIST is 1 column --> which is a 1 because the
7117 * scroll is based on columns not pixels
7120 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7121 INT nScrollDiff, HWND hScrollWnd)
7123 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7124 INT nOldScrollPos, nNewScrollPos;
7125 SCROLLINFO scrollInfo;
7127 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7129 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7131 scrollInfo.cbSize = sizeof(SCROLLINFO);
7132 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7134 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7136 nOldScrollPos = scrollInfo.nPos;
7138 switch (nScrollCode)
7140 case SB_INTERNAL:
7141 break;
7143 case SB_LINELEFT:
7144 nScrollDiff = -1;
7145 break;
7147 case SB_LINERIGHT:
7148 nScrollDiff = 1;
7149 break;
7151 case SB_PAGELEFT:
7152 nScrollDiff = -scrollInfo.nPage;
7153 break;
7155 case SB_PAGERIGHT:
7156 nScrollDiff = scrollInfo.nPage;
7157 break;
7159 case SB_THUMBPOSITION:
7160 case SB_THUMBTRACK:
7161 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7162 break;
7164 default:
7165 nScrollDiff = 0;
7168 /* quit right away if pos isn't changing */
7169 if (nScrollDiff == 0) return 0;
7171 /* calculate new position, and handle overflows */
7172 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7173 if (nScrollDiff > 0) {
7174 if (nNewScrollPos < nOldScrollPos ||
7175 nNewScrollPos > scrollInfo.nMax)
7176 nNewScrollPos = scrollInfo.nMax;
7177 } else {
7178 if (nNewScrollPos > nOldScrollPos ||
7179 nNewScrollPos < scrollInfo.nMin)
7180 nNewScrollPos = scrollInfo.nMin;
7183 /* set the new position, and reread in case it changed */
7184 scrollInfo.fMask = SIF_POS;
7185 scrollInfo.nPos = nNewScrollPos;
7186 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7188 /* carry on only if it really changed */
7189 if (nNewScrollPos == nOldScrollPos) return 0;
7191 if(uView == LVS_REPORT)
7192 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7194 /* now adjust to client coordinates */
7195 nScrollDiff = nOldScrollPos - nNewScrollPos;
7196 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7198 /* and scroll the window */
7199 scroll_list(infoPtr, nScrollDiff, 0);
7201 return 0;
7204 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7206 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7207 INT gcWheelDelta = 0;
7208 UINT pulScrollLines = 3;
7209 SCROLLINFO scrollInfo;
7211 TRACE("(wheelDelta=%d)\n", wheelDelta);
7213 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7214 gcWheelDelta -= wheelDelta;
7216 scrollInfo.cbSize = sizeof(SCROLLINFO);
7217 scrollInfo.fMask = SIF_POS;
7219 switch(uView)
7221 case LVS_ICON:
7222 case LVS_SMALLICON:
7224 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7225 * should be fixed in the future.
7227 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7228 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7229 scrollInfo.nPos + (gcWheelDelta < 0) ?
7230 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7231 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7232 break;
7234 case LVS_REPORT:
7235 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7237 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7239 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7240 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7241 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7244 break;
7246 case LVS_LIST:
7247 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7248 break;
7250 return 0;
7253 /***
7254 * DESCRIPTION:
7255 * ???
7257 * PARAMETER(S):
7258 * [I] infoPtr : valid pointer to the listview structure
7259 * [I] INT : virtual key
7260 * [I] LONG : key data
7262 * RETURN:
7263 * Zero
7265 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7267 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7268 INT nItem = -1;
7269 NMLVKEYDOWN nmKeyDown;
7271 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7273 /* send LVN_KEYDOWN notification */
7274 nmKeyDown.wVKey = nVirtualKey;
7275 nmKeyDown.flags = 0;
7276 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7278 switch (nVirtualKey)
7280 case VK_RETURN:
7281 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7283 notify(infoPtr, NM_RETURN);
7284 notify(infoPtr, LVN_ITEMACTIVATE);
7286 break;
7288 case VK_HOME:
7289 if (infoPtr->nItemCount > 0)
7290 nItem = 0;
7291 break;
7293 case VK_END:
7294 if (infoPtr->nItemCount > 0)
7295 nItem = infoPtr->nItemCount - 1;
7296 break;
7298 case VK_LEFT:
7299 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7300 break;
7302 case VK_UP:
7303 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7304 break;
7306 case VK_RIGHT:
7307 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7308 break;
7310 case VK_DOWN:
7311 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7312 break;
7314 case VK_PRIOR:
7315 if (uView == LVS_REPORT)
7316 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7317 else
7318 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7319 * LISTVIEW_GetCountPerRow(infoPtr);
7320 if(nItem < 0) nItem = 0;
7321 break;
7323 case VK_NEXT:
7324 if (uView == LVS_REPORT)
7325 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7326 else
7327 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7328 * LISTVIEW_GetCountPerRow(infoPtr);
7329 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7330 break;
7333 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7334 LISTVIEW_KeySelection(infoPtr, nItem);
7336 return 0;
7339 /***
7340 * DESCRIPTION:
7341 * Kills the focus.
7343 * PARAMETER(S):
7344 * [I] infoPtr : valid pointer to the listview structure
7346 * RETURN:
7347 * Zero
7349 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7351 TRACE("()\n");
7353 /* if we did not have the focus, there's nothing to do */
7354 if (!infoPtr->bFocus) return 0;
7356 /* send NM_KILLFOCUS notification */
7357 notify(infoPtr, NM_KILLFOCUS);
7359 /* if we have a focus rectagle, get rid of it */
7360 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7362 /* set window focus flag */
7363 infoPtr->bFocus = FALSE;
7365 /* invalidate the selected items before reseting focus flag */
7366 LISTVIEW_InvalidateSelectedItems(infoPtr);
7368 return 0;
7371 /***
7372 * DESCRIPTION:
7373 * Processes double click messages (left mouse button).
7375 * PARAMETER(S):
7376 * [I] infoPtr : valid pointer to the listview structure
7377 * [I] wKey : key flag
7378 * [I] pts : mouse coordinate
7380 * RETURN:
7381 * Zero
7383 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7385 LVHITTESTINFO htInfo;
7387 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7389 /* send NM_RELEASEDCAPTURE notification */
7390 notify(infoPtr, NM_RELEASEDCAPTURE);
7392 htInfo.pt.x = pts.x;
7393 htInfo.pt.y = pts.y;
7395 /* send NM_DBLCLK notification */
7396 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7397 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7399 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7400 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7402 return 0;
7405 /***
7406 * DESCRIPTION:
7407 * Processes mouse down messages (left mouse button).
7409 * PARAMETER(S):
7410 * [I] infoPtr : valid pointer to the listview structure
7411 * [I] wKey : key flag
7412 * [I] pts : mouse coordinate
7414 * RETURN:
7415 * Zero
7417 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7419 LVHITTESTINFO lvHitTestInfo;
7420 LONG lStyle = infoPtr->dwStyle;
7421 static BOOL bGroupSelect = TRUE;
7422 POINT pt = { pts.x, pts.y };
7423 INT nItem;
7425 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7427 /* send NM_RELEASEDCAPTURE notification */
7428 notify(infoPtr, NM_RELEASEDCAPTURE);
7430 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7432 /* set left button down flag */
7433 infoPtr->bLButtonDown = TRUE;
7435 lvHitTestInfo.pt.x = pts.x;
7436 lvHitTestInfo.pt.y = pts.y;
7438 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7439 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7440 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7442 if (lStyle & LVS_SINGLESEL)
7444 if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7445 && infoPtr->nEditLabelItem == -1)
7446 infoPtr->nEditLabelItem = nItem;
7447 else
7448 LISTVIEW_SetSelection(infoPtr, nItem);
7450 else
7452 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7454 if (bGroupSelect)
7455 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7456 else
7458 LVITEMW item;
7460 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7461 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7463 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7464 infoPtr->nSelectionMark = nItem;
7467 else if (wKey & MK_CONTROL)
7469 LVITEMW item;
7471 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7473 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7474 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7475 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7476 infoPtr->nSelectionMark = nItem;
7478 else if (wKey & MK_SHIFT)
7480 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7482 else
7484 BOOL was_selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
7486 /* set selection (clears other pre-existing selections) */
7487 LISTVIEW_SetSelection(infoPtr, nItem);
7489 if (was_selected && infoPtr->nEditLabelItem == -1)
7490 infoPtr->nEditLabelItem = nItem;
7494 else
7496 /* remove all selections */
7497 LISTVIEW_RemoveAllSelections(infoPtr);
7500 return 0;
7503 /***
7504 * DESCRIPTION:
7505 * Processes mouse up messages (left mouse button).
7507 * PARAMETER(S):
7508 * [I] infoPtr : valid pointer to the listview structure
7509 * [I] wKey : key flag
7510 * [I] pts : mouse coordinate
7512 * RETURN:
7513 * Zero
7515 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7517 LVHITTESTINFO lvHitTestInfo;
7519 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7521 if (!infoPtr->bLButtonDown) return 0;
7523 lvHitTestInfo.pt.x = pts.x;
7524 lvHitTestInfo.pt.y = pts.y;
7526 /* send NM_CLICK notification */
7527 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7528 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7530 /* set left button flag */
7531 infoPtr->bLButtonDown = FALSE;
7533 if(infoPtr->nEditLabelItem != -1)
7535 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem &&
7536 (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7537 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7538 infoPtr->nEditLabelItem = -1;
7541 return 0;
7544 /***
7545 * DESCRIPTION:
7546 * Destroys the listview control (called after WM_DESTROY).
7548 * PARAMETER(S):
7549 * [I] infoPtr : valid pointer to the listview structure
7551 * RETURN:
7552 * Zero
7554 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7556 LONG lStyle = infoPtr->dwStyle;
7558 TRACE("()\n");
7560 /* delete all items */
7561 LISTVIEW_DeleteAllItems(infoPtr);
7563 /* destroy data structure */
7564 DPA_Destroy(infoPtr->hdpaItems);
7565 DPA_Destroy(infoPtr->hdpaSelectionRanges);
7567 /* destroy image lists */
7568 if (!(lStyle & LVS_SHAREIMAGELISTS))
7570 /* FIXME: If the caller does a ImageList_Destroy and then we
7571 * do this code the area will be freed twice. Currently
7572 * this generates an "err:heap:HEAP_ValidateInUseArena
7573 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
7574 * has PREV_FREE flag" sometimes.
7576 * We will leak the memory till we figure out how to fix
7578 if (infoPtr->himlNormal)
7579 ImageList_Destroy(infoPtr->himlNormal);
7580 if (infoPtr->himlSmall)
7581 ImageList_Destroy(infoPtr->himlSmall);
7582 if (infoPtr->himlState)
7583 ImageList_Destroy(infoPtr->himlState);
7586 /* destroy font, bkgnd brush */
7587 infoPtr->hFont = 0;
7588 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7589 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7591 /* free listview info pointer*/
7592 COMCTL32_Free(infoPtr);
7594 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7595 return 0;
7598 /***
7599 * DESCRIPTION:
7600 * Handles notifications from children.
7602 * PARAMETER(S):
7603 * [I] infoPtr : valid pointer to the listview structure
7604 * [I] INT : control identifier
7605 * [I] LPNMHDR : notification information
7607 * RETURN:
7608 * Zero
7610 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7612 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7614 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7616 /* handle notification from header control */
7617 if (lpnmh->code == HDN_ENDTRACKW)
7619 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7620 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7622 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7624 /* Handle sorting by Header Column */
7625 NMLISTVIEW nmlv;
7627 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7628 nmlv.iItem = -1;
7629 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
7630 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7632 else if(lpnmh->code == NM_RELEASEDCAPTURE)
7634 /* Idealy this should be done in HDN_ENDTRACKA
7635 * but since SetItemBounds in Header.c is called after
7636 * the notification is sent, it is neccessary to handle the
7637 * update of the scroll bar here (Header.c works fine as it is,
7638 * no need to disturb it)
7640 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7641 LISTVIEW_UpdateScroll(infoPtr);
7642 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7647 return 0;
7650 /***
7651 * DESCRIPTION:
7652 * Determines the type of structure to use.
7654 * PARAMETER(S):
7655 * [I] infoPtr : valid pointer to the listview structureof the sender
7656 * [I] HWND : listview window handle
7657 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
7659 * RETURN:
7660 * Zero
7662 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7664 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7666 if (nCommand == NF_REQUERY)
7667 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7668 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7669 return 0;
7672 /***
7673 * DESCRIPTION:
7674 * Paints/Repaints the listview control.
7676 * PARAMETER(S):
7677 * [I] infoPtr : valid pointer to the listview structure
7678 * [I] HDC : device context handle
7680 * RETURN:
7681 * Zero
7683 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7685 TRACE("(hdc=%x)\n", hdc);
7687 if (hdc)
7688 LISTVIEW_Refresh(infoPtr, hdc);
7689 else
7691 PAINTSTRUCT ps;
7693 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7694 if (!hdc) return 1;
7695 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7696 LISTVIEW_Refresh(infoPtr, hdc);
7697 EndPaint(infoPtr->hwndSelf, &ps);
7700 return 0;
7703 /***
7704 * DESCRIPTION:
7705 * Processes double click messages (right mouse button).
7707 * PARAMETER(S):
7708 * [I] infoPtr : valid pointer to the listview structure
7709 * [I] wKey : key flag
7710 * [I] pts : mouse coordinate
7712 * RETURN:
7713 * Zero
7715 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7717 LVHITTESTINFO lvHitTestInfo;
7719 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7721 /* send NM_RELEASEDCAPTURE notification */
7722 notify(infoPtr, NM_RELEASEDCAPTURE);
7724 /* send NM_RDBLCLK notification */
7725 lvHitTestInfo.pt.x = pts.x;
7726 lvHitTestInfo.pt.y = pts.y;
7727 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7728 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
7730 return 0;
7733 /***
7734 * DESCRIPTION:
7735 * Processes mouse down messages (right mouse button).
7737 * PARAMETER(S):
7738 * [I] infoPtr : valid pointer to the listview structure
7739 * [I] wKey : key flag
7740 * [I] pts : mouse coordinate
7742 * RETURN:
7743 * Zero
7745 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7747 LVHITTESTINFO lvHitTestInfo;
7748 INT nItem;
7750 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7752 /* send NM_RELEASEDCAPTURE notification */
7753 notify(infoPtr, NM_RELEASEDCAPTURE);
7755 /* make sure the listview control window has the focus */
7756 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7758 /* set right button down flag */
7759 infoPtr->bRButtonDown = TRUE;
7761 /* determine the index of the selected item */
7762 lvHitTestInfo.pt.x = pts.x;
7763 lvHitTestInfo.pt.y = pts.y;
7764 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7766 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7768 LISTVIEW_SetItemFocus(infoPtr, nItem);
7769 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7770 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7771 LISTVIEW_SetSelection(infoPtr, nItem);
7773 else
7775 LISTVIEW_RemoveAllSelections(infoPtr);
7778 return 0;
7781 /***
7782 * DESCRIPTION:
7783 * Processes mouse up messages (right mouse button).
7785 * PARAMETER(S):
7786 * [I] infoPtr : valid pointer to the listview structure
7787 * [I] wKey : key flag
7788 * [I] pts : mouse coordinate
7790 * RETURN:
7791 * Zero
7793 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7795 LVHITTESTINFO lvHitTestInfo;
7796 POINT pt;
7798 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7800 if (!infoPtr->bRButtonDown) return 0;
7802 /* set button flag */
7803 infoPtr->bRButtonDown = FALSE;
7805 /* Send NM_RClICK notification */
7806 lvHitTestInfo.pt.x = pts.x;
7807 lvHitTestInfo.pt.y = pts.y;
7808 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7809 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
7811 /* Change to screen coordinate for WM_CONTEXTMENU */
7812 pt = lvHitTestInfo.pt;
7813 ClientToScreen(infoPtr->hwndSelf, &pt);
7815 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
7816 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
7817 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
7819 return 0;
7823 /***
7824 * DESCRIPTION:
7825 * Sets the cursor.
7827 * PARAMETER(S):
7828 * [I] infoPtr : valid pointer to the listview structure
7829 * [I] hwnd : window handle of window containing the cursor
7830 * [I] nHittest : hit-test code
7831 * [I] wMouseMsg : ideintifier of the mouse message
7833 * RETURN:
7834 * TRUE if cursor is set
7835 * FALSE otherwise
7837 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
7839 LVHITTESTINFO lvHitTestInfo;
7841 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
7843 if(!infoPtr->hHotCursor) return FALSE;
7845 GetCursorPos(&lvHitTestInfo.pt);
7846 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
7848 SetCursor(infoPtr->hHotCursor);
7850 return TRUE;
7853 /***
7854 * DESCRIPTION:
7855 * Sets the focus.
7857 * PARAMETER(S):
7858 * [I] infoPtr : valid pointer to the listview structure
7859 * [I] infoPtr : handle of previously focused window
7861 * RETURN:
7862 * Zero
7864 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
7866 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
7868 /* if we have the focus already, there's nothing to do */
7869 if (infoPtr->bFocus) return 0;
7871 /* send NM_SETFOCUS notification */
7872 notify(infoPtr, NM_SETFOCUS);
7874 /* set window focus flag */
7875 infoPtr->bFocus = TRUE;
7877 /* put the focus rect back on */
7878 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
7880 /* redraw all visible selected items */
7881 LISTVIEW_InvalidateSelectedItems(infoPtr);
7883 return 0;
7886 /***
7887 * DESCRIPTION:
7888 * Sets the font.
7890 * PARAMETER(S):
7891 * [I] infoPtr : valid pointer to the listview structure
7892 * [I] HFONT : font handle
7893 * [I] WORD : redraw flag
7895 * RETURN:
7896 * Zero
7898 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
7900 HFONT oldFont = infoPtr->hFont;
7902 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
7904 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
7905 if (infoPtr->hFont == oldFont) return 0;
7907 LISTVIEW_SaveTextMetrics(infoPtr);
7909 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
7910 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
7912 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
7914 return 0;
7917 /***
7918 * DESCRIPTION:
7919 * Message handling for WM_SETREDRAW.
7920 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
7922 * PARAMETER(S):
7923 * [I] infoPtr : valid pointer to the listview structure
7924 * [I] bRedraw: state of redraw flag
7926 * RETURN:
7927 * DefWinProc return value
7929 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
7931 infoPtr->bRedraw = bRedraw;
7932 if(bRedraw)
7933 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
7934 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
7935 return 0;
7938 /***
7939 * DESCRIPTION:
7940 * Resizes the listview control. This function processes WM_SIZE
7941 * messages. At this time, the width and height are not used.
7943 * PARAMETER(S):
7944 * [I] infoPtr : valid pointer to the listview structure
7945 * [I] WORD : new width
7946 * [I] WORD : new height
7948 * RETURN:
7949 * Zero
7951 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
7953 LONG lStyle = infoPtr->dwStyle;
7954 UINT uView = lStyle & LVS_TYPEMASK;
7956 TRACE("(width=%d, height=%d)\n", Width, Height);
7958 if (LISTVIEW_UpdateSize(infoPtr))
7960 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
7962 if (lStyle & LVS_ALIGNLEFT)
7963 LISTVIEW_AlignLeft(infoPtr);
7964 else
7965 LISTVIEW_AlignTop(infoPtr);
7968 LISTVIEW_UpdateScroll(infoPtr);
7970 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7973 return 0;
7976 /***
7977 * DESCRIPTION:
7978 * Sets the size information.
7980 * PARAMETER(S):
7981 * [I] infoPtr : valid pointer to the listview structure
7983 * RETURN:
7984 * Zero if no size change
7985 * 1 of size changed
7987 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
7989 LONG lStyle = infoPtr->dwStyle;
7990 UINT uView = lStyle & LVS_TYPEMASK;
7991 RECT rcList;
7992 RECT rcOld;
7994 GetClientRect(infoPtr->hwndSelf, &rcList);
7995 CopyRect(&rcOld,&(infoPtr->rcList));
7996 infoPtr->rcList.left = 0;
7997 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
7998 infoPtr->rcList.top = 0;
7999 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8001 if (uView == LVS_LIST)
8003 /* Apparently the "LIST" style is supposed to have the same
8004 * number of items in a column even if there is no scroll bar.
8005 * Since if a scroll bar already exists then the bottom is already
8006 * reduced, only reduce if the scroll bar does not currently exist.
8007 * The "2" is there to mimic the native control. I think it may be
8008 * related to either padding or edges. (GLA 7/2002)
8010 if (!(lStyle & WS_HSCROLL))
8012 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8013 if (infoPtr->rcList.bottom > nHScrollHeight)
8014 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8016 else
8018 if (infoPtr->rcList.bottom > 2)
8019 infoPtr->rcList.bottom -= 2;
8022 else if (uView == LVS_REPORT)
8024 HDLAYOUT hl;
8025 WINDOWPOS wp;
8027 hl.prc = &rcList;
8028 hl.pwpos = &wp;
8029 Header_Layout(infoPtr->hwndHeader, &hl);
8031 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8033 if (!(LVS_NOCOLUMNHEADER & lStyle))
8034 infoPtr->rcList.top = max(wp.cy, 0);
8036 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8039 /***
8040 * DESCRIPTION:
8041 * Processes WM_STYLECHANGED messages.
8043 * PARAMETER(S):
8044 * [I] infoPtr : valid pointer to the listview structure
8045 * [I] WPARAM : window style type (normal or extended)
8046 * [I] LPSTYLESTRUCT : window style information
8048 * RETURN:
8049 * Zero
8051 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8052 LPSTYLESTRUCT lpss)
8054 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8055 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8056 RECT rcList = infoPtr->rcList;
8058 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8059 wStyleType, lpss->styleOld, lpss->styleNew);
8061 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8063 if (wStyleType == GWL_STYLE)
8065 infoPtr->dwStyle = lpss->styleNew;
8067 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8068 ((lpss->styleNew & WS_HSCROLL) == 0))
8069 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8071 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8072 ((lpss->styleNew & WS_VSCROLL) == 0))
8073 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8075 /* If switching modes, then start with no scroll bars and then
8076 * decide.
8078 if (uNewView != uOldView)
8080 if (uOldView == LVS_REPORT)
8081 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8083 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8084 SetRectEmpty(&infoPtr->rcFocus);
8087 if (uNewView == LVS_ICON)
8089 INT oldcx, oldcy;
8091 /* First readjust the iconSize and if necessary the iconSpacing */
8092 oldcx = infoPtr->iconSize.cx;
8093 oldcy = infoPtr->iconSize.cy;
8094 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8095 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8096 if (infoPtr->himlNormal != NULL)
8098 INT cx, cy;
8099 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
8100 infoPtr->iconSize.cx = cx;
8101 infoPtr->iconSize.cy = cy;
8103 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
8105 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8106 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8107 LISTVIEW_SetIconSpacing(infoPtr,0);
8110 /* Now update the full item width and height */
8111 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8112 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8113 if (lpss->styleNew & LVS_ALIGNLEFT)
8114 LISTVIEW_AlignLeft(infoPtr);
8115 else
8116 LISTVIEW_AlignTop(infoPtr);
8118 else if (uNewView == LVS_REPORT)
8120 HDLAYOUT hl;
8121 WINDOWPOS wp;
8123 hl.prc = &rcList;
8124 hl.pwpos = &wp;
8125 Header_Layout(infoPtr->hwndHeader, &hl);
8126 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8127 wp.flags);
8128 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8129 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8131 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8132 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8133 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8134 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8136 else if (uNewView == LVS_LIST)
8138 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8139 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8140 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8141 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8143 else
8145 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8146 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8147 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8148 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8149 if (lpss->styleNew & LVS_ALIGNLEFT)
8150 LISTVIEW_AlignLeft(infoPtr);
8151 else
8152 LISTVIEW_AlignTop(infoPtr);
8155 /* update the size of the client area */
8156 LISTVIEW_UpdateSize(infoPtr);
8158 /* add scrollbars if needed */
8159 LISTVIEW_UpdateScroll(infoPtr);
8161 /* invalidate client area + erase background */
8162 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8164 /* print the list of unsupported window styles */
8165 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8168 /* If they change the view and we have an active edit control
8169 we will need to kill the control since the redraw will
8170 misplace the edit control.
8172 if (infoPtr->bEditing &&
8173 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8174 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8176 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8179 return 0;
8182 /***
8183 * DESCRIPTION:
8184 * Window procedure of the listview control.
8187 static LRESULT WINAPI
8188 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8190 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8192 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8194 if (!infoPtr && (uMsg != WM_CREATE))
8195 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8197 if (infoPtr)
8199 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8202 switch (uMsg)
8204 case LVM_APPROXIMATEVIEWRECT:
8205 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8206 LOWORD(lParam), HIWORD(lParam));
8207 case LVM_ARRANGE:
8208 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8210 /* case LVN_CANCELEDITLABEL */
8212 /* case LVM_CREATEDRAGIMAGE: */
8214 case LVM_DELETEALLITEMS:
8215 return LISTVIEW_DeleteAllItems(infoPtr);
8217 case LVM_DELETECOLUMN:
8218 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8220 case LVM_DELETEITEM:
8221 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8223 case LVM_EDITLABELW:
8224 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8226 case LVM_EDITLABELA:
8227 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8229 /* case LVN_ENABLEGROUPVIEW: */
8231 case LVM_ENSUREVISIBLE:
8232 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8234 case LVM_FINDITEMW:
8235 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8237 case LVM_FINDITEMA:
8238 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8240 case LVM_GETBKCOLOR:
8241 return infoPtr->clrBk;
8243 /* case LVM_GETBKIMAGE: */
8245 case LVM_GETCALLBACKMASK:
8246 return infoPtr->uCallbackMask;
8248 case LVM_GETCOLUMNA:
8249 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8251 case LVM_GETCOLUMNW:
8252 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8254 case LVM_GETCOLUMNORDERARRAY:
8255 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8257 case LVM_GETCOLUMNWIDTH:
8258 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8260 case LVM_GETCOUNTPERPAGE:
8261 return LISTVIEW_GetCountPerPage(infoPtr);
8263 case LVM_GETEDITCONTROL:
8264 return (LRESULT)infoPtr->hwndEdit;
8266 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8267 return infoPtr->dwLvExStyle;
8269 case LVM_GETHEADER:
8270 return (LRESULT)infoPtr->hwndHeader;
8272 case LVM_GETHOTCURSOR:
8273 return (LRESULT)infoPtr->hHotCursor;
8275 case LVM_GETHOTITEM:
8276 return infoPtr->nHotItem;
8278 case LVM_GETHOVERTIME:
8279 return infoPtr->dwHoverTime;
8281 case LVM_GETIMAGELIST:
8282 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8284 /* case LVN_GETINSERTMARK: */
8286 /* case LVN_GETINSERTMARKCOLOR: */
8288 /* case LVN_GETINSERTMARKRECT: */
8290 case LVM_GETISEARCHSTRINGA:
8291 case LVM_GETISEARCHSTRINGW:
8292 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8293 return FALSE;
8295 case LVM_GETITEMA:
8296 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8298 case LVM_GETITEMW:
8299 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8301 case LVM_GETITEMCOUNT:
8302 return infoPtr->nItemCount;
8304 case LVM_GETITEMPOSITION:
8305 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8307 case LVM_GETITEMRECT:
8308 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8310 case LVM_GETITEMSPACING:
8311 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8313 case LVM_GETITEMSTATE:
8314 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8316 case LVM_GETITEMTEXTA:
8317 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8319 case LVM_GETITEMTEXTW:
8320 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8322 case LVM_GETNEXTITEM:
8323 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8325 case LVM_GETNUMBEROFWORKAREAS:
8326 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8327 return 1;
8329 case LVM_GETORIGIN:
8330 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8332 /* case LVN_GETOUTLINECOLOR: */
8334 /* case LVM_GETSELECTEDCOLUMN: */
8336 case LVM_GETSELECTEDCOUNT:
8337 return LISTVIEW_GetSelectedCount(infoPtr);
8339 case LVM_GETSELECTIONMARK:
8340 return infoPtr->nSelectionMark;
8342 case LVM_GETSTRINGWIDTHA:
8343 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8345 case LVM_GETSTRINGWIDTHW:
8346 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8348 case LVM_GETSUBITEMRECT:
8349 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8351 case LVM_GETTEXTBKCOLOR:
8352 return infoPtr->clrTextBk;
8354 case LVM_GETTEXTCOLOR:
8355 return infoPtr->clrText;
8357 /* case LVN_GETTILEINFO: */
8359 /* case LVN_GETTILEVIEWINFO: */
8361 case LVM_GETTOOLTIPS:
8362 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8363 return FALSE;
8365 case LVM_GETTOPINDEX:
8366 return LISTVIEW_GetTopIndex(infoPtr);
8368 /*case LVM_GETUNICODEFORMAT:
8369 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8370 return FALSE;*/
8372 case LVM_GETVIEWRECT:
8373 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8375 case LVM_GETWORKAREAS:
8376 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8377 return FALSE;
8379 /* case LVN_HASGROUP: */
8381 case LVM_HITTEST:
8382 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8384 case LVM_INSERTCOLUMNA:
8385 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8387 case LVM_INSERTCOLUMNW:
8388 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8390 /* case LVN_INSERTGROUP: */
8392 /* case LVN_INSERTGROUPSORTED: */
8394 case LVM_INSERTITEMA:
8395 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8397 case LVM_INSERTITEMW:
8398 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8400 /* case LVN_INSERTMARKHITTEST: */
8402 /* case LVN_ISGROUPVIEWENABLED: */
8404 /* case LVN_MAPIDTOINDEX: */
8406 /* case LVN_INEDXTOID: */
8408 /* case LVN_MOVEGROUP: */
8410 /* case LVN_MOVEITEMTOGROUP: */
8412 case LVM_REDRAWITEMS:
8413 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8415 /* case LVN_REMOVEALLGROUPS: */
8417 /* case LVN_REMOVEGROUP: */
8419 case LVM_SCROLL:
8420 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8422 case LVM_SETBKCOLOR:
8423 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8425 /* case LVM_SETBKIMAGE: */
8427 case LVM_SETCALLBACKMASK:
8428 infoPtr->uCallbackMask = (UINT)wParam;
8429 return TRUE;
8431 case LVM_SETCOLUMNA:
8432 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8434 case LVM_SETCOLUMNW:
8435 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8437 case LVM_SETCOLUMNORDERARRAY:
8438 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8440 case LVM_SETCOLUMNWIDTH:
8441 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8443 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8444 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8446 /* case LVN_SETGROUPINFO: */
8448 /* case LVN_SETGROUPMETRICS: */
8450 case LVM_SETHOTCURSOR:
8451 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8453 case LVM_SETHOTITEM:
8454 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8456 case LVM_SETHOVERTIME:
8457 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8459 case LVM_SETICONSPACING:
8460 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8462 case LVM_SETIMAGELIST:
8463 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8465 /* case LVN_SETINFOTIP: */
8467 /* case LVN_SETINSERTMARK: */
8469 /* case LVN_SETINSERTMARKCOLOR: */
8471 case LVM_SETITEMA:
8472 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8474 case LVM_SETITEMW:
8475 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8477 case LVM_SETITEMCOUNT:
8478 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8480 case LVM_SETITEMPOSITION:
8482 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8483 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8486 case LVM_SETITEMPOSITION32:
8487 if (lParam == 0) return FALSE;
8488 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8490 case LVM_SETITEMSTATE:
8491 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8493 case LVM_SETITEMTEXTA:
8494 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8496 case LVM_SETITEMTEXTW:
8497 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8499 /* case LVN_SETOUTLINECOLOR: */
8501 /* case LVN_SETSELECTEDCOLUMN: */
8503 case LVM_SETSELECTIONMARK:
8504 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8506 case LVM_SETTEXTBKCOLOR:
8507 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8509 case LVM_SETTEXTCOLOR:
8510 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8512 /* case LVN_SETTILEINFO: */
8514 /* case LVN_SETTILEVIEWINFO: */
8516 /* case LVN_SETTILEWIDTH: */
8518 /* case LVM_SETTOOLTIPS: */
8520 /* case LVM_SETUNICODEFORMAT: */
8522 /* case LVN_SETVIEW: */
8524 /* case LVM_SETWORKAREAS: */
8526 /* case LVN_SORTGROUPS: */
8528 case LVM_SORTITEMS:
8529 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8531 case LVM_SUBITEMHITTEST:
8532 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8534 case LVM_UPDATE:
8535 return LISTVIEW_Update(infoPtr, (INT)wParam);
8537 case WM_CHAR:
8538 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8540 case WM_COMMAND:
8541 return LISTVIEW_Command(infoPtr, wParam, lParam);
8543 case WM_CREATE:
8544 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8546 case WM_ERASEBKGND:
8547 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8549 case WM_GETDLGCODE:
8550 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8552 case WM_GETFONT:
8553 return infoPtr->hFont;
8555 case WM_HSCROLL:
8556 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8558 case WM_KEYDOWN:
8559 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8561 case WM_KILLFOCUS:
8562 return LISTVIEW_KillFocus(infoPtr);
8564 case WM_LBUTTONDBLCLK:
8565 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8567 case WM_LBUTTONDOWN:
8568 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8570 case WM_LBUTTONUP:
8571 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8573 case WM_MOUSEMOVE:
8574 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8576 case WM_MOUSEHOVER:
8577 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8579 case WM_NCDESTROY:
8580 return LISTVIEW_NCDestroy(infoPtr);
8582 case WM_NOTIFY:
8583 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8585 case WM_NOTIFYFORMAT:
8586 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8588 case WM_PAINT:
8589 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8591 case WM_RBUTTONDBLCLK:
8592 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8594 case WM_RBUTTONDOWN:
8595 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8597 case WM_RBUTTONUP:
8598 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8600 case WM_SETCURSOR:
8601 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8602 return TRUE;
8603 goto fwd_msg;
8605 case WM_SETFOCUS:
8606 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8608 case WM_SETFONT:
8609 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8611 case WM_SETREDRAW:
8612 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8614 case WM_SIZE:
8615 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8617 case WM_STYLECHANGED:
8618 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8620 case WM_SYSCOLORCHANGE:
8621 COMCTL32_RefreshSysColors();
8622 return 0;
8624 /* case WM_TIMER: */
8626 case WM_VSCROLL:
8627 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8629 case WM_MOUSEWHEEL:
8630 if (wParam & (MK_SHIFT | MK_CONTROL))
8631 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8632 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8634 case WM_WINDOWPOSCHANGED:
8635 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8636 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8637 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8638 LISTVIEW_UpdateSize(infoPtr);
8639 LISTVIEW_UpdateScroll(infoPtr);
8641 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8643 /* case WM_WININICHANGE: */
8645 default:
8646 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8647 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8649 fwd_msg:
8650 /* call default window procedure */
8651 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8654 return 0;
8657 /***
8658 * DESCRIPTION:
8659 * Registers the window class.
8661 * PARAMETER(S):
8662 * None
8664 * RETURN:
8665 * None
8667 void LISTVIEW_Register(void)
8669 WNDCLASSW wndClass;
8671 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8672 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8673 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8674 wndClass.cbClsExtra = 0;
8675 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8676 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8677 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8678 wndClass.lpszClassName = WC_LISTVIEWW;
8679 RegisterClassW(&wndClass);
8682 /***
8683 * DESCRIPTION:
8684 * Unregisters the window class.
8686 * PARAMETER(S):
8687 * None
8689 * RETURN:
8690 * None
8692 void LISTVIEW_Unregister(void)
8694 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8697 /***
8698 * DESCRIPTION:
8699 * Handle any WM_COMMAND messages
8701 * PARAMETER(S):
8703 * RETURN:
8705 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8707 switch (HIWORD(wParam))
8709 case EN_UPDATE:
8712 * Adjust the edit window size
8714 WCHAR buffer[1024];
8715 HDC hdc = GetDC(infoPtr->hwndEdit);
8716 HFONT hFont, hOldFont = 0;
8717 RECT rect;
8718 SIZE sz;
8719 int len;
8721 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8722 GetWindowRect(infoPtr->hwndEdit, &rect);
8724 /* Select font to get the right dimension of the string */
8725 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8726 if(hFont != 0)
8728 hOldFont = SelectObject(hdc, hFont);
8731 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8733 TEXTMETRICW textMetric;
8735 /* Add Extra spacing for the next character */
8736 GetTextMetricsW(hdc, &textMetric);
8737 sz.cx += (textMetric.tmMaxCharWidth * 2);
8739 SetWindowPos (
8740 infoPtr->hwndEdit,
8741 HWND_TOP,
8744 sz.cx,
8745 rect.bottom - rect.top,
8746 SWP_DRAWFRAME|SWP_NOMOVE);
8748 if(hFont != 0)
8749 SelectObject(hdc, hOldFont);
8751 ReleaseDC(infoPtr->hwndSelf, hdc);
8753 break;
8756 default:
8757 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8760 return 0;
8764 /***
8765 * DESCRIPTION:
8766 * Subclassed edit control windproc function
8768 * PARAMETER(S):
8770 * RETURN:
8772 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
8773 WPARAM wParam, LPARAM lParam, BOOL isW)
8775 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8776 static BOOL bIgnoreKillFocus = FALSE;
8777 BOOL cancel = FALSE;
8779 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8780 hwnd, uMsg, wParam, lParam, isW);
8782 switch (uMsg)
8784 case WM_GETDLGCODE:
8785 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8787 case WM_KILLFOCUS:
8788 if(bIgnoreKillFocus) return TRUE;
8789 break;
8791 case WM_DESTROY:
8793 WNDPROC editProc = infoPtr->EditWndProc;
8794 infoPtr->EditWndProc = 0;
8795 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
8796 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
8799 case WM_KEYDOWN:
8800 if (VK_ESCAPE == (INT)wParam)
8802 cancel = TRUE;
8803 break;
8805 else if (VK_RETURN == (INT)wParam)
8806 break;
8808 default:
8809 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
8812 if (infoPtr->bEditing)
8814 LPWSTR buffer = NULL;
8816 if (!cancel)
8818 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
8820 if (len)
8822 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
8824 if (isW) GetWindowTextW(hwnd, buffer, len+1);
8825 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
8829 /* Processing LVN_ENDLABELEDIT message could kill the focus */
8830 /* eg. Using a messagebox */
8831 bIgnoreKillFocus = TRUE;
8832 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
8834 if (buffer) COMCTL32_Free(buffer);
8836 bIgnoreKillFocus = FALSE;
8839 SendMessageW(hwnd, WM_CLOSE, 0, 0);
8840 return TRUE;
8843 /***
8844 * DESCRIPTION:
8845 * Subclassed edit control windproc function
8847 * PARAMETER(S):
8849 * RETURN:
8851 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8853 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
8856 /***
8857 * DESCRIPTION:
8858 * Subclassed edit control windproc function
8860 * PARAMETER(S):
8862 * RETURN:
8864 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8866 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
8869 /***
8870 * DESCRIPTION:
8871 * Creates a subclassed edit cotrol
8873 * PARAMETER(S):
8875 * RETURN:
8877 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
8878 INT x, INT y, INT width, INT height, BOOL isW)
8880 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
8881 HWND hedit;
8882 SIZE sz;
8883 HDC hdc;
8884 HDC hOldFont=0;
8885 TEXTMETRICW textMetric;
8886 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
8888 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
8890 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
8891 hdc = GetDC(infoPtr->hwndSelf);
8893 /* Select the font to get appropriate metric dimensions */
8894 if(infoPtr->hFont != 0)
8895 hOldFont = SelectObject(hdc, infoPtr->hFont);
8897 /*Get String Lenght in pixels */
8898 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
8900 /*Add Extra spacing for the next character */
8901 GetTextMetricsW(hdc, &textMetric);
8902 sz.cx += (textMetric.tmMaxCharWidth * 2);
8904 if(infoPtr->hFont != 0)
8905 SelectObject(hdc, hOldFont);
8907 ReleaseDC(infoPtr->hwndSelf, hdc);
8908 if (isW)
8909 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8910 else
8911 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8913 if (!hedit) return 0;
8915 infoPtr->EditWndProc = (WNDPROC)
8916 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
8917 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
8919 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
8921 return hedit;