Get rid of 'goto's in GetItemMeasures.
[wine/testsucceed.git] / dlls / comctl32 / listview.c
blobb9b109fcf27f8a52e528e256168760350e28f205
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.
31 * Notifications:
32 * LISTVIEW_Notify : most notifications from children (editbox and header)
34 * Data structure:
35 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
37 * Advanced functionality:
38 * LISTVIEW_GetNumberOfWorkAreas : not implemented
39 * LISTVIEW_GetISearchString : not implemented
40 * LISTVIEW_GetBkImage : not implemented
41 * LISTVIEW_SetBkImage : not implemented
42 * LISTVIEW_GetColumnOrderArray : simple hack only
43 * LISTVIEW_SetColumnOrderArray : simple hack only
44 * LISTVIEW_Arrange : empty stub
45 * LISTVIEW_ApproximateViewRect : incomplete
46 * LISTVIEW_Update : not completed
48 * Known differences in message stream from native control (not known if
49 * these differences cause problems):
50 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
51 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
52 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
53 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
54 * does *not* invoke DefWindowProc
55 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
56 * processing for "USEDOUBLECLICKTIME".
60 #include "config.h"
61 #include "wine/port.h"
63 #include <ctype.h>
64 #include <string.h>
65 #include <stdlib.h>
66 #include <stdio.h>
68 #include "winbase.h"
69 #include "winnt.h"
70 #include "heap.h"
71 #include "commctrl.h"
72 #include "comctl32.h"
73 #include "wine/debug.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(listview);
77 /* Some definitions for inline edit control */
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 tagLISTVIEW_INFO
114 HWND hwndSelf;
115 HBRUSH hBkBrush;
116 COLORREF clrBk;
117 COLORREF clrText;
118 COLORREF clrTextBk;
119 HIMAGELIST himlNormal;
120 HIMAGELIST himlSmall;
121 HIMAGELIST himlState;
122 BOOL bLButtonDown;
123 BOOL bRButtonDown;
124 INT nItemHeight;
125 INT nItemWidth;
126 HDPA hdpaSelectionRanges;
127 INT nSelectionMark;
128 BOOL bRemovingAllSelections;
129 INT nHotItem;
130 SHORT notifyFormat;
131 RECT rcList; /* This rectangle is really the window
132 * client rectangle possibly reduced by the
133 * horizontal scroll bar and/or header - see
134 * LISTVIEW_UpdateSize. This rectangle offset
135 * by the LISTVIEW_GetOrigin value is in
136 * client coordinates */
137 RECT rcView; /* This rectangle contains all items -
138 * contructed in LISTVIEW_AlignTop and
139 * LISTVIEW_AlignLeft */
140 SIZE iconSize;
141 SIZE iconSpacing;
142 SIZE iconStateSize;
143 UINT uCallbackMask;
144 HWND hwndHeader;
145 HFONT hDefaultFont;
146 HCURSOR hHotCursor;
147 HFONT hFont;
148 INT ntmHeight; /* from GetTextMetrics from above font */
149 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
150 BOOL bFocus;
151 INT nFocusedItem;
152 RECT rcFocus;
153 DWORD dwStyle; /* the cached window GWL_STYLE */
154 DWORD dwLvExStyle; /* extended listview style */
155 INT nItemCount;
156 HDPA hdpaItems;
157 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
158 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
159 PFNLVCOMPARE pfnCompare;
160 LPARAM lParamSort;
161 HWND hwndEdit;
162 BOOL bEditing;
163 WNDPROC EditWndProc;
164 INT nEditLabelItem;
165 DWORD dwHoverTime;
167 DWORD lastKeyPressTimestamp;
168 WPARAM charCode;
169 INT nSearchParamLength;
170 WCHAR szSearchParam[ MAX_PATH ];
171 BOOL bIsDrawing;
172 } LISTVIEW_INFO;
174 DEFINE_COMMON_NOTIFICATIONS(LISTVIEW_INFO, hwndSelf);
177 * constants
179 /* How many we debug buffer to allocate */
180 #define DEBUG_BUFFERS 20
181 /* The size of a single debug bbuffer */
182 #define DEBUG_BUFFER_SIZE 256
184 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
185 #define SB_INTERNAL -1
187 /* maximum size of a label */
188 #define DISP_TEXT_SIZE 512
190 /* padding for items in list and small icon display modes */
191 #define WIDTH_PADDING 12
193 /* padding for items in list, report and small icon display modes */
194 #define HEIGHT_PADDING 1
196 /* offset of items in report display mode */
197 #define REPORT_MARGINX 2
199 /* padding for icon in large icon display mode
200 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
201 * that HITTEST will see.
202 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
203 * ICON_TOP_PADDING - sum of the two above.
204 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
205 * LABEL_VERT_PADDING - between bottom of text and end of box
207 * ICON_LR_PADDING - additional width above icon size.
208 * ICON_LR_HALF - half of the above value
210 #define ICON_TOP_PADDING_NOTHITABLE 2
211 #define ICON_TOP_PADDING_HITABLE 2
212 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
213 #define ICON_BOTTOM_PADDING 4
214 #define LABEL_VERT_PADDING 7
215 #define ICON_LR_PADDING 16
216 #define ICON_LR_HALF (ICON_LR_PADDING/2)
218 /* default label width for items in list and small icon display modes */
219 #define DEFAULT_LABEL_WIDTH 40
221 /* default column width for items in list display mode */
222 #define DEFAULT_COLUMN_WIDTH 128
224 /* Size of "line" scroll for V & H scrolls */
225 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
227 /* Padding betwen image and label */
228 #define IMAGE_PADDING 2
230 /* Padding behind the label */
231 #define TRAILING_PADDING 5
233 /* Border for the icon caption */
234 #define CAPTION_BORDER 2
236 /* Standard DrawText flags for LISTVIEW_UpdateLargeItemLabelRect and LISTVIEW_DrawLargeItem */
237 #define LISTVIEW_DTFLAGS DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL
239 /* Dump the LISTVIEW_INFO structure to the debug channel */
240 #define LISTVIEW_DUMP(iP) do { \
241 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
242 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
243 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
244 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
245 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
246 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
247 (iP->bFocus) ? "true" : "false"); \
248 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
249 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
250 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
251 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
252 iP->hwndSelf, \
253 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
254 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
255 } while(0)
259 * forward declarations
261 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
262 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
263 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
264 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
265 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
266 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *);
267 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
268 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
269 static INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *);
270 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *, INT, LPRECT);
271 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
272 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *, INT);
273 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
274 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
275 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
276 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
277 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, POINT);
278 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
279 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
280 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
281 static void LISTVIEW_UnsupportedStyles(LONG);
282 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
283 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
284 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
285 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
286 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *, WPARAM, LPARAM);
287 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
288 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
289 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
290 static BOOL LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *, int, RECT*);
291 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
292 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
293 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
294 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
295 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
296 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
298 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
299 #define KEY_DELAY 450
301 /******** Text handling functions *************************************/
303 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
304 * text string. The string may be ANSI or Unicode, in which case
305 * the boolean isW tells us the type of the string.
307 * The name of the function tell what type of strings it expects:
308 * W: Unicode, T: ANSI/Unicode - function of isW
311 static inline BOOL is_textW(LPCWSTR text)
313 return text != NULL && text != LPSTR_TEXTCALLBACKW;
316 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
318 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
319 return is_textW(text);
322 static inline int textlenT(LPCWSTR text, BOOL isW)
324 return !is_textT(text, isW) ? 0 :
325 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
328 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
330 if (isDestW)
331 if (isSrcW) lstrcpynW(dest, src, max);
332 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
333 else
334 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
335 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
338 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
340 LPWSTR wstr = (LPWSTR)text;
342 if (!isW && is_textT(text, isW))
344 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
345 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
346 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
348 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
349 return wstr;
352 static inline void textfreeT(LPWSTR wstr, BOOL isW)
354 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
358 * dest is a pointer to a Unicode string
359 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
361 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
363 BOOL bResult = TRUE;
365 if (src == LPSTR_TEXTCALLBACKW)
367 if (is_textW(*dest)) COMCTL32_Free(*dest);
368 *dest = LPSTR_TEXTCALLBACKW;
370 else
372 LPWSTR pszText = textdupTtoW(src, isW);
373 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
374 bResult = Str_SetPtrW(dest, pszText);
375 textfreeT(pszText, isW);
377 return bResult;
381 * compares a Unicode to a Unicode/ANSI text string
383 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
385 if (!aw) return bt ? -1 : 0;
386 if (!bt) return aw ? 1 : 0;
387 if (aw == LPSTR_TEXTCALLBACKW)
388 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
389 if (bt != LPSTR_TEXTCALLBACKW)
391 LPWSTR bw = textdupTtoW(bt, isW);
392 int r = bw ? lstrcmpW(aw, bw) : 1;
393 textfreeT(bw, isW);
394 return r;
397 return 1;
400 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
402 int res;
404 n = min(min(n, strlenW(s1)), strlenW(s2));
405 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
406 return res ? res - sizeof(WCHAR) : res;
409 /******** Debugging functions *****************************************/
411 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
413 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
414 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
417 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
419 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
420 n = min(textlenT(text, isW), n);
421 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
424 static char* debug_getbuf()
426 static int index = 0;
427 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
428 return buffers[index++ % DEBUG_BUFFERS];
431 static inline char* debugpoint(const POINT* lppt)
433 if (lppt)
435 char* buf = debug_getbuf();
436 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
437 return buf;
438 } else return "(null)";
441 static inline char* debugrect(const RECT* rect)
443 if (rect)
445 char* buf = debug_getbuf();
446 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
447 rect->left, rect->top, rect->right, rect->bottom);
448 return buf;
449 } else return "(null)";
452 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
454 char* buf = debug_getbuf(), *text = buf;
455 int len, size = DEBUG_BUFFER_SIZE;
457 if (lpLVItem == NULL) return "(null)";
458 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
459 if (len == -1) goto end; buf += len; size -= len;
460 if (lpLVItem->mask & LVIF_STATE)
461 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
462 else len = 0;
463 if (len == -1) goto end; buf += len; size -= len;
464 if (lpLVItem->mask & LVIF_TEXT)
465 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
466 else len = 0;
467 if (len == -1) goto end; buf += len; size -= len;
468 if (lpLVItem->mask & LVIF_IMAGE)
469 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
470 else len = 0;
471 if (len == -1) goto end; buf += len; size -= len;
472 if (lpLVItem->mask & LVIF_PARAM)
473 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
474 else len = 0;
475 if (len == -1) goto end; buf += len; size -= len;
476 if (lpLVItem->mask & LVIF_INDENT)
477 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
478 else len = 0;
479 if (len == -1) goto end; buf += len; size -= len;
480 goto undo;
481 end:
482 buf = text + strlen(text);
483 undo:
484 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
485 return text;
488 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
490 char* buf = debug_getbuf(), *text = buf;
491 int len, size = DEBUG_BUFFER_SIZE;
493 if (lpColumn == NULL) return "(null)";
494 len = snprintf(buf, size, "{");
495 if (len == -1) goto end; buf += len; size -= len;
496 if (lpColumn->mask & LVCF_SUBITEM)
497 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
498 else len = 0;
499 if (len == -1) goto end; buf += len; size -= len;
500 if (lpColumn->mask & LVCF_FMT)
501 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
502 else len = 0;
503 if (len == -1) goto end; buf += len; size -= len;
504 if (lpColumn->mask & LVCF_WIDTH)
505 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
506 else len = 0;
507 if (len == -1) goto end; buf += len; size -= len;
508 if (lpColumn->mask & LVCF_TEXT)
509 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
510 else len = 0;
511 if (len == -1) goto end; buf += len; size -= len;
512 if (lpColumn->mask & LVCF_IMAGE)
513 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
514 else len = 0;
515 if (len == -1) goto end; buf += len; size -= len;
516 if (lpColumn->mask & LVCF_ORDER)
517 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
518 else len = 0;
519 if (len == -1) goto end; buf += len; size -= len;
520 goto undo;
521 end:
522 buf = text + strlen(text);
523 undo:
524 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
525 return text;
529 /******** Notification functions i************************************/
531 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
533 pnmh->hwndFrom = infoPtr->hwndSelf;
534 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
535 pnmh->code = code;
536 return (BOOL)SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
537 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
540 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
542 NMHDR nmh;
543 notify(infoPtr, LVN_ITEMACTIVATE, &nmh);
546 static inline BOOL notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
548 return notify(infoPtr, code, (LPNMHDR)plvnm);
551 static int get_ansi_notification(INT unicodeNotificationCode)
553 switch (unicodeNotificationCode)
555 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
556 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
557 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
558 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
559 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
560 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
562 ERR("unknown notification %x\n", unicodeNotificationCode);
563 return unicodeNotificationCode;
567 Send notification. depends on dispinfoW having same
568 structure as dispinfoA.
569 infoPtr : listview struct
570 notificationCode : *Unicode* notification code
571 pdi : dispinfo structure (can be unicode or ansi)
572 isW : TRUE if dispinfo is Unicode
574 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
576 BOOL bResult = FALSE;
577 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
578 INT realNotifCode;
579 INT cchTempBufMax = 0, savCchTextMax = 0;
580 LPWSTR pszTempBuf = NULL, savPszText = NULL;
582 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
584 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
585 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
588 if (convertToAnsi || convertToUnicode)
590 if (notificationCode != LVN_GETDISPINFOW)
592 cchTempBufMax = convertToUnicode ?
593 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
594 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
596 else
598 cchTempBufMax = pdi->item.cchTextMax;
599 *pdi->item.pszText = 0; /* make sure we don't process garbage */
602 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
603 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
604 if (!pszTempBuf) return FALSE;
605 if (convertToUnicode)
606 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
607 pszTempBuf, cchTempBufMax);
608 else
609 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
610 cchTempBufMax, NULL, NULL);
611 savCchTextMax = pdi->item.cchTextMax;
612 savPszText = pdi->item.pszText;
613 pdi->item.pszText = pszTempBuf;
614 pdi->item.cchTextMax = cchTempBufMax;
617 if (infoPtr->notifyFormat == NFR_ANSI)
618 realNotifCode = get_ansi_notification(notificationCode);
619 else
620 realNotifCode = notificationCode;
621 bResult = notify(infoPtr, realNotifCode, (LPNMHDR)pdi);
623 if (convertToUnicode || convertToAnsi)
625 if (convertToUnicode) /* note : pointer can be changed by app ! */
626 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
627 savCchTextMax, NULL, NULL);
628 else
629 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
630 savPszText, savCchTextMax);
631 pdi->item.pszText = savPszText; /* restores our buffer */
632 pdi->item.cchTextMax = savCchTextMax;
633 HeapFree(GetProcessHeap(), 0, pszTempBuf);
635 return bResult;
638 static inline void notify_odcachehint(LISTVIEW_INFO *infoPtr, INT iFrom, INT iTo)
640 NMLVCACHEHINT nmlv;
642 nmlv.iFrom = iFrom;
643 nmlv.iTo = iTo;
644 notify(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
647 static BOOL notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, HDC hdc, RECT rc)
649 NMLVCUSTOMDRAW nmlvcd;
651 TRACE("(dwDrawStage=%lx, hdc=%x, rc=?)\n", dwDrawStage, hdc);
653 nmlvcd.nmcd.dwDrawStage = dwDrawStage;
654 nmlvcd.nmcd.hdc = hdc;
655 nmlvcd.nmcd.rc = rc;
656 nmlvcd.nmcd.dwItemSpec = 0;
657 nmlvcd.nmcd.uItemState = 0;
658 nmlvcd.nmcd.lItemlParam = 0;
659 nmlvcd.clrText = infoPtr->clrText;
660 nmlvcd.clrTextBk = infoPtr->clrBk;
662 return (BOOL)notify(infoPtr, NM_CUSTOMDRAW, &nmlvcd.nmcd.hdr);
665 /* FIXME: we should inline this where it's called somehow
666 * I think we need to pass in the structure
668 static BOOL notify_customdrawitem (LISTVIEW_INFO *infoPtr, HDC hdc, UINT iItem, UINT iSubItem, UINT uItemDrawState)
670 NMLVCUSTOMDRAW nmlvcd;
671 UINT uItemState;
672 RECT itemRect;
673 LVITEMW item;
674 BOOL bReturn;
676 item.iItem = iItem;
677 item.iSubItem = 0;
678 item.mask = LVIF_PARAM;
679 if (!LISTVIEW_GetItemT(infoPtr, &item, TRUE)) return FALSE;
681 uItemState = 0;
683 if (LISTVIEW_GetItemState(infoPtr, iItem, LVIS_SELECTED)) uItemState |= CDIS_SELECTED;
684 if (LISTVIEW_GetItemState(infoPtr, iItem, LVIS_FOCUSED)) uItemState |= CDIS_FOCUS;
685 if (iItem == infoPtr->nHotItem) uItemState |= CDIS_HOT;
687 itemRect.left = LVIR_BOUNDS;
688 LISTVIEW_GetItemRect(infoPtr, iItem, &itemRect);
690 nmlvcd.nmcd.dwDrawStage = CDDS_ITEM | uItemDrawState;
691 nmlvcd.nmcd.hdc = hdc;
692 nmlvcd.nmcd.rc = itemRect;
693 nmlvcd.nmcd.dwItemSpec = iItem;
694 nmlvcd.nmcd.uItemState = uItemState;
695 nmlvcd.nmcd.lItemlParam = item.lParam;
696 nmlvcd.clrText = infoPtr->clrText;
697 nmlvcd.clrTextBk = infoPtr->clrBk;
698 nmlvcd.iSubItem = iSubItem;
700 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
701 nmlvcd.nmcd.dwDrawStage, nmlvcd.nmcd.hdc, nmlvcd.nmcd.dwItemSpec,
702 nmlvcd.nmcd.uItemState, nmlvcd.nmcd.lItemlParam);
704 bReturn = notify(infoPtr, NM_CUSTOMDRAW, &nmlvcd.nmcd.hdr);
706 infoPtr->clrText = nmlvcd.clrText;
707 infoPtr->clrBk = nmlvcd.clrTextBk;
709 return bReturn;
712 /******** Misc helper functions ************************************/
714 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
715 WPARAM wParam, LPARAM lParam, BOOL isW)
717 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
718 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
721 /******** Internal API functions ************************************/
723 /* The Invalidate* are macros, so we preserve the caller location */
724 #define LISTVIEW_InvalidateRect(infoPtr, rect) do { \
725 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
726 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
727 } while (0)
729 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
730 RECT rcItem; \
731 rcItem.left = LVIR_BOUNDS; \
732 if(LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) \
733 LISTVIEW_InvalidateRect(infoPtr, &rcItem); \
734 } while (0)
736 #define LISTVIEW_InvalidateList(infoPtr)\
737 LISTVIEW_InvalidateRect(infoPtr, NULL)
739 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
741 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
744 static inline int LISTVIEW_GetType(LISTVIEW_INFO *infoPtr)
746 return infoPtr->dwStyle & LVS_TYPEMASK;
749 /***
750 * DESCRIPTION:
751 * Retrieves the number of items that can fit vertically in the client area.
753 * PARAMETER(S):
754 * [I] infoPtr : valid pointer to the listview structure
756 * RETURN:
757 * Number of items per row.
759 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
761 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
763 return max(nListWidth/infoPtr->nItemWidth, 1);
766 /***
767 * DESCRIPTION:
768 * Retrieves the number of items that can fit horizontally in the client
769 * area.
771 * PARAMETER(S):
772 * [I] infoPtr : valid pointer to the listview structure
774 * RETURN:
775 * Number of items per column.
777 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
779 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
781 return max(nListHeight / infoPtr->nItemHeight, 1);
784 /***
785 * DESCRIPTION:
786 * Retrieves the range of visible items. Note that the upper limit
787 * may be a bit larger than the actual last visible item.
789 * PARAMETER(S):
790 * [I] infoPtr : valid pointer to the listview structure
792 * RETURN:
793 * maximum range of visible items
795 static RANGE LISTVIEW_GetVisibleRange(LISTVIEW_INFO *infoPtr)
797 UINT uView = LISTVIEW_GetType(infoPtr);
798 INT nPerCol, nPerRow;
799 RANGE visrange;
801 visrange.lower = LISTVIEW_GetTopIndex(infoPtr);
803 if (uView == LVS_REPORT)
805 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
806 nPerRow = 1;
808 else if (uView == LVS_LIST)
810 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
811 nPerRow = LISTVIEW_GetCountPerRow(infoPtr);
813 else
815 /* FIXME: this is correct only in autoarrange mode */
816 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
817 nPerRow = LISTVIEW_GetCountPerRow(infoPtr) + 1;
820 visrange.upper = visrange.lower + nPerCol * nPerRow;
821 if (visrange.upper > infoPtr->nItemCount)
822 visrange.upper = infoPtr->nItemCount;
824 TRACE("range=(%d, %d)\n", visrange.lower, visrange.upper);
826 return visrange;
830 /*************************************************************************
831 * LISTVIEW_ProcessLetterKeys
833 * Processes keyboard messages generated by pressing the letter keys
834 * on the keyboard.
835 * What this does is perform a case insensitive search from the
836 * current position with the following quirks:
837 * - If two chars or more are pressed in quick succession we search
838 * for the corresponding string (e.g. 'abc').
839 * - If there is a delay we wipe away the current search string and
840 * restart with just that char.
841 * - If the user keeps pressing the same character, whether slowly or
842 * fast, so that the search string is entirely composed of this
843 * character ('aaaaa' for instance), then we search for first item
844 * that starting with that character.
845 * - If the user types the above character in quick succession, then
846 * we must also search for the corresponding string ('aaaaa'), and
847 * go to that string if there is a match.
849 * PARAMETERS
850 * [I] hwnd : handle to the window
851 * [I] charCode : the character code, the actual character
852 * [I] keyData : key data
854 * RETURNS
856 * Zero.
858 * BUGS
860 * - The current implementation has a list of characters it will
861 * accept and it ignores averything else. In particular it will
862 * ignore accentuated characters which seems to match what
863 * Windows does. But I'm not sure it makes sense to follow
864 * Windows there.
865 * - We don't sound a beep when the search fails.
867 * SEE ALSO
869 * TREEVIEW_ProcessLetterKeys
871 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
873 INT nItem;
874 INT endidx,idx;
875 LVITEMW item;
876 WCHAR buffer[MAX_PATH];
877 DWORD timestamp,elapsed;
879 /* simple parameter checking */
880 if (!charCode || !keyData) return 0;
882 /* only allow the valid WM_CHARs through */
883 if (!isalnum(charCode) &&
884 charCode != '.' && charCode != '`' && charCode != '!' &&
885 charCode != '@' && charCode != '#' && charCode != '$' &&
886 charCode != '%' && charCode != '^' && charCode != '&' &&
887 charCode != '*' && charCode != '(' && charCode != ')' &&
888 charCode != '-' && charCode != '_' && charCode != '+' &&
889 charCode != '=' && charCode != '\\'&& charCode != ']' &&
890 charCode != '}' && charCode != '[' && charCode != '{' &&
891 charCode != '/' && charCode != '?' && charCode != '>' &&
892 charCode != '<' && charCode != ',' && charCode != '~')
893 return 0;
895 /* if there's one item or less, there is no where to go */
896 if (infoPtr->nItemCount <= 1) return 0;
898 /* compute how much time elapsed since last keypress */
899 timestamp=GetTickCount();
900 if (timestamp > infoPtr->lastKeyPressTimestamp) {
901 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
902 } else {
903 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
906 /* update the search parameters */
907 infoPtr->lastKeyPressTimestamp=timestamp;
908 if (elapsed < KEY_DELAY) {
909 if (infoPtr->nSearchParamLength < MAX_PATH) {
910 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
912 if (infoPtr->charCode != charCode) {
913 infoPtr->charCode=charCode=0;
915 } else {
916 infoPtr->charCode=charCode;
917 infoPtr->szSearchParam[0]=charCode;
918 infoPtr->nSearchParamLength=1;
919 /* Redundant with the 1 char string */
920 charCode=0;
923 /* and search from the current position */
924 nItem=-1;
925 if (infoPtr->nFocusedItem >= 0) {
926 endidx=infoPtr->nFocusedItem;
927 idx=endidx;
928 /* if looking for single character match,
929 * then we must always move forward
931 if (infoPtr->nSearchParamLength == 1)
932 idx++;
933 } else {
934 endidx=infoPtr->nItemCount;
935 idx=0;
937 do {
938 if (idx == infoPtr->nItemCount) {
939 if (endidx == infoPtr->nItemCount || endidx == 0)
940 break;
941 idx=0;
944 /* get item */
945 item.mask = LVIF_TEXT;
946 item.iItem = idx;
947 item.iSubItem = 0;
948 item.pszText = buffer;
949 item.cchTextMax = MAX_PATH;
950 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
952 /* check for a match */
953 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
954 nItem=idx;
955 break;
956 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
957 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
958 /* This would work but we must keep looking for a longer match */
959 nItem=idx;
961 idx++;
962 } while (idx != endidx);
964 if (nItem != -1)
965 LISTVIEW_KeySelection(infoPtr, nItem);
967 return 0;
970 /*************************************************************************
971 * LISTVIEW_UpdateHeaderSize [Internal]
973 * Function to resize the header control
975 * PARAMS
976 * hwnd [I] handle to a window
977 * nNewScrollPos [I] Scroll Pos to Set
979 * RETURNS
980 * nothing
982 * NOTES
984 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
986 RECT winRect;
987 POINT point[2];
989 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
991 GetWindowRect(infoPtr->hwndHeader, &winRect);
992 point[0].x = winRect.left;
993 point[0].y = winRect.top;
994 point[1].x = winRect.right;
995 point[1].y = winRect.bottom;
997 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
998 point[0].x = -nNewScrollPos;
999 point[1].x += nNewScrollPos;
1001 SetWindowPos(infoPtr->hwndHeader,0,
1002 point[0].x,point[0].y,point[1].x,point[1].y,
1003 SWP_NOZORDER | SWP_NOACTIVATE);
1006 /***
1007 * DESCRIPTION:
1008 * Update the scrollbars. This functions should be called whenever
1009 * the content, size or view changes.
1011 * PARAMETER(S):
1012 * [I] infoPtr : valid pointer to the listview structure
1014 * RETURN:
1015 * None
1017 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1019 LONG lStyle = infoPtr->dwStyle;
1020 UINT uView = lStyle & LVS_TYPEMASK;
1021 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1022 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1023 SCROLLINFO scrollInfo;
1025 if (lStyle & LVS_NOSCROLL) return;
1027 scrollInfo.cbSize = sizeof(SCROLLINFO);
1029 if (uView == LVS_LIST)
1031 /* update horizontal scrollbar */
1032 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1033 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1035 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1036 infoPtr->nItemCount, nCountPerColumn, nCountPerRow);
1038 scrollInfo.nMin = 0;
1039 scrollInfo.nMax = infoPtr->nItemCount / nCountPerColumn;
1040 if((infoPtr->nItemCount % nCountPerColumn) == 0)
1041 scrollInfo.nMax--;
1042 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1043 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1044 scrollInfo.nPage = nCountPerRow;
1045 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1046 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1047 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1049 else if (uView == LVS_REPORT)
1051 BOOL test;
1053 /* update vertical scrollbar */
1054 scrollInfo.nMin = 0;
1055 scrollInfo.nMax = infoPtr->nItemCount - 1;
1056 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1057 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1058 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1059 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1060 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1061 scrollInfo.nMax, scrollInfo.nPage, test);
1062 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1063 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1065 /* update horizontal scrollbar */
1066 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1067 scrollInfo.fMask = SIF_POS;
1068 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1069 || infoPtr->nItemCount == 0)
1071 scrollInfo.nPos = 0;
1073 scrollInfo.nMin = 0;
1074 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1075 scrollInfo.nPage = nListWidth;
1076 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1077 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1078 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1079 scrollInfo.nMax, scrollInfo.nPage, test);
1080 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1081 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1083 /* Update the Header Control */
1084 scrollInfo.fMask = SIF_POS;
1085 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1086 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1089 else
1091 RECT rcView;
1093 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1095 INT nViewWidth = rcView.right - rcView.left;
1096 INT nViewHeight = rcView.bottom - rcView.top;
1098 /* Update Horizontal Scrollbar */
1099 scrollInfo.fMask = SIF_POS;
1100 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1101 || infoPtr->nItemCount == 0)
1103 scrollInfo.nPos = 0;
1105 scrollInfo.nMin = 0;
1106 scrollInfo.nMax = max(nViewWidth, 0)-1;
1107 scrollInfo.nPage = nListWidth;
1108 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1109 TRACE("LVS_ICON/SMALLICON Horz.\n");
1110 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1112 /* Update Vertical Scrollbar */
1113 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1114 scrollInfo.fMask = SIF_POS;
1115 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1116 || infoPtr->nItemCount == 0)
1118 scrollInfo.nPos = 0;
1120 scrollInfo.nMin = 0;
1121 scrollInfo.nMax = max(nViewHeight,0)-1;
1122 scrollInfo.nPage = nListHeight;
1123 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1124 TRACE("LVS_ICON/SMALLICON Vert.\n");
1125 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1131 /***
1132 * DESCRIPTION:
1133 * Shows/hides the focus rectangle.
1135 * PARAMETER(S):
1136 * [I] infoPtr : valid pointer to the listview structure
1137 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1139 * RETURN:
1140 * None
1142 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, INT nItem, BOOL fShow)
1144 RECT rcItem;
1146 TRACE("fShow=%d, nItem=%d\n", fShow, nItem);
1148 if (nItem < 0 || nItem >= infoPtr->nItemCount) return;
1150 rcItem.left = LVIR_BOUNDS;
1151 rcItem.top = 0;
1152 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
1153 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
1154 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
1156 /* this little optimization eliminates some nasty flicker */
1157 if (!LISTVIEW_GetSubItemRect(infoPtr, nItem, &rcItem)) return;
1159 else
1161 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
1164 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
1166 DRAWITEMSTRUCT dis;
1167 LVITEMW item;
1168 HDC hdc;
1170 item.iItem = nItem;
1171 item.iSubItem = 0;
1172 item.mask = LVIF_PARAM;
1173 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto invalidate;
1175 if (!(hdc = GetDC(infoPtr->hwndSelf))) goto invalidate;
1176 ZeroMemory(&dis, sizeof(dis));
1177 dis.CtlType = ODT_LISTVIEW;
1178 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1179 dis.itemID = nItem;
1180 dis.itemAction = ODA_FOCUS;
1181 if (fShow) dis.itemState |= ODS_FOCUS;
1182 dis.hwndItem = infoPtr->hwndSelf;
1183 dis.hDC = hdc;
1184 dis.rcItem = rcItem;
1185 dis.itemData = item.lParam;
1187 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1188 ReleaseDC(infoPtr->hwndSelf, hdc);
1189 return;
1191 else
1193 /* Here we are inneficient. We could, in theory, simply DrawFocusRect
1194 * to erase/show the focus, without all this heavy duty redraw.
1195 * Note that there are cases where we can not do that: when the list
1196 * is in ICON mode, and the item is large, we must to invalidate it.
1197 * Moreover, in the vast majority of cases, the selection status of
1198 * the item changes anyway, and so the item is invalidated already,
1199 * so not too much harm is done. If we do notice any flicker, we should
1200 * refine this method. */
1201 invalidate:
1202 LISTVIEW_InvalidateRect(infoPtr, &rcItem);
1206 /***
1207 * Invalidates all visible selected items.
1209 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1211 RANGE visrange;
1212 INT i;
1214 visrange = LISTVIEW_GetVisibleRange(infoPtr);
1215 for (i = visrange.lower; i <= visrange.upper; i++)
1217 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
1218 LISTVIEW_InvalidateItem(infoPtr, i);
1223 /***
1224 * DESCRIPTION:
1225 * Prints a message for unsupported window styles.
1226 * A kind of TODO list for window styles.
1228 * PARAMETER(S):
1229 * [I] LONG : window style
1231 * RETURN:
1232 * None
1234 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1236 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1237 FIXME(" LVS_NOSCROLL\n");
1239 if (lStyle & LVS_NOLABELWRAP)
1240 FIXME(" LVS_NOLABELWRAP\n");
1242 if (lStyle & LVS_SORTASCENDING)
1243 FIXME(" LVS_SORTASCENDING\n");
1245 if (lStyle & LVS_SORTDESCENDING)
1246 FIXME(" LVS_SORTDESCENDING\n");
1249 /***
1250 * DESCRIPTION: [INTERNAL]
1251 * Update the bounding rectangle around the text under a large icon.
1252 * This depends on whether it has the focus or not.
1253 * On entry the rectangle's top, left and right should be set.
1254 * On return the bottom will also be set and the width may have been
1255 * modified.
1257 * PARAMETER
1258 * [I] infoPtr : pointer to the listview structure
1259 * [I] nItem : the item for which we are calculating this
1260 * [I/O] rect : the rectangle to be updated
1262 * This appears to be weird, even in the Microsoft implementation.
1264 static BOOL LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *infoPtr, int nItem, RECT *rect)
1266 HDC hdc = GetDC (infoPtr->hwndSelf);
1267 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
1268 UINT uFormat = LISTVIEW_DTFLAGS | DT_CALCRECT;
1269 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1270 RECT rcText = *rect;
1271 RECT rcBack = *rect;
1272 BOOL focused, selected;
1273 int dx, dy, old_wid, new_wid;
1274 LVITEMW lvItem;
1276 TRACE("%s, focus item=%d, cur item=%d\n",
1277 (infoPtr->bFocus) ? "Window has focus" : "Window not focused",
1278 infoPtr->nFocusedItem, nItem);
1281 focused = infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED);
1282 selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
1284 uFormat |= (focused) ? DT_NOCLIP : DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
1286 /* We (aim to) display the full text. In Windows 95 it appears to
1287 * calculate the size assuming the specified font and then it draws
1288 * the text in that region with the specified font except scaled to
1289 * 10 point (or the height of the system font or ...). Thus if the
1290 * window has 24 point Helvetica the highlit rectangle will be
1291 * taller than the text and if it is 7 point Helvetica then the text
1292 * will be clipped.
1293 * For now we will simply say that it is the correct size to display
1294 * the text in the specified font.
1296 lvItem.mask = LVIF_TEXT;
1297 lvItem.iItem = nItem;
1298 lvItem.iSubItem = 0;
1299 lvItem.pszText = szDispText;
1300 lvItem.cchTextMax = DISP_TEXT_SIZE;
1301 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
1303 InflateRect(&rcText, -2, 0);
1304 DrawTextW (hdc, lvItem.pszText, -1, &rcText, uFormat);
1305 /* Microsoft, in their great wisdom, have decided that the rectangle
1306 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
1307 * not the location. So we have to do the centring ourselves (and take
1308 * responsibility for agreeing off-by-one consistency with them).
1311 old_wid = rcText.right - rcText.left;
1312 new_wid = rcBack.right - rcBack.left;
1313 dx = rcBack.left - rcText.left + (new_wid-old_wid)/2;
1314 dy = rcBack.top - rcText.top;
1315 OffsetRect (&rcText, dx, dy);
1317 if (focused)
1319 rcText.bottom += 2;
1320 InflateRect(&rcText, 2, 0);
1322 else /* not focused, may or may not be selected */
1325 * We need to have the bottom to be an intergal number of
1326 * text lines (ntmHeight) below text top that is less than
1327 * or equal to the nItemHeight.
1329 INT lh = infoPtr->nItemHeight - infoPtr->iconSize.cy -
1330 ICON_TOP_PADDING - ICON_BOTTOM_PADDING;
1331 INT ih = (lh / infoPtr->ntmHeight) * infoPtr->ntmHeight;
1332 rcText.bottom = min(rcText.bottom, rcText.top + ih);
1333 rcText.bottom += 1;
1335 *rect = rcText;
1337 TRACE("%s and %s, bounding rect=(%d,%d)-(%d,%d)\n",
1338 (focused) ? "focused(full text)" : "not focused",
1339 (selected) ? "selected" : "not selected",
1340 rect->left, rect->top, rect->right, rect->bottom);
1342 SelectObject (hdc, hOldFont);
1343 ReleaseDC (infoPtr->hwndSelf, hdc);
1345 return TRUE;
1348 /***
1349 * DESCRIPTION: [INTERNAL]
1350 * Compute the rectangles of an item. This is to localize all
1351 * the computations in one place. If you are not interested in some
1352 * of these values, simply pass in a NULL -- the fucntion is smart
1353 * enough to compute only what's necessary. The function computes
1354 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1355 * one, the BOX rectangle. This rectangle is very cheap to compute,
1356 * and is guaranteed to contain all the other rectangles. Computing
1357 * the ICON rect is also cheap, but all the others are potentaily
1358 * expensive. This gives an easy and effective optimization when
1359 * searching (like point inclusion, or rectangle intersection):
1360 * first test against the BOX, and if TRUE, test agains the desired
1361 * rectangle. These optimizations are coded in:
1362 * LISTVIEW_PtInRect, and LISTVIEW_IntersectRect
1363 * use them wherever is appropriate.
1365 * PARAMETER(S):
1366 * [I] infoPtr : valid pointer to the listview structure
1367 * [I] nItem : item number
1368 * [O] lprcBox : ptr to Box rectangle
1369 * The internal LVIR_BOX rectangle
1370 * [O] lprcBounds : ptr to Bounds rectangle
1371 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1372 * [O] lprcIcon : ptr to Icon rectangle
1373 * Same as LVM_GETITEMRECT with LVIR_ICON
1374 * [O] lprcLabel : ptr to Label rectangle
1375 * Same as LVM_GETITEMRECT with LVIR_LABEL
1377 * RETURN:
1378 * TRUE if computations OK
1379 * FALSE otherwise
1381 static BOOL LISTVIEW_GetItemMeasures(LISTVIEW_INFO *infoPtr, INT nItem,
1382 LPRECT lprcBox, LPRECT lprcBounds,
1383 LPRECT lprcIcon, LPRECT lprcLabel)
1385 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1386 BOOL doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1387 RECT Box, Icon, Label;
1388 POINT Origin;
1390 /* This should be very cheap to compute */
1391 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
1393 /* Be smart and try to figure out the minimum we have to do */
1394 if (lprcBounds)
1396 if (uView == LVS_REPORT) doIcon = TRUE;
1397 else doLabel = TRUE;
1399 if (uView == LVS_ICON && infoPtr->bFocus &&
1400 LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1401 oversizedBox = doLabel = TRUE;
1402 if (lprcLabel) doLabel = TRUE;
1403 if (doLabel || lprcIcon) doIcon = TRUE;
1405 /************************************************************/
1406 /* compute the box rectangle (it should be cheap to do) */
1407 /************************************************************/
1408 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1410 Box.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1411 Box.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1413 else if (uView == LVS_LIST)
1415 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1416 Box.left = nItem / nCountPerColumn * infoPtr->nItemWidth;
1417 Box.top = nItem % nCountPerColumn * infoPtr->nItemHeight;
1419 else /* LVS_REPORT */
1421 Box.left = REPORT_MARGINX;
1422 Box.top = nItem * infoPtr->nItemHeight;
1424 Box.left += Origin.x;
1425 Box.top += Origin.y;
1426 Box.right = Box.left + infoPtr->nItemWidth;
1427 Box.bottom = Box.top + infoPtr->nItemHeight;
1429 /************************************************************/
1430 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1431 /************************************************************/
1432 if (doIcon)
1434 if (uView == LVS_ICON)
1436 Icon.left = Box.left;
1437 if (infoPtr->himlNormal)
1438 Icon.left += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2 - ICON_LR_HALF;
1439 Icon.top = Box.top;
1440 Icon.right = Icon.left;
1441 Icon.bottom = Icon.top;
1442 if (infoPtr->himlNormal)
1444 Icon.right += infoPtr->iconSize.cx + ICON_LR_PADDING;
1445 Icon.bottom += infoPtr->iconSize.cy + ICON_TOP_PADDING;
1448 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1450 /* do indent */
1451 Icon.left = Box.left;
1452 if (uView == LVS_REPORT)
1454 LVITEMW lvItem;
1456 lvItem.mask = LVIF_INDENT;
1457 lvItem.iItem = nItem;
1458 lvItem.iSubItem = 0;
1459 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
1460 Icon.left += infoPtr->iconSize.cx * lvItem.iIndent;
1462 if (infoPtr->himlState) Icon.left += infoPtr->iconStateSize.cx;
1463 Icon.top = Box.top;
1464 Icon.right = Icon.left;
1465 if (infoPtr->himlSmall) Icon.right += infoPtr->iconSize.cx;
1466 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1468 if(lprcIcon) *lprcIcon = Icon;
1469 TRACE("hwnd=%x, item=%d, icon=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Icon));
1472 /************************************************************/
1473 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1474 /************************************************************/
1475 if (doLabel)
1477 if (uView == LVS_ICON)
1479 INT nLabelWidth;
1481 Label.left = Box.left;
1482 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1483 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1485 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1486 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
1488 Label.left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
1489 Label.right = Label.left + nLabelWidth;
1490 Label.bottom = Label.top + infoPtr->ntmHeight + 1;
1491 Label.bottom += HEIGHT_PADDING;
1493 else
1495 Label.right = Label.left + infoPtr->nItemWidth;
1496 Label.bottom = Label.top + infoPtr->nItemHeight + HEIGHT_PADDING;
1497 LISTVIEW_UpdateLargeItemLabelRect (infoPtr, nItem, &Label);
1500 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1502 INT nLabelWidth;
1504 Label.left = Icon.right;
1505 Label.top = Box.top;
1506 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1507 nLabelWidth += TRAILING_PADDING;
1508 if (infoPtr->himlSmall) nLabelWidth += IMAGE_PADDING;
1509 Label.right = min(Label.left + nLabelWidth, Box.right);
1510 Label.bottom = Label.top + infoPtr->nItemHeight;
1513 if (lprcLabel) *lprcLabel = Label;
1514 TRACE("hwnd=%x, item=%d, label=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Label));
1517 /***********************************************************/
1518 /* compute bounds box for the item (ala LVM_GETITEMRECT) */
1519 /***********************************************************/
1520 if (lprcBounds)
1522 if (uView == LVS_REPORT)
1524 lprcBounds->left = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT ? Box.left : Icon.left;
1525 lprcBounds->top = Box.top;
1526 lprcBounds->right = min(lprcBounds->left + infoPtr->nItemWidth, Box.right) - REPORT_MARGINX;
1527 if (lprcBounds->right < lprcBounds->left) lprcBounds->right = lprcBounds->left;
1528 lprcBounds->bottom = lprcBounds->top + infoPtr->nItemHeight;
1530 else
1531 UnionRect(lprcBounds, &Icon, &Label);
1532 TRACE("hwnd=%x, item=%d, bounds=%s\n", infoPtr->hwndSelf, nItem, debugrect(lprcBounds));
1535 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1536 else if (lprcBox) *lprcBox = Box;
1537 TRACE("hwnd=%x, item=%d, box=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Box));
1539 return TRUE;
1542 /***
1543 * DESCRIPTION:
1544 * Aligns the items with the top edge of the window.
1546 * PARAMETER(S):
1547 * [I] infoPtr : valid pointer to the listview structure
1549 * RETURN:
1550 * None
1552 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1554 UINT uView = LISTVIEW_GetType(infoPtr);
1555 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1556 POINT ptItem;
1557 RECT rcView;
1558 INT i, off_x=0, off_y=0;
1560 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1562 /* Since SetItemPosition uses upper-left of icon, and for
1563 style=LVS_ICON the icon is not left adjusted, get the offset */
1564 if (uView == LVS_ICON)
1566 off_y = ICON_TOP_PADDING;
1567 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1569 ptItem.x = off_x;
1570 ptItem.y = off_y;
1571 ZeroMemory(&rcView, sizeof(RECT));
1572 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1573 off_x, off_y,
1574 infoPtr->rcList.left, infoPtr->rcList.right);
1576 if (nListWidth > infoPtr->nItemWidth)
1578 for (i = 0; i < infoPtr->nItemCount; i++)
1580 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1582 ptItem.x = off_x;
1583 ptItem.y += infoPtr->nItemHeight;
1586 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1587 ptItem.x += infoPtr->nItemWidth;
1588 rcView.right = max(rcView.right, ptItem.x);
1591 rcView.right -= off_x;
1592 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1594 else
1596 for (i = 0; i < infoPtr->nItemCount; i++)
1598 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1599 ptItem.y += infoPtr->nItemHeight;
1602 rcView.right = infoPtr->nItemWidth;
1603 rcView.bottom = ptItem.y-off_y;
1606 infoPtr->rcView = rcView;
1610 /***
1611 * DESCRIPTION:
1612 * Aligns the items with the left edge of the window.
1614 * PARAMETER(S):
1615 * [I] infoPtr : valid pointer to the listview structure
1617 * RETURN:
1618 * None
1620 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1622 UINT uView = LISTVIEW_GetType(infoPtr);
1623 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1624 POINT ptItem;
1625 RECT rcView;
1626 INT i, off_x=0, off_y=0;
1628 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1630 /* Since SetItemPosition uses upper-left of icon, and for
1631 style=LVS_ICON the icon is not left adjusted, get the offset */
1632 if (uView == LVS_ICON)
1634 off_y = ICON_TOP_PADDING;
1635 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1637 ptItem.x = off_x;
1638 ptItem.y = off_y;
1639 ZeroMemory(&rcView, sizeof(RECT));
1640 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1642 if (nListHeight > infoPtr->nItemHeight)
1644 for (i = 0; i < infoPtr->nItemCount; i++)
1646 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1648 ptItem.y = off_y;
1649 ptItem.x += infoPtr->nItemWidth;
1652 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1653 ptItem.y += infoPtr->nItemHeight;
1654 rcView.bottom = max(rcView.bottom, ptItem.y);
1657 rcView.right = ptItem.x + infoPtr->nItemWidth;
1659 else
1661 for (i = 0; i < infoPtr->nItemCount; i++)
1663 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1664 ptItem.x += infoPtr->nItemWidth;
1667 rcView.bottom = infoPtr->nItemHeight;
1668 rcView.right = ptItem.x;
1671 infoPtr->rcView = rcView;
1676 /***
1677 * DESCRIPTION:
1678 * Retrieves the bounding rectangle of all the items.
1680 * PARAMETER(S):
1681 * [I] infoPtr : valid pointer to the listview structure
1682 * [O] lprcView : bounding rectangle
1684 * RETURN:
1685 * SUCCESS : TRUE
1686 * FAILURE : FALSE
1688 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1690 POINT ptOrigin;
1692 TRACE("(lprcView=%p)\n", lprcView);
1694 if (!lprcView) return FALSE;
1696 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return FALSE;
1698 *lprcView = infoPtr->rcView;
1699 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
1701 TRACE("lprcView=%s\n", debugrect(lprcView));
1703 return TRUE;
1706 /***
1707 * DESCRIPTION:
1708 * Retrieves the subitem pointer associated with the subitem index.
1710 * PARAMETER(S):
1711 * [I] HDPA : DPA handle for a specific item
1712 * [I] INT : index of subitem
1714 * RETURN:
1715 * SUCCESS : subitem pointer
1716 * FAILURE : NULL
1718 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1719 INT nSubItem)
1721 LISTVIEW_SUBITEM *lpSubItem;
1722 INT i;
1724 /* we should binary search here if need be */
1725 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1727 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1728 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
1729 return lpSubItem;
1732 return NULL;
1736 /***
1737 * DESCRIPTION:
1738 * Calculates the width of a specific item.
1740 * PARAMETER(S):
1741 * [I] infoPtr : valid pointer to the listview structure
1742 * [I] nItem : item to calculate width, or -1 for max of all
1744 * RETURN:
1745 * Returns the width of an item width an item.
1747 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr, INT nItem)
1749 UINT uView = LISTVIEW_GetType(infoPtr);
1750 INT nItemWidth = 0, i;
1752 if (uView == LVS_ICON)
1753 nItemWidth = infoPtr->iconSpacing.cx;
1754 else if (uView == LVS_REPORT)
1756 INT nHeaderItemCount;
1757 RECT rcHeaderItem;
1759 /* calculate width of header */
1760 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1761 for (i = 0; i < nHeaderItemCount; i++)
1762 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem))
1763 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1765 else
1767 INT nLabelWidth;
1769 if (infoPtr->nItemCount == 0) return DEFAULT_COLUMN_WIDTH;
1771 /* get width of string */
1772 if (nItem == -1)
1774 for (i = 0; i < infoPtr->nItemCount; i++)
1776 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
1777 nItemWidth = max(nItemWidth, nLabelWidth);
1780 else
1781 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1782 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
1783 nItemWidth += WIDTH_PADDING;
1784 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
1785 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
1786 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1789 return max(nItemWidth, 1);
1792 /***
1793 * DESCRIPTION:
1794 * Calculates the max width of any item in the list.
1796 * PARAMETER(S):
1797 * [I] infoPtr : valid pointer to the listview structure
1798 * [I] LONG : window style
1800 * RETURN:
1801 * Returns item width.
1803 static inline INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *infoPtr)
1805 return LISTVIEW_CalculateItemWidth(infoPtr, -1);
1808 /***
1809 * DESCRIPTION:
1810 * Retrieves and saves important text metrics info for the current
1811 * Listview font.
1813 * PARAMETER(S):
1814 * [I] infoPtr : valid pointer to the listview structure
1817 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
1819 TEXTMETRICW tm;
1820 HDC hdc = GetDC(infoPtr->hwndSelf);
1821 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1822 INT oldHeight, oldACW;
1824 GetTextMetricsW(hdc, &tm);
1826 oldHeight = infoPtr->ntmHeight;
1827 oldACW = infoPtr->ntmAveCharWidth;
1828 infoPtr->ntmHeight = tm.tmHeight;
1829 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1831 SelectObject(hdc, hOldFont);
1832 ReleaseDC(infoPtr->hwndSelf, hdc);
1833 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1834 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1838 /***
1839 * DESCRIPTION:
1840 * Calculates the height of an item.
1842 * PARAMETER(S):
1843 * [I] infoPtr : valid pointer to the listview structure
1845 * RETURN:
1846 * Returns item height.
1848 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *infoPtr)
1850 INT nItemHeight;
1852 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
1853 nItemHeight = infoPtr->iconSpacing.cy;
1854 else
1856 nItemHeight = infoPtr->ntmHeight;
1857 if (infoPtr->himlState)
1858 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
1859 if (infoPtr->himlSmall)
1860 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
1861 if (infoPtr->himlState || infoPtr->himlSmall)
1862 nItemHeight += HEIGHT_PADDING;
1864 return nItemHeight;
1867 #if 0
1868 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
1870 INT i;
1872 ERR("Selections are:\n");
1873 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
1875 RANGE *selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
1876 ERR(" [%d - %d]\n", selection->lower, selection->upper);
1879 #endif
1881 /***
1882 * DESCRIPTION:
1883 * A compare function for selection ranges
1885 *PARAMETER(S)
1886 * [I] range1 : pointer to selection range 1;
1887 * [I] range2 : pointer to selection range 2;
1888 * [I] flags : flags
1890 *RETURNS:
1891 * >0 : if Item 1 > Item 2
1892 * <0 : if Item 2 > Item 1
1893 * 0 : if Item 1 == Item 2
1895 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2, LPARAM flags)
1897 if (((RANGE*)range1)->upper < ((RANGE*)range2)->lower)
1898 return -1;
1899 if (((RANGE*)range2)->upper < ((RANGE*)range1)->lower)
1900 return 1;
1901 return 0;
1904 /***
1905 * Helper function for LISTVIEW_AddSelectionRange, and LISTVIEW_SetItem.
1907 static BOOL add_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
1909 RANGE selection;
1910 LVITEMW lvItem;
1911 INT index, i;
1913 TRACE("range (%i - %i)\n", lower, upper);
1915 /* try find overlapping selections first */
1916 selection.lower = lower - 1;
1917 selection.upper = upper + 1;
1918 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1919 LISTVIEW_CompareSelectionRanges, 0, 0);
1921 if (index == -1)
1923 RANGE *newsel;
1925 /* create the brand new selection to insert */
1926 newsel = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
1927 if(!newsel) return FALSE;
1928 newsel->lower = lower;
1929 newsel->upper = upper;
1931 /* figure out where to insert it */
1932 index = DPA_Search(infoPtr->hdpaSelectionRanges, newsel, 0,
1933 LISTVIEW_CompareSelectionRanges, 0, DPAS_INSERTAFTER);
1934 if (index == -1) index = 0;
1936 /* and get it over with */
1937 DPA_InsertPtr(infoPtr->hdpaSelectionRanges, index, newsel);
1939 else
1941 RANGE *chksel, *mrgsel;
1942 INT fromindex, mergeindex;
1944 chksel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index);
1945 if (!chksel) return FALSE;
1946 TRACE("Merge with index %i (%d - %d)\n",
1947 index, chksel->lower, chksel->upper);
1949 chksel->lower = min(lower, chksel->lower);
1950 chksel->upper = max(upper, chksel->upper);
1952 TRACE("New range %i (%d - %d)\n",
1953 index, chksel->lower, chksel->upper);
1955 /* merge now common selection ranges */
1956 fromindex = 0;
1957 selection.lower = chksel->lower - 1;
1958 selection.upper = chksel->upper + 1;
1962 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, fromindex,
1963 LISTVIEW_CompareSelectionRanges, 0, 0);
1964 if (mergeindex == -1) break;
1965 if (mergeindex == index)
1967 fromindex = index + 1;
1968 continue;
1971 TRACE("Merge with index %i\n", mergeindex);
1973 mrgsel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, mergeindex);
1974 if (!mrgsel) return FALSE;
1976 chksel->lower = min(chksel->lower, mrgsel->lower);
1977 chksel->upper = max(chksel->upper, mrgsel->upper);
1978 COMCTL32_Free(mrgsel);
1979 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, mergeindex);
1980 if (mergeindex < index) index --;
1981 } while(1);
1984 /*DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);*/
1986 if (adj_sel_only) return TRUE;
1988 /* set the selection on items */
1989 lvItem.state = LVIS_SELECTED;
1990 lvItem.stateMask = LVIS_SELECTED;
1991 for(i = lower; i <= upper; i++)
1992 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
1994 return TRUE;
1997 /***
1998 * Helper function for LISTVIEW_RemoveSelectionRange, and LISTVIEW_SetItem.
2000 static BOOL remove_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
2002 RANGE remsel, tmpsel, *chksel;
2003 BOOL done = FALSE;
2004 LVITEMW lvItem;
2005 INT index, i;
2007 lvItem.state = 0;
2008 lvItem.stateMask = LVIS_SELECTED;
2010 remsel.lower = lower;
2011 remsel.upper = upper;
2013 TRACE("range: (%d - %d)\n", remsel.lower, remsel.upper);
2017 index = DPA_Search(infoPtr->hdpaSelectionRanges, &remsel, 0,
2018 LISTVIEW_CompareSelectionRanges, 0, 0);
2019 if (index == -1) return TRUE;
2021 chksel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index);
2022 if (!chksel) return FALSE;
2024 TRACE("Matches range index %i (%d - %d)\n",
2025 index, chksel->lower, chksel->upper);
2027 /* case 1: Same range */
2028 if ( (chksel->upper == remsel.upper) &&
2029 (chksel->lower == remsel.lower) )
2031 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, index);
2032 done = TRUE;
2034 /* case 2: engulf */
2035 else if ( (chksel->upper <= remsel.upper) &&
2036 (chksel->lower >= remsel.lower) )
2038 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, index);
2040 /* case 3: overlap upper */
2041 else if ( (chksel->upper < remsel.upper) &&
2042 (chksel->lower < remsel.lower) )
2044 chksel->upper = remsel.lower - 1;
2046 /* case 4: overlap lower */
2047 else if ( (chksel->upper > remsel.upper) &&
2048 (chksel->lower > remsel.lower) )
2050 chksel->lower = remsel.upper + 1;
2052 /* case 5: fully internal */
2053 else
2055 RANGE *newsel =
2056 (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2057 if (!newsel) return FALSE;
2058 tmpsel = *chksel;
2059 newsel->lower = chksel->lower;
2060 newsel->upper = remsel.lower - 1;
2061 chksel->lower = remsel.upper + 1;
2062 DPA_InsertPtr(infoPtr->hdpaSelectionRanges, index, newsel);
2063 /*DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);*/
2064 chksel = &tmpsel;
2067 if (adj_sel_only) continue;
2069 /* here, chksel holds the selection to delete */
2070 for (i = chksel->lower; i <= chksel->upper; i++)
2071 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
2073 while(!done);
2075 return TRUE;
2079 * DESCRIPTION:
2080 * Adds a selection range.
2082 * PARAMETER(S):
2083 * [I] infoPtr : valid pointer to the listview structure
2084 * [I] lower : lower item index
2085 * [I] upper : upper item index
2087 * RETURN:
2088 * Success: TRUE
2089 * Failure: FALSE
2091 static inline BOOL LISTVIEW_AddSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2093 return add_selection_range(infoPtr, lower, upper, FALSE);
2096 /***
2097 * DESCRIPTION:
2098 * Removes a range selections.
2100 * PARAMETER(S):
2101 * [I] infoPtr : valid pointer to the listview structure
2102 * [I] lower : lower item index
2103 * [I] upper : upper item index
2105 * RETURN:
2106 * Success: TRUE
2107 * Failure: FALSE
2109 static inline BOOL LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2111 return remove_selection_range(infoPtr, lower, upper, FALSE);
2114 /***
2115 * DESCRIPTION:
2116 * Removes all selection ranges
2118 * Parameters(s):
2119 * [I] infoPtr : valid pointer to the listview structure
2121 * RETURNS:
2122 * SUCCESS : TRUE
2123 * FAILURE : TRUE
2125 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr)
2127 RANGE *sel;
2129 if (infoPtr->bRemovingAllSelections) return TRUE;
2131 infoPtr->bRemovingAllSelections = TRUE;
2133 TRACE("()\n");
2137 sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, 0);
2138 if (sel) LISTVIEW_RemoveSelectionRange(infoPtr, sel->lower, sel->upper);
2140 while (infoPtr->hdpaSelectionRanges->nItemCount > 0);
2142 infoPtr->bRemovingAllSelections = FALSE;
2144 return TRUE;
2147 /***
2148 * DESCRIPTION:
2149 * Retrieves the number of items that are marked as selected.
2151 * PARAMETER(S):
2152 * [I] infoPtr : valid pointer to the listview structure
2154 * RETURN:
2155 * Number of items selected.
2157 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2159 INT i, nSelectedCount = 0;
2161 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2163 INT i;
2164 for (i = 0; i < infoPtr->nItemCount; i++)
2166 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2167 nSelectedCount++;
2170 else
2172 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
2174 RANGE *sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
2175 nSelectedCount += sel->upper - sel->lower + 1;
2179 TRACE("nSelectedCount=%d\n", nSelectedCount);
2180 return nSelectedCount;
2183 /***
2184 * DESCRIPTION:
2185 * Manages the item focus.
2187 * PARAMETER(S):
2188 * [I] infoPtr : valid pointer to the listview structure
2189 * [I] INT : item index
2191 * RETURN:
2192 * TRUE : focused item changed
2193 * FALSE : focused item has NOT changed
2195 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2197 INT oldFocus = infoPtr->nFocusedItem;
2198 LVITEMW lvItem;
2200 lvItem.state = LVIS_FOCUSED;
2201 lvItem.stateMask = LVIS_FOCUSED;
2202 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2204 return oldFocus != infoPtr->nFocusedItem;
2208 * DESCRIPTION:
2209 * Updates the various indices after an item has been inserted or deleted.
2211 * PARAMETER(S):
2212 * [I] infoPtr : valid pointer to the listview structure
2213 * [I] nItem : item index
2214 * [I] direction : Direction of shift, +1 or -1.
2216 * RETURN:
2217 * None
2219 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2221 RANGE selection,*checkselection;
2222 INT index;
2224 TRACE("Shifting %iu, %i steps\n",nItem,direction);
2226 selection.upper = nItem;
2227 selection.lower = nItem;
2229 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
2230 LISTVIEW_CompareSelectionRanges,
2231 0,DPAS_SORTED|DPAS_INSERTAFTER);
2233 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
2235 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
2236 if ((checkselection->lower >= nItem)&&
2237 ((int)(checkselection->lower + direction) >= 0))
2238 checkselection->lower += direction;
2239 if ((checkselection->upper >= nItem)&&
2240 ((int)(checkselection->upper + direction) >= 0))
2241 checkselection->upper += direction;
2242 index ++;
2245 /* Note that the following will fail if direction != +1 and -1 */
2246 if (infoPtr->nSelectionMark > nItem)
2247 infoPtr->nSelectionMark += direction;
2248 else if (infoPtr->nSelectionMark == nItem)
2250 if (direction > 0)
2251 infoPtr->nSelectionMark += direction;
2252 else if (infoPtr->nSelectionMark >= infoPtr->nItemCount)
2253 infoPtr->nSelectionMark = infoPtr->nItemCount - 1;
2256 if (infoPtr->nFocusedItem > nItem)
2257 infoPtr->nFocusedItem += direction;
2258 else if (infoPtr->nFocusedItem == nItem)
2260 if (direction > 0)
2261 infoPtr->nFocusedItem += direction;
2262 else
2264 if (infoPtr->nFocusedItem >= infoPtr->nItemCount)
2265 infoPtr->nFocusedItem = infoPtr->nItemCount - 1;
2266 if (infoPtr->nFocusedItem >= 0)
2267 LISTVIEW_SetItemFocus(infoPtr, infoPtr->nFocusedItem);
2270 /* But we are not supposed to modify nHotItem! */
2275 * DESCRIPTION:
2276 * Adds a block of selections.
2278 * PARAMETER(S):
2279 * [I] infoPtr : valid pointer to the listview structure
2280 * [I] INT : item index
2282 * RETURN:
2283 * None
2285 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2287 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2288 INT nLast = max(infoPtr->nSelectionMark, nItem);
2289 INT i;
2290 LVITEMW item;
2292 if (nFirst == -1)
2293 nFirst = nItem;
2295 item.state = LVIS_SELECTED;
2296 item.stateMask = LVIS_SELECTED;
2298 /* FIXME: this is not correct LVS_OWNERDATA
2299 * See docu for LVN_ITEMCHANGED. Is there something similar for
2300 * RemoveGroupSelection (is there such a thing?)?
2302 for (i = nFirst; i <= nLast; i++)
2303 LISTVIEW_SetItemState(infoPtr,i,&item);
2305 LISTVIEW_SetItemFocus(infoPtr, nItem);
2306 infoPtr->nSelectionMark = nItem;
2310 /***
2311 * DESCRIPTION:
2312 * Sets a single group selection.
2314 * PARAMETER(S):
2315 * [I] infoPtr : valid pointer to the listview structure
2316 * [I] INT : item index
2318 * RETURN:
2319 * None
2321 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2323 UINT uView = LISTVIEW_GetType(infoPtr);
2324 INT i;
2325 LVITEMW item;
2326 POINT ptItem;
2327 RECT rcSel;
2329 LISTVIEW_RemoveAllSelections(infoPtr);
2331 item.state = LVIS_SELECTED;
2332 item.stateMask = LVIS_SELECTED;
2334 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2336 INT nFirst, nLast;
2338 if (infoPtr->nSelectionMark == -1)
2339 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2340 else
2342 nFirst = min(infoPtr->nSelectionMark, nItem);
2343 nLast = max(infoPtr->nSelectionMark, nItem);
2345 for (i = nFirst; i <= nLast; i++)
2346 LISTVIEW_SetItemState(infoPtr, i, &item);
2348 else
2350 RECT rcItem, rcSelMark;
2352 rcItem.left = LVIR_BOUNDS;
2353 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2354 rcSelMark.left = LVIR_BOUNDS;
2355 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2356 UnionRect(&rcSel, &rcItem, &rcSelMark);
2357 for (i = 0; i <= infoPtr->nItemCount; i++)
2359 LISTVIEW_GetItemPosition(infoPtr, i, &ptItem);
2360 if (PtInRect(&rcSel, ptItem))
2361 LISTVIEW_SetItemState(infoPtr, i, &item);
2365 LISTVIEW_SetItemFocus(infoPtr, nItem);
2368 /***
2369 * DESCRIPTION:
2370 * Sets a single selection.
2372 * PARAMETER(S):
2373 * [I] infoPtr : valid pointer to the listview structure
2374 * [I] INT : item index
2376 * RETURN:
2377 * None
2379 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2381 LVITEMW lvItem;
2383 TRACE("nItem=%d\n", nItem);
2385 LISTVIEW_RemoveAllSelections(infoPtr);
2387 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2388 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2389 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2391 infoPtr->nSelectionMark = nItem;
2394 /***
2395 * DESCRIPTION:
2396 * Set selection(s) with keyboard.
2398 * PARAMETER(S):
2399 * [I] infoPtr : valid pointer to the listview structure
2400 * [I] INT : item index
2402 * RETURN:
2403 * SUCCESS : TRUE (needs to be repainted)
2404 * FAILURE : FALSE (nothing has changed)
2406 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2408 /* FIXME: pass in the state */
2409 LONG lStyle = infoPtr->dwStyle;
2410 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2411 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2412 BOOL bResult = FALSE;
2414 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2416 if (lStyle & LVS_SINGLESEL)
2418 bResult = TRUE;
2419 LISTVIEW_SetSelection(infoPtr, nItem);
2420 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2422 else
2424 if (wShift)
2426 bResult = TRUE;
2427 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2429 else if (wCtrl)
2431 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2433 else
2435 bResult = TRUE;
2436 LISTVIEW_SetSelection(infoPtr, nItem);
2437 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2442 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2443 return bResult;
2446 /***
2447 * DESCRIPTION:
2448 * Selects an item based on coordinates.
2450 * PARAMETER(S):
2451 * [I] infoPtr : valid pointer to the listview structure
2452 * [I] pt : mouse click ccordinates
2454 * RETURN:
2455 * SUCCESS : item index
2456 * FAILURE : -1
2458 static INT LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, POINT pt)
2460 RANGE visrange;
2461 RECT rcItem;
2462 INT i;
2464 visrange = LISTVIEW_GetVisibleRange(infoPtr);
2465 for (i = visrange.lower; i <= visrange.upper; i++)
2467 rcItem.left = LVIR_SELECTBOUNDS;
2468 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
2470 TRACE("i=%d, rcItem=%s\n", i, debugrect(&rcItem));
2471 if (PtInRect(&rcItem, pt)) return i;
2474 return -1;
2477 /***
2478 * DESCRIPTION:
2479 * Called when the mouse is being actively tracked and has hovered for a specified
2480 * amount of time
2482 * PARAMETER(S):
2483 * [I] infoPtr : valid pointer to the listview structure
2484 * [I] fwKeys : key indicator
2485 * [I] pts : mouse position
2487 * RETURN:
2488 * 0 if the message was processed, non-zero if there was an error
2490 * INFO:
2491 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2492 * over the item for a certain period of time.
2495 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2497 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2498 /* FIXME: select the item!!! */
2499 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2501 return 0;
2504 /***
2505 * DESCRIPTION:
2506 * Called whenever WM_MOUSEMOVE is received.
2508 * PARAMETER(S):
2509 * [I] infoPtr : valid pointer to the listview structure
2510 * [I] fwKeys : key indicator
2511 * [I] pts : mouse position
2513 * RETURN:
2514 * 0 if the message is processed, non-zero if there was an error
2516 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2518 TRACKMOUSEEVENT trackinfo;
2520 /* see if we are supposed to be tracking mouse hovering */
2521 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2522 /* fill in the trackinfo struct */
2523 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2524 trackinfo.dwFlags = TME_QUERY;
2525 trackinfo.hwndTrack = infoPtr->hwndSelf;
2526 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2528 /* see if we are already tracking this hwnd */
2529 _TrackMouseEvent(&trackinfo);
2531 if(!(trackinfo.dwFlags & TME_HOVER)) {
2532 trackinfo.dwFlags = TME_HOVER;
2534 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2535 _TrackMouseEvent(&trackinfo);
2539 return 0;
2543 /***
2544 * Tests wheather the item is assignable to a list with style lStyle
2546 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2548 if ( (lpLVItem->mask & LVIF_TEXT) &&
2549 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2550 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2552 return TRUE;
2555 /***
2556 * DESCRIPTION:
2557 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2559 * PARAMETER(S):
2560 * [I] infoPtr : valid pointer to the listview structure
2561 * [I] lpLVItem : valid pointer to new item atttributes
2562 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2564 * RETURN:
2565 * SUCCESS : TRUE
2566 * FAILURE : FALSE
2568 static BOOL set_owner_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2570 LONG lStyle = infoPtr->dwStyle;
2571 NMLISTVIEW nmlv;
2572 INT oldState;
2574 /* a virtual listview stores only the state for the main item */
2575 if (lpLVItem->iSubItem || !(lpLVItem->mask & LVIF_STATE)) return FALSE;
2577 oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, LVIS_FOCUSED | LVIS_SELECTED);
2578 TRACE("oldState=%x, newState=%x, uCallbackMask=%x\n",
2579 oldState, lpLVItem->state, infoPtr->uCallbackMask);
2581 /* we're done if we don't need to change anything we handle */
2582 if ( !((oldState ^ lpLVItem->state) & lpLVItem->stateMask &
2583 ~infoPtr->uCallbackMask & (LVIS_FOCUSED | LVIS_SELECTED))) return TRUE;
2586 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2587 * by LVS_OWERNDATA list controls
2590 /* if we handle the focus, and we're asked to change it, do it now */
2591 if ( lpLVItem->stateMask & LVIS_FOCUSED )
2593 if (lpLVItem->state & LVIS_FOCUSED)
2594 infoPtr->nFocusedItem = lpLVItem->iItem;
2595 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2596 infoPtr->nFocusedItem = -1;
2599 /* and the selection is the only other state a virtual list may hold */
2600 if (lpLVItem->stateMask & LVIS_SELECTED)
2602 if (lpLVItem->state & LVIS_SELECTED)
2604 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2605 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2607 else
2608 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2611 /* notify the parent now that things have changed */
2612 ZeroMemory(&nmlv, sizeof(nmlv));
2613 nmlv.iItem = lpLVItem->iItem;
2614 nmlv.uNewState = lpLVItem->state;
2615 nmlv.uOldState = oldState;
2616 nmlv.uChanged = LVIF_STATE;
2617 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2619 return TRUE;
2622 /***
2623 * DESCRIPTION:
2624 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2626 * PARAMETER(S):
2627 * [I] infoPtr : valid pointer to the listview structure
2628 * [I] lpLVItem : valid pointer to new item atttributes
2629 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2631 * RETURN:
2632 * SUCCESS : TRUE
2633 * FAILURE : FALSE
2635 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2637 LONG lStyle = infoPtr->dwStyle;
2638 UINT uView = lStyle & LVS_TYPEMASK;
2639 HDPA hdpaSubItems;
2640 LISTVIEW_ITEM *lpItem;
2641 NMLISTVIEW nmlv;
2642 UINT uChanged = 0;
2644 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2645 if (!hdpaSubItems && hdpaSubItems != (HDPA)-1) return FALSE;
2647 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2648 if (!lpItem) return FALSE;
2650 /* determine what fields will change */
2651 if ((lpLVItem->mask & LVIF_STATE) &&
2652 ((lpItem->state ^ lpLVItem->state) & lpLVItem->stateMask))
2653 uChanged |= LVIF_STATE;
2655 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2656 uChanged |= LVIF_IMAGE;
2658 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2659 uChanged |= LVIF_PARAM;
2661 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2662 uChanged |= LVIF_INDENT;
2664 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2665 uChanged |= LVIF_TEXT;
2667 if (!uChanged) return TRUE;
2669 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2670 nmlv.iItem = lpLVItem->iItem;
2671 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2672 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2673 nmlv.uChanged = uChanged;
2674 nmlv.lParam = lpItem->lParam;
2676 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2677 if(lpItem->valid && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
2678 return FALSE;
2680 /* copy information */
2681 if (lpLVItem->mask & LVIF_TEXT)
2682 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
2684 if (lpLVItem->mask & LVIF_IMAGE)
2685 lpItem->hdr.iImage = lpLVItem->iImage;
2687 if (lpLVItem->mask & LVIF_PARAM)
2688 lpItem->lParam = lpLVItem->lParam;
2690 if (lpLVItem->mask & LVIF_INDENT)
2691 lpItem->iIndent = lpLVItem->iIndent;
2693 if (uChanged & LVIF_STATE)
2695 lpItem->state &= ~lpLVItem->stateMask;
2696 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2697 if (nmlv.uNewState & LVIS_SELECTED)
2699 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2700 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2702 else if (lpLVItem->stateMask & LVIS_SELECTED)
2703 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2705 /* if we are asked to change focus, and we manage it, do it */
2706 if (nmlv.uNewState & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
2708 if (lpLVItem->state & LVIS_FOCUSED)
2710 infoPtr->nFocusedItem = lpLVItem->iItem;
2711 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
2713 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2714 infoPtr->nFocusedItem = -1;
2718 /* if LVS_LIST or LVS_SMALLICON, update the width of the items */
2719 if((uChanged & LVIF_TEXT) && ((uView == LVS_LIST) || (uView == LVS_SMALLICON)))
2721 int item_width = LISTVIEW_CalculateItemWidth(infoPtr, lpLVItem->iItem);
2722 if(item_width > infoPtr->nItemWidth) infoPtr->nItemWidth = item_width;
2725 /* if we're inserting the item, we're done */
2726 if (!lpItem->valid) return TRUE;
2728 /* send LVN_ITEMCHANGED notification */
2729 nmlv.lParam = lpItem->lParam;
2730 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2732 return TRUE;
2735 /***
2736 * DESCRIPTION:
2737 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2739 * PARAMETER(S):
2740 * [I] infoPtr : valid pointer to the listview structure
2741 * [I] lpLVItem : valid pointer to new subitem atttributes
2742 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2744 * RETURN:
2745 * SUCCESS : TRUE
2746 * FAILURE : FALSE
2748 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2750 HDPA hdpaSubItems;
2751 LISTVIEW_SUBITEM *lpSubItem;
2752 BOOL bModified = FALSE;
2754 /* set subitem only if column is present */
2755 if (Header_GetItemCount(infoPtr->hwndHeader) <= lpLVItem->iSubItem)
2756 return FALSE;
2758 /* First do some sanity checks */
2759 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
2760 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
2762 /* get the subitem structure, and create it if not there */
2763 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2764 if (!hdpaSubItems) return FALSE;
2766 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
2767 if (!lpSubItem)
2769 LISTVIEW_SUBITEM *tmpSubItem;
2770 INT i;
2772 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2773 if (!lpSubItem) return FALSE;
2774 /* we could binary search here, if need be...*/
2775 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2777 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2778 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
2780 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
2782 COMCTL32_Free(lpSubItem);
2783 return FALSE;
2785 lpSubItem->iSubItem = lpLVItem->iSubItem;
2786 bModified = TRUE;
2789 if (lpLVItem->mask & LVIF_IMAGE)
2790 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
2792 lpSubItem->hdr.iImage = lpLVItem->iImage;
2793 bModified = TRUE;
2796 if (lpLVItem->mask & LVIF_TEXT)
2797 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
2799 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
2800 bModified = TRUE;
2803 if (bModified && !infoPtr->bIsDrawing)
2805 RECT rect;
2807 rect.left = LVIR_BOUNDS;
2808 rect.top = lpLVItem->iSubItem;
2809 /* GetSubItemRect will fail in non-report mode, so there's
2810 * gonna be no invalidation then, yay! */
2811 if (LISTVIEW_GetSubItemRect(infoPtr, lpLVItem->iItem, &rect))
2812 LISTVIEW_InvalidateRect(infoPtr, &rect);
2815 return TRUE;
2818 /***
2819 * DESCRIPTION:
2820 * Sets item attributes.
2822 * PARAMETER(S):
2823 * [I] infoPtr : valid pointer to the listview structure
2824 * [I] LPLVITEM : new item atttributes
2825 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2827 * RETURN:
2828 * SUCCESS : TRUE
2829 * FAILURE : FALSE
2831 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2833 INT oldFocus = infoPtr->nFocusedItem;
2834 LPWSTR pszText = NULL;
2835 BOOL bResult;
2837 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2839 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
2840 return FALSE;
2842 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
2843 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
2845 pszText = lpLVItem->pszText;
2846 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
2849 /* actually set the fields */
2850 if (infoPtr->dwStyle & LVS_OWNERDATA)
2851 bResult = set_owner_item(infoPtr, lpLVItem, TRUE);
2852 else
2854 /* sanity checks first */
2855 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
2857 if (lpLVItem->iSubItem)
2858 bResult = set_sub_item(infoPtr, lpLVItem, TRUE);
2859 else
2860 bResult = set_main_item(infoPtr, lpLVItem, TRUE);
2863 /* redraw item, if necessary */
2864 if (bResult && !infoPtr->bIsDrawing && lpLVItem->iSubItem == 0)
2866 if (oldFocus != infoPtr->nFocusedItem && infoPtr->bFocus)
2867 LISTVIEW_ShowFocusRect(infoPtr, oldFocus, FALSE);
2869 /* this little optimization eliminates some nasty flicker */
2870 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
2871 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
2872 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
2874 RECT rect;
2876 rect.left = LVIR_BOUNDS;
2877 rect.top = 0;
2878 if (LISTVIEW_GetSubItemRect(infoPtr, lpLVItem->iItem, &rect))
2879 LISTVIEW_InvalidateRect(infoPtr, &rect);
2881 else
2882 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
2884 /* restore text */
2885 if (pszText)
2887 textfreeT(lpLVItem->pszText, isW);
2888 lpLVItem->pszText = pszText;
2891 return bResult;
2894 /***
2895 * DESCRIPTION:
2896 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2898 * PARAMETER(S):
2899 * [I] infoPtr : valid pointer to the listview structure
2901 * RETURN:
2902 * item index
2904 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
2906 LONG lStyle = infoPtr->dwStyle;
2907 UINT uView = lStyle & LVS_TYPEMASK;
2908 INT nItem = 0;
2909 SCROLLINFO scrollInfo;
2911 scrollInfo.cbSize = sizeof(SCROLLINFO);
2912 scrollInfo.fMask = SIF_POS;
2914 if (uView == LVS_LIST)
2916 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
2917 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
2919 else if (uView == LVS_REPORT)
2921 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2922 nItem = scrollInfo.nPos;
2924 else
2926 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2927 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
2930 TRACE("nItem=%d\n", nItem);
2932 return nItem;
2935 /* used by the drawing code */
2936 typedef struct tagTEXTATTR
2938 int bkMode;
2939 COLORREF bkColor;
2940 COLORREF fgColor;
2941 } TEXTATTR;
2943 /* helper function for the drawing code */
2944 static inline void set_text_attr(HDC hdc, TEXTATTR *ta)
2946 ta->bkMode = SetBkMode(hdc, ta->bkMode);
2947 ta->bkColor = SetBkColor(hdc, ta->bkColor);
2948 ta->fgColor = SetTextColor(hdc, ta->fgColor);
2951 /* helper function for the drawing code */
2952 static void select_text_attr(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL isSelected, TEXTATTR *ta)
2954 ta->bkMode = OPAQUE;
2956 if (isSelected && infoPtr->bFocus)
2958 ta->bkColor = comctl32_color.clrHighlight;
2959 ta->fgColor = comctl32_color.clrHighlightText;
2961 else if (isSelected && (infoPtr->dwStyle & LVS_SHOWSELALWAYS))
2963 ta->bkColor = comctl32_color.clr3dFace;
2964 ta->fgColor = comctl32_color.clrBtnText;
2966 else if ( (infoPtr->clrTextBk != CLR_DEFAULT) && (infoPtr->clrTextBk != CLR_NONE) )
2968 ta->bkColor = infoPtr->clrTextBk;
2969 ta->fgColor = infoPtr->clrText;
2971 else
2973 ta->bkMode = TRANSPARENT;
2974 ta->bkColor = GetBkColor(hdc);
2975 ta->fgColor = infoPtr->clrText;
2978 set_text_attr(hdc, ta);
2981 /***
2982 * DESCRIPTION:
2983 * Erases the background of the given rectangle
2985 * PARAMETER(S):
2986 * [I] infoPtr : valid pointer to the listview structure
2987 * [I] hdc : device context handle
2988 * [I] lprcBox : clipping rectangle
2990 * RETURN:
2991 * Success: TRUE
2992 * Failure: FALSE
2994 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
2996 if (!infoPtr->hBkBrush) return FALSE;
2998 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3000 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3003 /***
3004 * DESCRIPTION:
3005 * Draws a subitem.
3007 * PARAMETER(S):
3008 * [I] infoPtr : valid pointer to the listview structure
3009 * [I] HDC : device context handle
3010 * [I] INT : item index
3011 * [I] INT : subitem index
3012 * [I] RECT * : clipping rectangle
3014 * RETURN:
3015 * Success: TRUE
3016 * Failure: FALSE
3018 static BOOL LISTVIEW_DrawSubItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem,
3019 INT nSubItem, RECT rcItem, UINT align)
3021 WCHAR szDispText[DISP_TEXT_SIZE];
3022 LVITEMW lvItem;
3024 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, rcItem=%s)\n",
3025 hdc, nItem, nSubItem, debugrect(&rcItem));
3027 /* get information needed for drawing the item */
3028 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3029 lvItem.iItem = nItem;
3030 lvItem.iSubItem = nSubItem;
3031 lvItem.cchTextMax = DISP_TEXT_SIZE;
3032 lvItem.pszText = szDispText;
3033 *lvItem.pszText = '\0';
3034 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3036 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3038 if (lvItem.iImage) FIXME("Draw the image for the subitem\n");
3040 DrawTextW(hdc, lvItem.pszText, -1, &rcItem,
3041 DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS | align);
3043 return TRUE;
3047 /***
3048 * DESCRIPTION:
3049 * Draws an item.
3051 * PARAMETER(S):
3052 * [I] infoPtr : valid pointer to the listview structure
3053 * [I] hdc : device context handle
3054 * [I] nItem : item index
3055 * [I] rcItem : item rectangle
3057 * RETURN:
3058 * TRUE: if item is focused
3059 * FALSE: otherwise
3061 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem)
3063 WCHAR szDispText[DISP_TEXT_SIZE];
3064 INT nLabelWidth, imagePadding = 0;
3065 RECT* lprcFocus, rcOrig = rcItem;
3066 LVITEMW lvItem;
3067 TEXTATTR ta;
3069 TRACE("(hdc=%x, nItem=%d)\n", hdc, nItem);
3071 /* get information needed for drawing the item */
3072 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3073 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3074 lvItem.iItem = nItem;
3075 lvItem.iSubItem = 0;
3076 lvItem.cchTextMax = DISP_TEXT_SIZE;
3077 lvItem.pszText = szDispText;
3078 *lvItem.pszText = '\0';
3079 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3080 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3082 /* now check if we need to update the focus rectangle */
3083 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3084 if (lprcFocus) SetRectEmpty(lprcFocus);
3086 /* do indent */
3087 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3089 /* state icons */
3090 if (infoPtr->himlState)
3092 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3093 if (uStateImage)
3095 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3096 rcItem.left, rcItem.top, ILD_NORMAL);
3098 rcItem.left += infoPtr->iconStateSize.cx;
3099 imagePadding = IMAGE_PADDING;
3102 /* small icons */
3103 if (infoPtr->himlSmall)
3105 if (lvItem.iImage >= 0)
3107 UINT mode = (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ?
3108 ILD_SELECTED : ILD_NORMAL;
3109 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3110 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc,
3111 rcItem.left, rcItem.top, mode);
3113 rcItem.left += infoPtr->iconSize.cx;
3114 imagePadding = IMAGE_PADDING;
3117 /* Don't bother painting item being edited */
3118 if (infoPtr->bEditing && lprcFocus)
3119 return FALSE;
3121 select_text_attr(infoPtr, hdc, lvItem.state & LVIS_SELECTED, &ta);
3123 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
3124 rcItem.left += imagePadding;
3125 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3126 if (rcItem.right > rcOrig.right) rcItem.right = rcOrig.right;
3128 if (lvItem.pszText)
3130 TRACE("drawing text=%s, in rect=%s\n", debugstr_w(lvItem.pszText), debugrect(&rcItem));
3131 if(lprcFocus) *lprcFocus = rcItem;
3132 if (lvItem.state & LVIS_SELECTED)
3133 ExtTextOutW(hdc, rcItem.left, rcItem.top, ETO_OPAQUE, &rcItem, 0, 0, 0);
3134 DrawTextW(hdc, lvItem.pszText, -1, &rcItem,
3135 DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS | DT_CENTER);
3138 set_text_attr(hdc, &ta);
3139 return lprcFocus != NULL;
3142 /***
3143 * DESCRIPTION:
3144 * Draws an item when in large icon display mode.
3146 * PARAMETER(S):
3147 * [I] infoPtr : valid pointer to the listview structure
3148 * [I] hdc : device context handle
3149 * [I] nItem : item index
3150 * [I] rcItem : clipping rectangle
3152 * RETURN:
3153 * TRUE: if item is focused
3154 * FALSE: otherwise
3156 static BOOL LISTVIEW_DrawLargeItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem)
3158 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3159 LVITEMW lvItem;
3160 UINT uFormat = LISTVIEW_DTFLAGS;
3161 RECT rcIcon, rcFocus, rcLabel, *lprcFocus;
3163 TRACE("(hdc=%x, nItem=%d, rcItem=%s)\n", hdc, nItem, debugrect(&rcItem));
3165 /* get information needed for drawing the item */
3166 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3167 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3168 lvItem.iItem = nItem;
3169 lvItem.iSubItem = 0;
3170 lvItem.cchTextMax = DISP_TEXT_SIZE;
3171 lvItem.pszText = szDispText;
3172 *lvItem.pszText = '\0';
3173 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3174 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3176 /* now check if we need to update the focus rectangle */
3177 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3179 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, &rcIcon, &rcLabel)) return FALSE;
3181 /* Set the item to the boundary box for now */
3182 TRACE("rcIcon=%s, rcLabel=%s\n", debugrect(&rcIcon), debugrect(&rcLabel));
3184 /* Figure out text colours etc. depending on state
3185 * At least the following states exist; there may be more.
3186 * Many items may be selected
3187 * At most one item may have the focus
3188 * The application may not actually be active currently
3189 * 1. The item is not selected in any way
3190 * 2. The cursor is flying over the icon or text and the text is being
3191 * expanded because it is not fully displayed currently.
3192 * 3. The item is selected and is focussed, i.e. the user has not clicked
3193 * in the blank area of the window, and the window (or application?)
3194 * still has the focus.
3195 * 4. As 3 except that a different window has the focus
3196 * 5. The item is the selected item of all the items, but the user has
3197 * clicked somewhere else on the window.
3198 * Only a few of these are handled currently. In particular 2 is not yet
3199 * handled since we do not support the functionality currently (or at least
3200 * we didn't when I wrote this)
3203 if (lvItem.state & LVIS_SELECTED)
3205 /* set item colors */
3206 SetBkColor(hdc, comctl32_color.clrHighlight);
3207 SetTextColor(hdc, comctl32_color.clrHighlightText);
3208 SetBkMode (hdc, OPAQUE);
3209 /* set raster mode */
3210 SetROP2(hdc, R2_XORPEN);
3211 /* When exactly is it in XOR? while being dragged? */
3213 else
3215 /* set item colors */
3216 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3218 SetBkMode(hdc, TRANSPARENT);
3220 else
3222 SetBkMode(hdc, OPAQUE);
3223 SetBkColor(hdc, infoPtr->clrTextBk);
3226 SetTextColor(hdc, infoPtr->clrText);
3227 /* set raster mode */
3228 SetROP2(hdc, R2_COPYPEN);
3231 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3232 * wrapping and long words split.
3233 * In cases 1 and 4 only a portion of the text is displayed with word
3234 * wrapping and both word and end ellipsis. (I don't yet know about path
3235 * ellipsis)
3237 uFormat |= lprcFocus ? DT_NOCLIP : DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3239 /* state icons */
3240 if (infoPtr->himlState != NULL)
3242 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3243 INT x, y;
3245 x = rcIcon.left - infoPtr->iconStateSize.cx + 10;
3246 y = rcIcon.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
3247 if (uStateImage > 0)
3248 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, x, y, ILD_NORMAL);
3251 /* draw the icon */
3252 if (infoPtr->himlNormal != NULL)
3254 if (lvItem.iImage >= 0)
3255 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc,
3256 rcIcon.left + ICON_LR_HALF, rcIcon.top + ICON_TOP_PADDING,
3257 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3260 /* Draw the text below the icon */
3262 /* Don't bother painting item being edited */
3263 if ((infoPtr->bEditing && lprcFocus) || !lvItem.pszText || !lstrlenW(lvItem.pszText))
3265 if(lprcFocus) SetRectEmpty(lprcFocus);
3266 return FALSE;
3269 /* draw label */
3271 /* I am sure of most of the uFormat values. However I am not sure about
3272 * whether we need or do not need the following:
3273 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3274 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3275 * We certainly do not need
3276 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3277 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3280 /* If the text is being drawn without clipping (i.e. the full text) then we
3281 * need to jump through a few hoops to ensure that it all gets displayed and
3282 * that the background is complete
3284 rcFocus = rcLabel; /* save for focus */
3285 if (lvItem.state & LVIS_SELECTED)
3286 ExtTextOutW(hdc, rcLabel.left, rcLabel.top, ETO_OPAQUE, &rcLabel, 0, 0, 0);
3287 /* else ? What if we are losing the focus? will we not get a complete
3288 * background?
3291 DrawTextW (hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3292 TRACE("text at rcLabel=%s is %s\n", debugrect(&rcLabel), debugstr_w(lvItem.pszText));
3294 if(lprcFocus) CopyRect(lprcFocus, &rcFocus);
3296 return lprcFocus != NULL;
3299 /***
3300 * DESCRIPTION:
3301 * Draws listview items when in report display mode.
3303 * PARAMETER(S):
3304 * [I] infoPtr : valid pointer to the listview structure
3305 * [I] HDC : device context handle
3307 * RETURN:
3308 * None
3310 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3312 INT rgntype, nDrawPosY, j;
3313 INT nTop, nItem, nLast, nUpdateHeight, nUpdateWidth;
3314 INT nColumnCount, nFirstCol, nLastCol;
3315 RECT rcItem, rcClip, rcFullSelect;
3316 BOOL bFullSelected, isFocused;
3317 DWORD cditemmode = CDRF_DODEFAULT;
3318 LONG lStyle = infoPtr->dwStyle;
3319 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3320 TEXTATTR tmpTa, oldTa;
3321 COLUMNCACHE *lpCols;
3322 LVCOLUMNW lvColumn;
3323 LVITEMW item;
3324 POINT ptOrig;
3326 TRACE("()\n");
3328 /* figure out what to draw */
3329 rgntype = GetClipBox(hdc, &rcClip);
3330 if (rgntype == NULLREGION) return;
3331 nUpdateHeight = rcClip.bottom - rcClip.top + 1;
3332 nUpdateWidth = rcClip.right - rcClip.left;
3333 nTop = LISTVIEW_GetTopIndex(infoPtr);
3334 nItem = nTop + (rcClip.top - infoPtr->rcList.top) / infoPtr->nItemHeight;
3335 if (nItem < nTop)
3336 nItem = nTop;
3337 nLast = nItem + nUpdateHeight / infoPtr->nItemHeight;
3338 if (nUpdateHeight % infoPtr->nItemHeight) nLast++;
3339 if (nLast > infoPtr->nItemCount)
3340 nLast = infoPtr->nItemCount;
3342 /* send cache hint notification */
3343 if (lStyle & LVS_OWNERDATA)
3344 notify_odcachehint(infoPtr, nItem, nLast);
3346 /* cache column info */
3347 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3348 lpCols = COMCTL32_Alloc(nColumnCount * sizeof(COLUMNCACHE));
3349 if (!lpCols) return;
3350 for (j = 0; j < nColumnCount; j++)
3352 Header_GetItemRect(infoPtr->hwndHeader, j, &lpCols[j].rc);
3353 TRACE("lpCols[%d].rc=%s\n", j, debugrect(&lpCols[j].rc));
3356 /* Get scroll info once before loop */
3357 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrig)) return;
3359 /* we now narrow the columns as well */
3360 nLastCol = nColumnCount - 1;
3361 for(nFirstCol = 0; nFirstCol < nColumnCount; nFirstCol++)
3362 if (lpCols[nFirstCol].rc.right + ptOrig.x >= rcClip.left) break;
3363 for(nLastCol = nColumnCount - 1; nLastCol >= 0; nLastCol--)
3364 if (lpCols[nLastCol].rc.left + ptOrig.x < rcClip.right) break;
3366 /* cache the per-column information before we start drawing */
3367 for (j = nFirstCol; j <= nLastCol; j++)
3369 lvColumn.mask = LVCF_FMT;
3370 LISTVIEW_GetColumnT(infoPtr, j, &lvColumn, TRUE);
3371 TRACE("lvColumn=%s\n", debuglvcolumn_t(&lvColumn, TRUE));
3372 lpCols[j].align = DT_LEFT;
3373 if (lvColumn.fmt & LVCFMT_RIGHT)
3374 lpCols[j].align = DT_RIGHT;
3375 else if (lvColumn.fmt & LVCFMT_CENTER)
3376 lpCols[j].align = DT_CENTER;
3379 /* a last few bits before we start drawing */
3380 TRACE("nTop=%d, nItem=%d, nLast=%d, nFirstCol=%d, nLastCol=%d\n",
3381 nTop, nItem, nLast, nFirstCol, nLastCol);
3382 bFullSelected = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT;
3383 nDrawPosY = infoPtr->rcList.top + (nItem - nTop) * infoPtr->nItemHeight;
3385 /* save dc values we're gonna trash while drawing */
3386 oldTa.bkMode = GetBkMode(hdc);
3387 oldTa.bkColor = GetBkColor(hdc);
3388 oldTa.fgColor = GetTextColor(hdc);
3390 /* iterate through the invalidated rows */
3391 for (; nItem < nLast; nItem++, nDrawPosY += infoPtr->nItemHeight)
3393 /* if owner wants to take a first stab at it, have it his way... */
3394 if (lStyle & LVS_OWNERDRAWFIXED)
3396 DRAWITEMSTRUCT dis;
3398 TRACE("Owner Drawn\n");
3400 item.iItem = nItem;
3401 item.iSubItem = 0;
3402 item.mask = LVIF_PARAM | LVIF_STATE;
3403 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3404 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3406 ZeroMemory(&dis, sizeof(dis));
3407 dis.CtlType = ODT_LISTVIEW;
3408 dis.CtlID = uID;
3409 dis.itemID = nItem;
3410 dis.itemAction = ODA_DRAWENTIRE;
3411 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3412 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3413 dis.hwndItem = infoPtr->hwndSelf;
3414 dis.hDC = hdc;
3415 dis.rcItem.left = lpCols[0].rc.left;
3416 dis.rcItem.right = lpCols[nColumnCount - 1].rc.right;
3417 dis.rcItem.top = nDrawPosY;
3418 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3419 OffsetRect(&dis.rcItem, ptOrig.x, 0);
3420 dis.itemData = item.lParam;
3422 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3423 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3424 /* In theory we should do the default drawing if WM_DRAWITEM
3425 * returns FALSE but, in the words of Larry McVoy, in practice
3426 * theory is different than practice, and hence there are
3427 * important apps out there that depend on no default drawing
3428 * in LVS_OWNERDRAWFIXED. So we always skip to the next item. */
3429 continue;
3432 /* compute the full select rectangle, if needed */
3433 if (bFullSelected)
3435 item.mask = LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3436 item.stateMask = LVIS_SELECTED;
3437 item.iItem = nItem;
3438 item.iSubItem = 0;
3439 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3441 rcFullSelect.left = lpCols[0].rc.left + REPORT_MARGINX +
3442 infoPtr->iconSize.cx * item.iIndent +
3443 (infoPtr->himlSmall ? infoPtr->iconSize.cx : 0);
3444 rcFullSelect.right = max(rcFullSelect.left, lpCols[nColumnCount - 1].rc.right - REPORT_MARGINX);
3445 rcFullSelect.top = nDrawPosY;
3446 rcFullSelect.bottom = rcFullSelect.top + infoPtr->nItemHeight;
3447 OffsetRect(&rcFullSelect, ptOrig.x, 0);
3450 /* draw the background of the selection rectangle, if need be */
3451 select_text_attr(infoPtr, hdc, bFullSelected && (item.state & LVIS_SELECTED), &tmpTa);
3452 if (bFullSelected && (item.state & LVIS_SELECTED))
3453 ExtTextOutW(hdc, rcFullSelect.left, rcFullSelect.top, ETO_OPAQUE, &rcFullSelect, 0, 0, 0);
3455 /* iterate through the invalidated columns */
3456 isFocused = FALSE;
3457 for (j = nFirstCol; j <= nLastCol; j++)
3459 if (cdmode & CDRF_NOTIFYITEMDRAW)
3460 cditemmode = notify_customdrawitem (infoPtr, hdc, nItem, j, CDDS_ITEMPREPAINT);
3461 if (cditemmode & CDRF_SKIPDEFAULT) continue;
3463 rcItem = lpCols[j].rc;
3464 rcItem.left += REPORT_MARGINX;
3465 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3466 rcItem.top = nDrawPosY;
3467 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3469 /* Offset the Scroll Bar Pos */
3470 OffsetRect(&rcItem, ptOrig.x, 0);
3472 if (j == 0)
3473 isFocused = LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem);
3474 else
3475 LISTVIEW_DrawSubItem(infoPtr, hdc, nItem, j, rcItem, lpCols[j].align);
3477 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3478 notify_customdrawitem(infoPtr, hdc, nItem, 0, CDDS_ITEMPOSTPAINT);
3481 /* Adjust focus if we have it, and we are in full select */
3482 if (bFullSelected && isFocused) infoPtr->rcFocus = rcFullSelect;
3485 /* cleanup the mess */
3486 set_text_attr(hdc, &oldTa);
3487 COMCTL32_Free(lpCols);
3490 /***
3491 * DESCRIPTION:
3492 * Draws listview items when in list display mode.
3494 * PARAMETER(S):
3495 * [I] infoPtr : valid pointer to the listview structure
3496 * [I] HDC : device context handle
3498 * RETURN:
3499 * None
3501 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3503 RECT rcItem;
3504 INT i, j;
3505 INT nItem;
3506 INT nColumnCount;
3507 INT nCountPerColumn;
3508 INT nItemWidth = infoPtr->nItemWidth;
3509 INT nItemHeight = infoPtr->nItemHeight;
3510 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3511 DWORD cditemmode = CDRF_DODEFAULT;
3513 /* get number of fully visible columns */
3514 nColumnCount = nListWidth / nItemWidth;
3515 if (nListWidth % nItemWidth) nColumnCount++;
3516 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
3517 nItem = ListView_GetTopIndex(infoPtr->hwndSelf);
3518 TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3519 nColumnCount, nCountPerColumn, nItem);
3521 for (i = 0; i < nColumnCount; i++)
3523 for (j = 0; j < nCountPerColumn; j++, nItem++)
3525 if (nItem >= infoPtr->nItemCount)
3526 return;
3528 if (cdmode & CDRF_NOTIFYITEMDRAW)
3529 cditemmode = notify_customdrawitem (infoPtr, hdc, nItem, 0, CDDS_ITEMPREPAINT);
3530 if (cditemmode & CDRF_SKIPDEFAULT)
3531 continue;
3533 rcItem.top = j * nItemHeight;
3534 rcItem.left = i * nItemWidth;
3535 rcItem.bottom = rcItem.top + nItemHeight;
3536 rcItem.right = rcItem.left + nItemWidth;
3538 LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem);
3540 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3541 notify_customdrawitem(infoPtr, hdc, nItem, 0, CDDS_ITEMPOSTPAINT);
3547 /***
3548 * DESCRIPTION:
3549 * Draws listview items when in icon or small icon display mode.
3551 * PARAMETER(S):
3552 * [I] infoPtr : valid pointer to the listview structure
3553 * [I] HDC : device context handle
3555 * RETURN:
3556 * None
3558 static void LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL bSmall, DWORD cdmode)
3560 POINT ptPosition;
3561 RECT rcItem, rcClip, rcTemp;
3562 INT i;
3563 DWORD cditemmode = CDRF_DODEFAULT;
3565 TRACE("\n");
3567 GetClipBox(hdc, &rcClip);
3569 /* Draw the visible non-selected items */
3570 for (i = 0; i < infoPtr->nItemCount; i++)
3572 if (LISTVIEW_GetItemState(infoPtr,i,LVIS_SELECTED))
3573 continue;
3575 rcItem.left = LVIR_BOUNDS;
3576 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
3577 if (!IntersectRect(&rcTemp, &rcItem, &rcClip))
3578 continue;
3580 if (cdmode & CDRF_NOTIFYITEMDRAW)
3581 cditemmode = notify_customdrawitem (infoPtr, hdc, i, 0, CDDS_ITEMPREPAINT);
3582 if (cditemmode & CDRF_SKIPDEFAULT)
3583 continue;
3585 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3587 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3589 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3591 if (ptPosition.y < infoPtr->rcList.bottom)
3593 if (ptPosition.x < infoPtr->rcList.right)
3595 rcItem.top = ptPosition.y;
3596 rcItem.left = ptPosition.x;
3597 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3598 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3600 if (bSmall)
3601 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem);
3602 else
3603 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem);
3608 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3609 notify_customdrawitem(infoPtr, hdc, i, 0, CDDS_ITEMPOSTPAINT);
3612 /* Draw the visible selected items */
3613 for (i = 0; i < infoPtr->nItemCount; i++)
3615 if (!LISTVIEW_GetItemState(infoPtr,i,LVIS_SELECTED))
3616 continue;
3618 rcItem.left = LVIR_BOUNDS;
3619 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
3620 if (!IntersectRect(&rcTemp, &rcItem, &rcClip))
3621 continue;
3623 if (cdmode & CDRF_NOTIFYITEMDRAW)
3624 cditemmode = notify_customdrawitem (infoPtr, hdc, i, 0, CDDS_ITEMPREPAINT);
3625 if (cditemmode & CDRF_SKIPDEFAULT)
3626 continue;
3628 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3630 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3632 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3634 if (ptPosition.y < infoPtr->rcList.bottom)
3636 if (ptPosition.x < infoPtr->rcList.right)
3638 rcItem.top = ptPosition.y;
3639 rcItem.left = ptPosition.x;
3640 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3641 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3643 if (bSmall)
3644 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem);
3645 else
3646 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem);
3651 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3652 notify_customdrawitem(infoPtr, hdc, i, 0, CDDS_ITEMPOSTPAINT);
3656 /***
3657 * DESCRIPTION:
3658 * Draws listview items.
3660 * PARAMETER(S):
3661 * [I] infoPtr : valid pointer to the listview structure
3662 * [I] HDC : device context handle
3664 * RETURN:
3665 * NoneX
3667 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3669 UINT uView = LISTVIEW_GetType(infoPtr);
3670 HFONT hOldFont;
3671 DWORD cdmode;
3672 RECT rcClient;
3674 LISTVIEW_DUMP(infoPtr);
3676 GetClientRect(infoPtr->hwndSelf, &rcClient);
3678 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, hdc, rcClient);
3679 if (cdmode == CDRF_SKIPDEFAULT) return;
3681 infoPtr->bIsDrawing = TRUE;
3683 /* nothing to draw */
3684 if(infoPtr->nItemCount == 0) goto enddraw;
3686 /* select font */
3687 hOldFont = SelectObject(hdc, infoPtr->hFont);
3689 if (uView == LVS_LIST)
3690 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3691 else if (uView == LVS_REPORT)
3692 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3693 else
3694 LISTVIEW_RefreshIcon(infoPtr, hdc, uView == LVS_SMALLICON, cdmode);
3696 /* if we have a focus rect, draw it */
3697 if (infoPtr->bFocus && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
3698 DrawFocusRect(hdc, &infoPtr->rcFocus);
3700 /* unselect objects */
3701 SelectObject(hdc, hOldFont);
3703 enddraw:
3704 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3705 notify_customdraw(infoPtr, CDDS_POSTPAINT, hdc, rcClient);
3707 infoPtr->bIsDrawing = FALSE;
3711 /***
3712 * DESCRIPTION:
3713 * Calculates the approximate width and height of a given number of items.
3715 * PARAMETER(S):
3716 * [I] infoPtr : valid pointer to the listview structure
3717 * [I] INT : number of items
3718 * [I] INT : width
3719 * [I] INT : height
3721 * RETURN:
3722 * Returns a DWORD. The width in the low word and the height in high word.
3724 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3725 WORD wWidth, WORD wHeight)
3727 UINT uView = LISTVIEW_GetType(infoPtr);
3728 INT nItemCountPerColumn = 1;
3729 INT nColumnCount = 0;
3730 DWORD dwViewRect = 0;
3732 if (nItemCount == -1)
3733 nItemCount = infoPtr->nItemCount;
3735 if (uView == LVS_LIST)
3737 if (wHeight == 0xFFFF)
3739 /* use current height */
3740 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3743 if (wHeight < infoPtr->nItemHeight)
3744 wHeight = infoPtr->nItemHeight;
3746 if (nItemCount > 0)
3748 if (infoPtr->nItemHeight > 0)
3750 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3751 if (nItemCountPerColumn == 0)
3752 nItemCountPerColumn = 1;
3754 if (nItemCount % nItemCountPerColumn != 0)
3755 nColumnCount = nItemCount / nItemCountPerColumn;
3756 else
3757 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3761 /* Microsoft padding magic */
3762 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3763 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3765 dwViewRect = MAKELONG(wWidth, wHeight);
3767 else if (uView == LVS_REPORT)
3768 FIXME("uView == LVS_REPORT: not implemented\n");
3769 else if (uView == LVS_SMALLICON)
3770 FIXME("uView == LVS_SMALLICON: not implemented\n");
3771 else if (uView == LVS_ICON)
3772 FIXME("uView == LVS_ICON: not implemented\n");
3774 return dwViewRect;
3777 /***
3778 * DESCRIPTION:
3779 * Arranges listview items in icon display mode.
3781 * PARAMETER(S):
3782 * [I] infoPtr : valid pointer to the listview structure
3783 * [I] INT : alignment code
3785 * RETURN:
3786 * SUCCESS : TRUE
3787 * FAILURE : FALSE
3789 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3791 UINT uView = LISTVIEW_GetType(infoPtr);
3792 BOOL bResult = FALSE;
3794 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3796 switch (nAlignCode)
3798 case LVA_ALIGNLEFT:
3799 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3800 break;
3801 case LVA_ALIGNTOP:
3802 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3803 break;
3804 case LVA_DEFAULT:
3805 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3806 break;
3807 case LVA_SNAPTOGRID:
3808 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3809 break;
3813 return bResult;
3816 /* << LISTVIEW_CreateDragImage >> */
3819 /***
3820 * DESCRIPTION:
3821 * Removes all listview items and subitems.
3823 * PARAMETER(S):
3824 * [I] infoPtr : valid pointer to the listview structure
3826 * RETURN:
3827 * SUCCESS : TRUE
3828 * FAILURE : FALSE
3830 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3832 LONG lStyle = infoPtr->dwStyle;
3833 UINT uView = lStyle & LVS_TYPEMASK;
3834 LISTVIEW_ITEM *lpItem;
3835 LISTVIEW_SUBITEM *lpSubItem;
3836 NMLISTVIEW nmlv;
3837 BOOL bSuppress;
3838 BOOL bResult = FALSE;
3839 HDPA hdpaSubItems;
3841 TRACE("()\n");
3843 LISTVIEW_RemoveAllSelections(infoPtr);
3844 infoPtr->nSelectionMark=-1;
3845 infoPtr->nFocusedItem=-1;
3846 /* But we are supposed to leave nHotItem as is! */
3848 if (lStyle & LVS_OWNERDATA)
3850 infoPtr->nItemCount = 0;
3851 LISTVIEW_InvalidateList(infoPtr);
3852 return TRUE;
3855 if (infoPtr->nItemCount > 0)
3857 INT i, j;
3859 /* send LVN_DELETEALLITEMS notification */
3860 /* verify if subsequent LVN_DELETEITEM notifications should be
3861 suppressed */
3862 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3863 nmlv.iItem = -1;
3864 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3866 for (i = 0; i < infoPtr->nItemCount; i++)
3868 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3869 if (hdpaSubItems != NULL)
3871 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3873 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3874 if (lpSubItem != NULL)
3876 /* free subitem string */
3877 if (is_textW(lpSubItem->hdr.pszText))
3878 COMCTL32_Free(lpSubItem->hdr.pszText);
3880 /* free subitem */
3881 COMCTL32_Free(lpSubItem);
3885 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3886 if (lpItem != NULL)
3888 if (!bSuppress)
3890 /* send LVN_DELETEITEM notification */
3891 nmlv.iItem = i;
3892 nmlv.lParam = lpItem->lParam;
3893 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3896 /* free item string */
3897 if (is_textW(lpItem->hdr.pszText))
3898 COMCTL32_Free(lpItem->hdr.pszText);
3900 /* free item */
3901 COMCTL32_Free(lpItem);
3904 DPA_Destroy(hdpaSubItems);
3908 /* reinitialize listview memory */
3909 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3910 infoPtr->nItemCount = 0;
3911 DPA_DeleteAllPtrs(infoPtr->hdpaPosX);
3912 DPA_DeleteAllPtrs(infoPtr->hdpaPosY);
3914 /* align items (set position of each item) */
3915 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3917 if (lStyle & LVS_ALIGNLEFT)
3919 LISTVIEW_AlignLeft(infoPtr);
3921 else
3923 LISTVIEW_AlignTop(infoPtr);
3927 LISTVIEW_UpdateScroll(infoPtr);
3929 LISTVIEW_InvalidateList(infoPtr);
3932 return bResult;
3935 /***
3936 * DESCRIPTION:
3937 * Removes a column from the listview control.
3939 * PARAMETER(S):
3940 * [I] infoPtr : valid pointer to the listview structure
3941 * [I] INT : column index
3943 * RETURN:
3944 * SUCCESS : TRUE
3945 * FAILURE : FALSE
3947 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3949 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3950 RECT rcCol, rcOld;
3952 TRACE("nColumn=%d\n", nColumn);
3954 if (nColumn <= 0) return FALSE;
3956 if (uView == LVS_REPORT)
3958 if (!Header_GetItemRect(infoPtr->hwndHeader, nColumn, &rcCol))
3959 return FALSE;
3961 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3962 return FALSE;
3965 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3967 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3968 HDPA hdpaSubItems;
3969 INT nItem, nSubItem, i;
3971 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3973 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3974 if (!hdpaSubItems) continue;
3975 nSubItem = 0;
3976 lpDelItem = 0;
3977 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3979 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3980 if (!lpSubItem) break;
3981 if (lpSubItem->iSubItem == nColumn)
3983 nSubItem = i;
3984 lpDelItem = lpSubItem;
3986 else if (lpSubItem->iSubItem > nColumn)
3988 lpSubItem->iSubItem--;
3992 /* if we found our subitem, zapp it */
3993 if (nSubItem > 0)
3995 /* free string */
3996 if (is_textW(lpDelItem->hdr.pszText))
3997 COMCTL32_Free(lpDelItem->hdr.pszText);
3999 /* free item */
4000 COMCTL32_Free(lpDelItem);
4002 /* free dpa memory */
4003 DPA_DeletePtr(hdpaSubItems, nSubItem);
4008 /* we need to worry about display issues in report mode only */
4009 if (uView != LVS_REPORT) return TRUE;
4011 /* if we have a focus, must first erase the focus rect */
4012 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, infoPtr->nFocusedItem, FALSE);
4014 /* Need to reset the item width when deleting a column */
4015 infoPtr->nItemWidth -= rcCol.right - rcCol.left;
4017 /* update scrollbar(s) */
4018 LISTVIEW_UpdateScroll(infoPtr);
4020 /* scroll to cover the deleted column, and invalidate for redraw */
4021 rcOld = infoPtr->rcList;
4022 rcOld.left = rcCol.left;
4023 ScrollWindowEx(infoPtr->hwndSelf, -(rcCol.right - rcCol.left), 0,
4024 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4026 /* we can restore focus now */
4027 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, infoPtr->nFocusedItem, TRUE);
4029 return TRUE;
4032 /***
4033 * DESCRIPTION:
4034 * Removes an item from the listview control.
4036 * PARAMETER(S):
4037 * [I] infoPtr : valid pointer to the listview structure
4038 * [I] INT : item index
4040 * RETURN:
4041 * SUCCESS : TRUE
4042 * FAILURE : FALSE
4044 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4046 LONG lStyle = infoPtr->dwStyle;
4047 UINT uView = lStyle & LVS_TYPEMASK;
4048 LONG lCtrlId = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
4049 NMLISTVIEW nmlv;
4050 BOOL bResult = FALSE;
4051 HDPA hdpaSubItems;
4052 LISTVIEW_ITEM *lpItem;
4053 LISTVIEW_SUBITEM *lpSubItem;
4054 INT i;
4055 LVITEMW item;
4057 TRACE("(nItem=%d)\n", nItem);
4060 /* First, send LVN_DELETEITEM notification. */
4061 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4062 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
4063 nmlv.hdr.idFrom = lCtrlId;
4064 nmlv.hdr.code = LVN_DELETEITEM;
4065 nmlv.iItem = nItem;
4066 SendMessageW((infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)lCtrlId,
4067 (LPARAM)&nmlv);
4070 /* remove it from the selection range */
4071 item.state = LVIS_SELECTED;
4072 item.stateMask = LVIS_SELECTED;
4073 LISTVIEW_SetItemState(infoPtr,nItem,&item);
4075 if (lStyle & LVS_OWNERDATA)
4077 infoPtr->nItemCount--;
4078 LISTVIEW_InvalidateList(infoPtr); /*FIXME: optimize */
4079 return TRUE;
4082 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
4084 /* initialize memory */
4085 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4087 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4088 if (hdpaSubItems != NULL)
4090 infoPtr->nItemCount--;
4091 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4093 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4094 if (lpSubItem != NULL)
4096 /* free item string */
4097 if (is_textW(lpSubItem->hdr.pszText))
4098 COMCTL32_Free(lpSubItem->hdr.pszText);
4100 /* free item */
4101 COMCTL32_Free(lpSubItem);
4105 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4106 if (lpItem != NULL)
4108 /* free item string */
4109 if (is_textW(lpItem->hdr.pszText))
4110 COMCTL32_Free(lpItem->hdr.pszText);
4112 /* free item */
4113 COMCTL32_Free(lpItem);
4116 bResult = DPA_Destroy(hdpaSubItems);
4117 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4118 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4121 LISTVIEW_ShiftIndices(infoPtr,nItem,-1);
4123 /* align items (set position of each item) */
4124 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4126 if (lStyle & LVS_ALIGNLEFT)
4127 LISTVIEW_AlignLeft(infoPtr);
4128 else
4129 LISTVIEW_AlignTop(infoPtr);
4132 LISTVIEW_UpdateScroll(infoPtr);
4134 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
4137 return bResult;
4141 /***
4142 * DESCRIPTION:
4143 * Callback implementation for editlabel control
4145 * PARAMETER(S):
4146 * [I] infoPtr : valid pointer to the listview structure
4147 * [I] pszText : modified text
4148 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4150 * RETURN:
4151 * SUCCESS : TRUE
4152 * FAILURE : FALSE
4154 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4156 NMLVDISPINFOW dispInfo;
4158 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4160 infoPtr->bEditing = FALSE;
4162 ZeroMemory(&dispInfo, sizeof(dispInfo));
4163 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4164 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4165 dispInfo.item.iSubItem = 0;
4166 dispInfo.item.stateMask = ~0;
4167 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4168 dispInfo.item.pszText = pszText;
4169 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4171 /* Do we need to update the Item Text */
4172 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4173 if (!pszText) return TRUE;
4175 ZeroMemory(&dispInfo, sizeof(dispInfo));
4176 dispInfo.item.mask = LVIF_TEXT;
4177 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4178 dispInfo.item.iSubItem = 0;
4179 dispInfo.item.pszText = pszText;
4180 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4181 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4184 /***
4185 * DESCRIPTION:
4186 * Begin in place editing of specified list view item
4188 * PARAMETER(S):
4189 * [I] infoPtr : valid pointer to the listview structure
4190 * [I] INT : item index
4191 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4193 * RETURN:
4194 * SUCCESS : TRUE
4195 * FAILURE : FALSE
4197 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4199 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4200 NMLVDISPINFOW dispInfo;
4201 RECT rect;
4203 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4205 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4207 infoPtr->nEditLabelItem = nItem;
4209 /* Is the EditBox still there, if so remove it */
4210 if(infoPtr->hwndEdit != 0)
4212 SetFocus(infoPtr->hwndSelf);
4213 infoPtr->hwndEdit = 0;
4216 LISTVIEW_SetSelection(infoPtr, nItem);
4217 LISTVIEW_SetItemFocus(infoPtr, nItem);
4219 rect.left = LVIR_LABEL;
4220 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4222 ZeroMemory(&dispInfo, sizeof(dispInfo));
4223 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4224 dispInfo.item.iItem = nItem;
4225 dispInfo.item.iSubItem = 0;
4226 dispInfo.item.stateMask = ~0;
4227 dispInfo.item.pszText = szDispText;
4228 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4229 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4231 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4232 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4233 if (!infoPtr->hwndEdit) return 0;
4235 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4237 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4238 infoPtr->hwndEdit = 0;
4239 return 0;
4242 infoPtr->bEditing = TRUE;
4243 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4244 SetFocus(infoPtr->hwndEdit);
4245 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4246 return infoPtr->hwndEdit;
4250 /***
4251 * DESCRIPTION:
4252 * Ensures the specified item is visible, scrolling into view if necessary.
4254 * PARAMETER(S):
4255 * [I] infoPtr : valid pointer to the listview structure
4256 * [I] nItem : item index
4257 * [I] bPartial : partially or entirely visible
4259 * RETURN:
4260 * SUCCESS : TRUE
4261 * FAILURE : FALSE
4263 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4265 UINT uView = LISTVIEW_GetType(infoPtr);
4266 INT nScrollPosHeight = 0;
4267 INT nScrollPosWidth = 0;
4268 INT nHorzAdjust = 0;
4269 INT nVertAdjust = 0;
4270 INT nHorzDiff = 0;
4271 INT nVertDiff = 0;
4272 RECT rcItem;
4274 /* FIXME: ALWAYS bPartial == FALSE, FOR NOW! */
4276 rcItem.left = LVIR_BOUNDS;
4277 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4279 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4281 /* scroll left/right, but in LVS_REPORT mode */
4282 if (uView == LVS_LIST)
4283 nScrollPosWidth = infoPtr->nItemWidth;
4284 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4285 nScrollPosWidth = 1;
4287 if (rcItem.left < infoPtr->rcList.left)
4289 nHorzAdjust = -1;
4290 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4292 else
4294 nHorzAdjust = 1;
4295 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4299 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4301 /* scroll up/down, but not in LVS_LIST mode */
4302 if (uView == LVS_REPORT)
4303 nScrollPosHeight = infoPtr->nItemHeight;
4304 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4305 nScrollPosHeight = 1;
4307 if (rcItem.top < infoPtr->rcList.top)
4309 nVertAdjust = -1;
4310 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4312 else
4314 nVertAdjust = 1;
4315 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4319 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4321 if (nScrollPosWidth)
4323 INT diff = nHorzDiff / nScrollPosWidth;
4324 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4325 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4328 if (nScrollPosHeight)
4330 INT diff = nVertDiff / nScrollPosHeight;
4331 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4332 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4335 return TRUE;
4338 /***
4339 * DESCRIPTION:
4340 * Retrieves the nearest item, given a position and a direction.
4342 * PARAMETER(S):
4343 * [I] infoPtr : valid pointer to the listview structure
4344 * [I] POINT : start position
4345 * [I] UINT : direction
4347 * RETURN:
4348 * Item index if successdful, -1 otherwise.
4350 static INT LISTVIEW_GetNearestItem(LISTVIEW_INFO *infoPtr, POINT pt, UINT vkDirection)
4352 LVHITTESTINFO ht;
4353 RECT rcView;
4355 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4356 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4357 ((vkDirection == VK_UP) ? "VK_UP" :
4358 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4360 if (!LISTVIEW_GetViewRect(infoPtr, &rcView)) return -1;
4362 if (!LISTVIEW_GetOrigin(infoPtr, &ht.pt)) return -1;
4364 ht.pt.x += pt.x;
4365 ht.pt.y += pt.y;
4367 if (vkDirection == VK_DOWN) ht.pt.y += infoPtr->nItemHeight;
4368 else if (vkDirection == VK_UP) ht.pt.y -= infoPtr->nItemHeight;
4369 else if (vkDirection == VK_LEFT) ht.pt.x -= infoPtr->nItemWidth;
4370 else if (vkDirection == VK_RIGHT) ht.pt.x += infoPtr->nItemWidth;
4372 if (!PtInRect(&rcView, ht.pt)) return -1;
4374 return LISTVIEW_SuperHitTestItem(infoPtr, &ht, TRUE, TRUE);
4377 /***
4378 * DESCRIPTION:
4379 * Searches for an item with specific characteristics.
4381 * PARAMETER(S):
4382 * [I] hwnd : window handle
4383 * [I] nStart : base item index
4384 * [I] lpFindInfo : item information to look for
4386 * RETURN:
4387 * SUCCESS : index of item
4388 * FAILURE : -1
4390 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4391 LPLVFINDINFOW lpFindInfo)
4393 POINT ptItem;
4394 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4395 LVITEMW lvItem;
4396 BOOL bWrap = FALSE;
4397 INT nItem = nStart;
4398 INT nLast = infoPtr->nItemCount;
4400 if ((nItem >= -1) && (lpFindInfo != NULL))
4402 lvItem.mask = 0;
4403 if (lpFindInfo->flags & LVFI_PARAM)
4405 lvItem.mask |= LVIF_PARAM;
4408 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4410 lvItem.mask |= LVIF_TEXT;
4411 lvItem.pszText = szDispText;
4412 lvItem.cchTextMax = DISP_TEXT_SIZE;
4415 if (lpFindInfo->flags & LVFI_WRAP)
4416 bWrap = TRUE;
4418 if (lpFindInfo->flags & LVFI_NEARESTXY)
4420 ptItem.x = lpFindInfo->pt.x;
4421 ptItem.y = lpFindInfo->pt.y;
4424 while (1)
4426 while (nItem < nLast)
4428 if (lpFindInfo->flags & LVFI_NEARESTXY)
4430 nItem = LISTVIEW_GetNearestItem(infoPtr, ptItem,
4431 lpFindInfo->vkDirection);
4432 if (nItem != -1)
4434 /* get position of the new item index */
4435 if (!ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &ptItem))
4436 return -1;
4438 else
4439 return -1;
4441 else
4443 nItem++;
4446 lvItem.iItem = nItem;
4447 lvItem.iSubItem = 0;
4448 if (LISTVIEW_GetItemW(infoPtr, &lvItem))
4450 if (lvItem.mask & LVIF_TEXT)
4452 if (lpFindInfo->flags & LVFI_PARTIAL)
4454 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4455 continue;
4457 else
4459 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4460 continue;
4464 if (lvItem.mask & LVIF_PARAM)
4466 if (lpFindInfo->lParam != lvItem.lParam)
4467 continue;
4470 return nItem;
4474 if (bWrap)
4476 nItem = -1;
4477 nLast = nStart + 1;
4478 bWrap = FALSE;
4480 else
4482 return -1;
4487 return -1;
4490 /***
4491 * DESCRIPTION:
4492 * Searches for an item with specific characteristics.
4494 * PARAMETER(S):
4495 * [I] hwnd : window handle
4496 * [I] nStart : base item index
4497 * [I] lpFindInfo : item information to look for
4499 * RETURN:
4500 * SUCCESS : index of item
4501 * FAILURE : -1
4503 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4504 LPLVFINDINFOA lpFindInfo)
4506 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4507 LVFINDINFOW fiw;
4508 LRESULT res;
4510 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4511 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4512 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4513 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4514 return res;
4517 /***
4518 * DESCRIPTION:
4519 * Retrieves the background image of the listview control.
4521 * PARAMETER(S):
4522 * [I] infoPtr : valid pointer to the listview structure
4523 * [O] LPLVMKBIMAGE : background image attributes
4525 * RETURN:
4526 * SUCCESS : TRUE
4527 * FAILURE : FALSE
4529 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4530 /* { */
4531 /* FIXME (listview, "empty stub!\n"); */
4532 /* return FALSE; */
4533 /* } */
4535 /***
4536 * DESCRIPTION:
4537 * Retrieves column attributes.
4539 * PARAMETER(S):
4540 * [I] infoPtr : valid pointer to the listview structure
4541 * [I] INT : column index
4542 * [IO] LPLVCOLUMNW : column information
4543 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4544 * otherwise it is in fact a LPLVCOLUMNA
4546 * RETURN:
4547 * SUCCESS : TRUE
4548 * FAILURE : FALSE
4550 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4552 HDITEMW hdi;
4553 BOOL bResult = FALSE;
4555 if (lpColumn != NULL)
4558 /* initialize memory */
4559 ZeroMemory(&hdi, sizeof(hdi));
4561 if (lpColumn->mask & LVCF_FMT)
4562 hdi.mask |= HDI_FORMAT;
4564 if (lpColumn->mask & LVCF_WIDTH)
4565 hdi.mask |= HDI_WIDTH;
4567 if (lpColumn->mask & LVCF_TEXT)
4569 hdi.mask |= HDI_TEXT;
4570 hdi.cchTextMax = lpColumn->cchTextMax;
4571 hdi.pszText = lpColumn->pszText;
4574 if (lpColumn->mask & LVCF_IMAGE)
4575 hdi.mask |= HDI_IMAGE;
4577 if (lpColumn->mask & LVCF_ORDER)
4578 hdi.mask |= HDI_ORDER;
4580 if (isW)
4581 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4582 else
4583 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4585 if (bResult)
4587 if (lpColumn->mask & LVCF_FMT)
4589 lpColumn->fmt = 0;
4591 if (hdi.fmt & HDF_LEFT)
4592 lpColumn->fmt |= LVCFMT_LEFT;
4593 else if (hdi.fmt & HDF_RIGHT)
4594 lpColumn->fmt |= LVCFMT_RIGHT;
4595 else if (hdi.fmt & HDF_CENTER)
4596 lpColumn->fmt |= LVCFMT_CENTER;
4598 if (hdi.fmt & HDF_IMAGE)
4599 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4601 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4602 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4605 if (lpColumn->mask & LVCF_WIDTH)
4606 lpColumn->cx = hdi.cxy;
4608 if (lpColumn->mask & LVCF_IMAGE)
4609 lpColumn->iImage = hdi.iImage;
4611 if (lpColumn->mask & LVCF_ORDER)
4612 lpColumn->iOrder = hdi.iOrder;
4614 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4615 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4620 return bResult;
4624 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4626 INT i;
4628 if (!lpiArray)
4629 return FALSE;
4631 /* FIXME: little hack */
4632 for (i = 0; i < iCount; i++)
4633 lpiArray[i] = i;
4635 return TRUE;
4638 /***
4639 * DESCRIPTION:
4640 * Retrieves the column width.
4642 * PARAMETER(S):
4643 * [I] infoPtr : valid pointer to the listview structure
4644 * [I] int : column index
4646 * RETURN:
4647 * SUCCESS : column width
4648 * FAILURE : zero
4650 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4652 INT nColumnWidth = 0;
4653 HDITEMW hdi;
4655 TRACE("nColumn=%d\n", nColumn);
4657 switch(LISTVIEW_GetType(infoPtr))
4659 case LVS_LIST:
4660 nColumnWidth = infoPtr->nItemWidth;
4661 break;
4662 case LVS_REPORT:
4663 hdi.mask = HDI_WIDTH;
4664 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4665 nColumnWidth = hdi.cxy;
4666 break;
4667 default:
4668 /* we don't have a 'column' in [SMALL]ICON mode */
4671 TRACE("nColumnWidth=%d\n", nColumnWidth);
4672 return nColumnWidth;
4675 /***
4676 * DESCRIPTION:
4677 * In list or report display mode, retrieves the number of items that can fit
4678 * vertically in the visible area. In icon or small icon display mode,
4679 * retrieves the total number of visible items.
4681 * PARAMETER(S):
4682 * [I] infoPtr : valid pointer to the listview structure
4684 * RETURN:
4685 * Number of fully visible items.
4687 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4689 UINT uView = LISTVIEW_GetType(infoPtr);
4690 INT nItemCount = 0;
4692 if (uView == LVS_LIST)
4694 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4696 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4697 LISTVIEW_GetCountPerColumn(infoPtr);
4700 else if (uView == LVS_REPORT)
4702 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4704 else
4706 nItemCount = infoPtr->nItemCount;
4709 return nItemCount;
4713 /***
4714 * DESCRIPTION:
4715 * Retrieves an image list handle.
4717 * PARAMETER(S):
4718 * [I] infoPtr : valid pointer to the listview structure
4719 * [I] nImageList : image list identifier
4721 * RETURN:
4722 * SUCCESS : image list handle
4723 * FAILURE : NULL
4725 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4727 HIMAGELIST himl = NULL;
4729 switch (nImageList)
4731 case LVSIL_NORMAL:
4732 himl = infoPtr->himlNormal;
4733 break;
4734 case LVSIL_SMALL:
4735 himl = infoPtr->himlSmall;
4736 break;
4737 case LVSIL_STATE:
4738 himl = infoPtr->himlState;
4739 break;
4742 return (LRESULT)himl;
4745 /* LISTVIEW_GetISearchString */
4747 /***
4748 * Helper function for LISTVIEW_GetItemT *only*. Tests if an item is selected.
4749 * It is important that no other functions call this because of callbacks.
4751 static inline BOOL is_item_selected(LISTVIEW_INFO *infoPtr, INT nItem)
4753 RANGE selection = { nItem, nItem };
4755 return DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
4756 LISTVIEW_CompareSelectionRanges, 0, DPAS_SORTED) != -1;
4759 /***
4760 * DESCRIPTION:
4761 * Retrieves item attributes.
4763 * PARAMETER(S):
4764 * [I] hwnd : window handle
4765 * [IO] lpLVItem : item info
4766 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4767 * if FALSE, the lpLVItem is a LPLVITEMA.
4769 * NOTE:
4770 * This is the internal 'GetItem' interface -- it tries to
4771 * be smart, and avoids text copies, if possible, by modifing
4772 * lpLVItem->pszText to point to the text string. Please note
4773 * that this is not always possible (e.g. OWNERDATA), so on
4774 * entry you *must* supply valid values for pszText, and cchTextMax.
4775 * The only difference to the documented interface is that upon
4776 * return, you should use *only* the lpLVItem->pszText, rather than
4777 * the buffer pointer you provided on input. Most code already does
4778 * that, so it's not a problem.
4779 * For the two cases when the text must be copied (that is,
4780 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4782 * RETURN:
4783 * SUCCESS : TRUE
4784 * FAILURE : FALSE
4786 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4788 NMLVDISPINFOW dispInfo;
4789 LISTVIEW_ITEM *lpItem;
4790 ITEMHDR* pItemHdr;
4791 HDPA hdpaSubItems;
4793 /* In the following:
4794 * lpLVItem describes the information requested by the user
4795 * lpItem is what we have
4796 * dispInfo is a structure we use to request the missing
4797 * information from the application
4800 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4802 if (!lpLVItem || (lpLVItem->iItem < 0) ||
4803 (lpLVItem->iItem >= infoPtr->nItemCount))
4804 return FALSE;
4806 /* a quick optimization if all we're asked is the focus state
4807 * these queries are worth optimising since they are common,
4808 * and can be answered in constant time, without the heavy accesses */
4809 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIF_STATE) &&
4810 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4812 lpLVItem->state = 0;
4813 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4814 lpLVItem->state |= LVIS_FOCUSED;
4815 return TRUE;
4818 ZeroMemory(&dispInfo, sizeof(dispInfo));
4820 /* if the app stores all the data, handle it separately */
4821 if (infoPtr->dwStyle & LVS_OWNERDATA)
4823 dispInfo.item.state = 0;
4825 /* if we need to callback, do it now */
4826 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4828 /* NOTE: copy only fields which we _know_ are initialized, some apps
4829 * depend on the uninitialized fields being 0 */
4830 dispInfo.item.mask = lpLVItem->mask;
4831 dispInfo.item.iItem = lpLVItem->iItem;
4832 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4833 if (lpLVItem->mask & LVIF_TEXT)
4835 dispInfo.item.pszText = lpLVItem->pszText;
4836 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4838 if (lpLVItem->mask & LVIF_STATE)
4839 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4840 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4841 dispInfo.item.stateMask = lpLVItem->stateMask;
4842 *lpLVItem = dispInfo.item;
4843 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4846 /* we store only a little state, so if we're not asked, we're done */
4847 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4849 /* if focus is handled by us, report it */
4850 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4852 lpLVItem->state &= ~LVIS_FOCUSED;
4853 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4854 lpLVItem->state |= LVIS_FOCUSED;
4857 /* and do the same for selection, if we handle it */
4858 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4860 lpLVItem->state &= ~LVIS_SELECTED;
4861 if (is_item_selected(infoPtr, lpLVItem->iItem))
4862 lpLVItem->state |= LVIS_SELECTED;
4865 return TRUE;
4868 /* find the item and subitem structures before we proceed */
4869 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4870 if (hdpaSubItems == NULL) return FALSE;
4872 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4873 return FALSE;
4875 if (lpLVItem->iSubItem)
4877 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4878 if(!lpSubItem) return FALSE;
4879 pItemHdr = &lpSubItem->hdr;
4881 else
4882 pItemHdr = &lpItem->hdr;
4884 /* Do we need to query the state from the app? */
4885 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4887 dispInfo.item.mask |= LVIF_STATE;
4888 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4891 /* Do we need to enquire about the image? */
4892 if ((lpLVItem->mask & LVIF_IMAGE) && (pItemHdr->iImage==I_IMAGECALLBACK))
4893 dispInfo.item.mask |= LVIF_IMAGE;
4895 /* Do we need to enquire about the text? */
4896 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4898 dispInfo.item.mask |= LVIF_TEXT;
4899 dispInfo.item.pszText = lpLVItem->pszText;
4900 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4901 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4902 *dispInfo.item.pszText = '\0';
4905 /* If we don't have all the requested info, query the application */
4906 if (dispInfo.item.mask != 0)
4908 dispInfo.item.iItem = lpLVItem->iItem;
4909 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4910 dispInfo.item.lParam = lpItem->lParam;
4911 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4912 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4915 /* Now, handle the iImage field */
4916 if (dispInfo.item.mask & LVIF_IMAGE)
4918 lpLVItem->iImage = dispInfo.item.iImage;
4919 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (pItemHdr->iImage==I_IMAGECALLBACK))
4920 pItemHdr->iImage = dispInfo.item.iImage;
4922 else if (lpLVItem->mask & LVIF_IMAGE)
4923 lpLVItem->iImage = pItemHdr->iImage;
4925 /* The pszText field */
4926 if (dispInfo.item.mask & LVIF_TEXT)
4928 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4929 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4931 lpLVItem->pszText = dispInfo.item.pszText;
4933 else if (lpLVItem->mask & LVIF_TEXT)
4935 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4936 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4939 /* if this is a subitem, we're done*/
4940 if (lpLVItem->iSubItem) return TRUE;
4942 /* Next is the lParam field */
4943 if (dispInfo.item.mask & LVIF_PARAM)
4945 lpLVItem->lParam = dispInfo.item.lParam;
4946 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4947 lpItem->lParam = dispInfo.item.lParam;
4949 else if (lpLVItem->mask & LVIF_PARAM)
4950 lpLVItem->lParam = lpItem->lParam;
4952 /* ... the state field (this one is different due to uCallbackmask) */
4953 if (lpLVItem->mask & LVIF_STATE)
4955 lpLVItem->state = lpItem->state;
4956 if (dispInfo.item.mask & LVIF_STATE)
4958 lpLVItem->state &= ~dispInfo.item.stateMask;
4959 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4961 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4963 lpLVItem->state &= ~LVIS_FOCUSED;
4964 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4965 lpLVItem->state |= LVIS_FOCUSED;
4967 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4969 lpLVItem->state &= ~LVIS_SELECTED;
4970 if (is_item_selected(infoPtr, lpLVItem->iItem))
4971 lpLVItem->state |= LVIS_SELECTED;
4975 /* and last, but not least, the indent field */
4976 if (lpLVItem->mask & LVIF_INDENT)
4977 lpLVItem->iIndent = lpItem->iIndent;
4979 return TRUE;
4982 /***
4983 * DESCRIPTION:
4984 * Retrieves item attributes.
4986 * PARAMETER(S):
4987 * [I] hwnd : window handle
4988 * [IO] lpLVItem : item info
4989 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4990 * if FALSE, the lpLVItem is a LPLVITEMA.
4992 * NOTE:
4993 * This is the external 'GetItem' interface -- it properly copies
4994 * the text in the provided buffer.
4996 * RETURN:
4997 * SUCCESS : TRUE
4998 * FAILURE : FALSE
5000 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5002 LPWSTR pszText;
5003 BOOL bResult;
5005 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5006 return FALSE;
5008 pszText = lpLVItem->pszText;
5009 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5010 if (bResult && lpLVItem->pszText != pszText)
5011 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5012 lpLVItem->pszText = pszText;
5014 return bResult;
5018 /***
5019 * DESCRIPTION:
5020 * Retrieves the position (upper-left) of the listview control item.
5021 * Note that for LVS_ICON style, the upper-left is that of the icon
5022 * and not the bounding box.
5024 * PARAMETER(S):
5025 * [I] infoPtr : valid pointer to the listview structure
5026 * [I] nItem : item index
5027 * [O] lpptPosition : coordinate information
5029 * RETURN:
5030 * SUCCESS : TRUE
5031 * FAILURE : FALSE
5033 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5035 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5036 RECT Box;
5038 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5040 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5042 /* These should be very cheap to compute */
5043 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, &Box, NULL, NULL, NULL)) return FALSE;
5045 lpptPosition->x = Box.left;
5046 lpptPosition->y = Box.top;
5047 if (uView == LVS_ICON)
5049 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5050 lpptPosition->y += ICON_TOP_PADDING;
5053 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5054 return TRUE;
5058 /***
5059 * DESCRIPTION:
5060 * Retrieves the bounding rectangle for a listview control item.
5062 * PARAMETER(S):
5063 * [I] infoPtr : valid pointer to the listview structure
5064 * [I] nItem : item index
5065 * [IO] lprc : bounding rectangle coordinates
5066 * lprc->left specifies the portion of the item for which the bounding
5067 * rectangle will be retrieved.
5069 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5070 * including the icon and label.
5072 * * For LVS_ICON
5073 * * Experiment shows that native control returns:
5074 * * width = min (48, length of text line)
5075 * * .left = position.x - (width - iconsize.cx)/2
5076 * * .right = .left + width
5077 * * height = #lines of text * ntmHeight + icon height + 8
5078 * * .top = position.y - 2
5079 * * .bottom = .top + height
5080 * * separation between items .y = itemSpacing.cy - height
5081 * * .x = itemSpacing.cx - width
5082 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5084 * * For LVS_ICON
5085 * * Experiment shows that native control returns:
5086 * * width = iconSize.cx + 16
5087 * * .left = position.x - (width - iconsize.cx)/2
5088 * * .right = .left + width
5089 * * height = iconSize.cy + 4
5090 * * .top = position.y - 2
5091 * * .bottom = .top + height
5092 * * separation between items .y = itemSpacing.cy - height
5093 * * .x = itemSpacing.cx - width
5094 * LVIR_LABEL Returns the bounding rectangle of the item text.
5096 * * For LVS_ICON
5097 * * Experiment shows that native control returns:
5098 * * width = text length
5099 * * .left = position.x - width/2
5100 * * .right = .left + width
5101 * * height = ntmH * linecount + 2
5102 * * .top = position.y + iconSize.cy + 6
5103 * * .bottom = .top + height
5104 * * separation between items .y = itemSpacing.cy - height
5105 * * .x = itemSpacing.cx - width
5106 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5107 * rectangles, but excludes columns in report view.
5109 * RETURN:
5110 * SUCCESS : TRUE
5111 * FAILURE : FALSE
5113 * NOTES
5114 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5115 * upon whether the window has the focus currently and on whether the item
5116 * is the one with the focus. Ensure that the control's record of which
5117 * item has the focus agrees with the items' records.
5119 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5121 RECT label_rect;
5123 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5125 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5127 switch(lprc->left)
5129 case LVIR_ICON:
5130 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, lprc, NULL)) return FALSE;
5131 break;
5133 case LVIR_LABEL:
5134 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, NULL, lprc)) return FALSE;
5135 break;
5137 case LVIR_BOUNDS:
5138 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, lprc, NULL, NULL)) return FALSE;
5139 break;
5141 case LVIR_SELECTBOUNDS:
5142 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, lprc, NULL, &label_rect)) return FALSE;
5143 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
5144 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) )
5145 lprc->right = label_rect.right;
5146 break;
5148 default:
5149 WARN("Unknown value: %d\n", lprc->left);
5150 return FALSE;
5153 TRACE(" rect=%s\n", debugrect(lprc));
5155 return TRUE;
5158 /***
5159 * DESCRIPTION:
5160 * Retrieves the spacing between listview control items.
5162 * PARAMETER(S):
5163 * [I] infoPtr : valid pointer to the listview structure
5164 * [IO] lprc : rectangle to receive the output
5165 * on input, lprc->top = nSubItem
5166 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5168 * NOTE: this call is succeeds only for REPORT style listviews.
5169 * Because we can calculate things much faster in report mode,
5170 * we're gonna do the calculations inline here, instead of
5171 * calling functions that do heavy lifting.
5173 * RETURN:
5174 * TRUE: success
5175 * FALSE: failure
5177 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5179 POINT ptPosition;
5180 INT nSubItem, flags;
5182 if (!lprc || LISTVIEW_GetType(infoPtr) != LVS_REPORT) return FALSE;
5184 nSubItem = lprc->top;
5185 flags = lprc->left;
5187 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, nSubItem);
5189 if (!Header_GetItemRect(infoPtr->hwndHeader, nSubItem, lprc)) return FALSE;
5190 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptPosition)) return FALSE;
5191 lprc->top = ptPosition.y;
5192 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5194 switch(flags)
5196 case LVIR_ICON:
5197 FIXME("Unimplemented LVIR_ICON\n");
5198 return FALSE;
5199 case LVIR_LABEL:
5200 case LVIR_BOUNDS:
5201 /* nothing to do here, we're done */
5202 break;
5203 default:
5204 ERR("Unknown bounds=%d\n", lprc->left);
5205 return FALSE;
5207 return TRUE;
5211 /***
5212 * DESCRIPTION:
5213 * Retrieves the width of a label.
5215 * PARAMETER(S):
5216 * [I] infoPtr : valid pointer to the listview structure
5218 * RETURN:
5219 * SUCCESS : string width (in pixels)
5220 * FAILURE : zero
5222 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5224 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5225 LVITEMW lvItem;
5227 TRACE("(nItem=%d)\n", nItem);
5229 lvItem.mask = LVIF_TEXT;
5230 lvItem.iItem = nItem;
5231 lvItem.iSubItem = 0;
5232 lvItem.pszText = szDispText;
5233 lvItem.cchTextMax = DISP_TEXT_SIZE;
5234 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5236 /* FIXME: is this right? What if the label is very long? */
5237 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5240 /***
5241 * DESCRIPTION:
5242 * Retrieves the spacing between listview control items.
5244 * PARAMETER(S):
5245 * [I] infoPtr : valid pointer to the listview structure
5246 * [I] BOOL : flag for small or large icon
5248 * RETURN:
5249 * Horizontal + vertical spacing
5251 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5253 LONG lResult;
5255 if (!bSmall)
5257 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5259 else
5261 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
5262 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5263 else
5264 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5266 return lResult;
5269 /***
5270 * DESCRIPTION:
5271 * Retrieves the state of a listview control item.
5273 * PARAMETER(S):
5274 * [I] infoPtr : valid pointer to the listview structure
5275 * [I] nItem : item index
5276 * [I] uMask : state mask
5278 * RETURN:
5279 * State specified by the mask.
5281 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5283 LVITEMW lvItem;
5285 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5287 lvItem.iItem = nItem;
5288 lvItem.iSubItem = 0;
5289 lvItem.mask = LVIF_STATE;
5290 lvItem.stateMask = uMask;
5291 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5293 return lvItem.state & uMask;
5296 /***
5297 * DESCRIPTION:
5298 * Retrieves the text of a listview control item or subitem.
5300 * PARAMETER(S):
5301 * [I] hwnd : window handle
5302 * [I] nItem : item index
5303 * [IO] lpLVItem : item information
5304 * [I] isW : TRUE if lpLVItem is Unicode
5306 * RETURN:
5307 * SUCCESS : string length
5308 * FAILURE : 0
5310 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5312 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5314 lpLVItem->mask = LVIF_TEXT;
5315 lpLVItem->iItem = nItem;
5316 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5318 return textlenT(lpLVItem->pszText, isW);
5321 /***
5322 * DESCRIPTION:
5323 * Searches for an item based on properties + relationships.
5325 * PARAMETER(S):
5326 * [I] infoPtr : valid pointer to the listview structure
5327 * [I] nItem : item index
5328 * [I] uFlags : relationship flag
5330 * FIXME:
5331 * This function is ver, very inefficient! Needs work.
5333 * RETURN:
5334 * SUCCESS : item index
5335 * FAILURE : -1
5337 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5339 UINT uView = LISTVIEW_GetType(infoPtr);
5340 UINT uMask = 0;
5341 LVFINDINFOW lvFindInfo;
5342 INT nCountPerColumn;
5343 INT i;
5345 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5346 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5348 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5350 if (uFlags & LVNI_CUT)
5351 uMask |= LVIS_CUT;
5353 if (uFlags & LVNI_DROPHILITED)
5354 uMask |= LVIS_DROPHILITED;
5356 if (uFlags & LVNI_FOCUSED)
5357 uMask |= LVIS_FOCUSED;
5359 if (uFlags & LVNI_SELECTED)
5360 uMask |= LVIS_SELECTED;
5362 /* if we're asked for the focused item, that's only one,
5363 * so it's worth optimizing */
5364 if (uFlags & LVNI_FOCUSED)
5366 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5367 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5370 if (uFlags & LVNI_ABOVE)
5372 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5374 while (nItem >= 0)
5376 nItem--;
5377 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5378 return nItem;
5381 else
5383 lvFindInfo.flags = LVFI_NEARESTXY;
5384 lvFindInfo.vkDirection = VK_UP;
5385 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5386 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5388 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5389 return nItem;
5393 else if (uFlags & LVNI_BELOW)
5395 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5397 while (nItem < infoPtr->nItemCount)
5399 nItem++;
5400 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5401 return nItem;
5404 else
5406 lvFindInfo.flags = LVFI_NEARESTXY;
5407 lvFindInfo.vkDirection = VK_DOWN;
5408 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5409 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5411 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5412 return nItem;
5416 else if (uFlags & LVNI_TOLEFT)
5418 if (uView == LVS_LIST)
5420 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5421 while (nItem - nCountPerColumn >= 0)
5423 nItem -= nCountPerColumn;
5424 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5425 return nItem;
5428 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5430 lvFindInfo.flags = LVFI_NEARESTXY;
5431 lvFindInfo.vkDirection = VK_LEFT;
5432 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5433 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5435 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5436 return nItem;
5440 else if (uFlags & LVNI_TORIGHT)
5442 if (uView == LVS_LIST)
5444 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5445 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5447 nItem += nCountPerColumn;
5448 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5449 return nItem;
5452 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5454 lvFindInfo.flags = LVFI_NEARESTXY;
5455 lvFindInfo.vkDirection = VK_RIGHT;
5456 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5457 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5459 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5460 return nItem;
5464 else
5466 nItem++;
5468 /* search by index */
5469 for (i = nItem; i < infoPtr->nItemCount; i++)
5471 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5472 return i;
5476 return -1;
5479 /* LISTVIEW_GetNumberOfWorkAreas */
5481 /***
5482 * DESCRIPTION:
5483 * Retrieves the origin coordinates when in icon or small icon display mode.
5485 * PARAMETER(S):
5486 * [I] infoPtr : valid pointer to the listview structure
5487 * [O] lpptOrigin : coordinate information
5489 * RETURN:
5490 * SUCCESS : TRUE
5491 * FAILURE : FALSE
5493 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5495 DWORD lStyle = infoPtr->dwStyle;
5496 UINT uView = lStyle & LVS_TYPEMASK;
5497 INT nHorzPos = 0, nVertPos = 0;
5498 SCROLLINFO scrollInfo;
5500 if (!lpptOrigin) return FALSE;
5502 scrollInfo.cbSize = sizeof(SCROLLINFO);
5503 scrollInfo.fMask = SIF_POS;
5505 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5506 nHorzPos = scrollInfo.nPos;
5507 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5508 nVertPos = scrollInfo.nPos;
5510 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5512 lpptOrigin->x = infoPtr->rcList.left;
5513 lpptOrigin->y = infoPtr->rcList.top;
5514 if (uView == LVS_LIST)
5515 nHorzPos *= infoPtr->nItemWidth;
5516 else if (uView == LVS_REPORT)
5517 nVertPos *= infoPtr->nItemHeight;
5519 lpptOrigin->x -= nHorzPos;
5520 lpptOrigin->y -= nVertPos;
5522 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5524 return TRUE;
5527 /***
5528 * DESCRIPTION:
5529 * Retrieves the width of a string.
5531 * PARAMETER(S):
5532 * [I] hwnd : window handle
5533 * [I] lpszText : text string to process
5534 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5536 * RETURN:
5537 * SUCCESS : string width (in pixels)
5538 * FAILURE : zero
5540 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5542 SIZE stringSize;
5544 stringSize.cx = 0;
5545 if (is_textT(lpszText, isW))
5547 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5548 HDC hdc = GetDC(infoPtr->hwndSelf);
5549 HFONT hOldFont = SelectObject(hdc, hFont);
5551 if (isW)
5552 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5553 else
5554 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5555 SelectObject(hdc, hOldFont);
5556 ReleaseDC(infoPtr->hwndSelf, hdc);
5558 return stringSize.cx;
5562 /***
5563 * DESCRIPTION:
5564 * Determines item if a hit or closest if not
5566 * PARAMETER(S):
5567 * [I] infoPtr : valid pointer to the listview structure
5568 * [IO] lpht : hit test information
5569 * [I] subitem : fill out iSubItem.
5570 * [I] bNearItem : return the nearest item
5572 * RETURN:
5573 * SUCCESS : item index of hit
5574 * FAILURE : -1
5576 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL bNearItem)
5578 LONG lStyle = infoPtr->dwStyle;
5579 UINT uView = lStyle & LVS_TYPEMASK;
5580 INT i,j,topindex,bottomindex,nearestItem;
5581 RECT rcItem,rcSubItem;
5582 DWORD xterm, yterm, dist, mindist;
5584 TRACE("(lpht->pt=%s, subitem=%d\n", debugpoint(&lpht->pt), subitem);
5586 nearestItem = -1;
5587 mindist = -1;
5589 /* FIXME: get the visible range */
5590 topindex = LISTVIEW_GetTopIndex(infoPtr);
5591 if (uView == LVS_REPORT)
5593 bottomindex = topindex + LISTVIEW_GetCountPerColumn(infoPtr) + 1;
5594 bottomindex = min(bottomindex,infoPtr->nItemCount);
5596 else
5598 bottomindex = infoPtr->nItemCount;
5601 for (i = topindex; i < bottomindex; i++)
5603 rcItem.left = LVIR_BOUNDS;
5604 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5606 if (PtInRect(&rcItem, lpht->pt))
5608 rcSubItem = rcItem;
5609 rcItem.left = LVIR_ICON;
5610 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5612 if (PtInRect(&rcItem, lpht->pt))
5614 lpht->flags = LVHT_ONITEMICON;
5615 lpht->iItem = i;
5616 goto set_subitem;
5620 rcItem.left = LVIR_LABEL;
5621 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5623 if (PtInRect(&rcItem, lpht->pt))
5625 lpht->flags = LVHT_ONITEMLABEL;
5626 lpht->iItem = i;
5627 goto set_subitem;
5631 lpht->flags = LVHT_ONITEMSTATEICON;
5632 lpht->iItem = i;
5633 set_subitem:
5634 if (subitem)
5636 INT nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5637 lpht->iSubItem = 0;
5638 rcSubItem.right = rcSubItem.left;
5639 for (j = 0; j < nColumnCount; j++)
5641 rcSubItem.left = rcSubItem.right;
5642 rcSubItem.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5643 if (PtInRect(&rcSubItem, lpht->pt))
5645 lpht->iSubItem = j;
5646 break;
5650 TRACE("hit on item %d\n", i);
5651 return i;
5653 else if (bNearItem)
5656 * Now compute distance from point to center of boundary
5657 * box. Since we are only interested in the relative
5658 * distance, we can skip the nasty square root operation
5660 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpht->pt.x;
5661 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpht->pt.y;
5662 dist = xterm * xterm + yterm * yterm;
5663 if (mindist < 0 || dist < mindist)
5665 mindist = dist;
5666 nearestItem = i;
5672 lpht->flags = LVHT_NOWHERE;
5674 return bNearItem ? nearestItem : -1;
5678 /***
5679 * DESCRIPTION:
5680 * Determines which listview item is located at the specified position.
5682 * PARAMETER(S):
5683 * [I] infoPtr : valid pointer to the listview structure
5684 * [IO] lpht : hit test information
5685 * [I] subitem : fill out iSubItem.
5687 * RETURN:
5688 * SUCCESS : item index
5689 * FAILURE : -1
5691 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem)
5693 TRACE("(x=%ld, y=%ld)\n", lpht->pt.x, lpht->pt.y);
5695 lpht->flags = 0;
5696 lpht->iItem = -1;
5697 if (subitem) lpht->iSubItem = 0;
5699 if (infoPtr->rcList.left > lpht->pt.x)
5700 lpht->flags |= LVHT_TOLEFT;
5701 else if (infoPtr->rcList.right < lpht->pt.x)
5702 lpht->flags |= LVHT_TORIGHT;
5704 if (infoPtr->rcList.top > lpht->pt.y)
5705 lpht->flags |= LVHT_ABOVE;
5706 else if (infoPtr->rcList.bottom < lpht->pt.y)
5707 lpht->flags |= LVHT_BELOW;
5709 if (lpht->flags) return -1;
5711 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
5712 * an app might pass only a structure with space up to iItem!
5713 * (MS Office 97 does that for instance in the file open dialog)
5715 return LISTVIEW_SuperHitTestItem(infoPtr, lpht, subitem, FALSE);
5719 /***
5720 * DESCRIPTION:
5721 * Inserts a new column.
5723 * PARAMETER(S):
5724 * [I] infoPtr : valid pointer to the listview structure
5725 * [I] INT : column index
5726 * [I] LPLVCOLUMNW : column information
5728 * RETURN:
5729 * SUCCESS : new column index
5730 * FAILURE : -1
5732 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5733 LPLVCOLUMNW lpColumn, BOOL isW)
5735 RECT rcOld, rcCol;
5736 INT nNewColumn;
5737 HDITEMW hdi;
5739 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5741 if (!lpColumn) return -1;
5743 hdi.mask = hdi.fmt = 0;
5744 if (lpColumn->mask & LVCF_FMT)
5746 /* format member is valid */
5747 hdi.mask |= HDI_FORMAT;
5749 /* set text alignment (leftmost column must be left-aligned) */
5750 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5751 hdi.fmt |= HDF_LEFT;
5752 else if (lpColumn->fmt & LVCFMT_RIGHT)
5753 hdi.fmt |= HDF_RIGHT;
5754 else if (lpColumn->fmt & LVCFMT_CENTER)
5755 hdi.fmt |= HDF_CENTER;
5757 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5758 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5760 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5762 hdi.fmt |= HDF_IMAGE;
5763 hdi.iImage = I_IMAGECALLBACK;
5766 if (lpColumn->fmt & LVCFMT_IMAGE)
5767 ; /* FIXME: enable images for *(sub)items* this column */
5770 if (lpColumn->mask & LVCF_WIDTH)
5772 hdi.mask |= HDI_WIDTH;
5773 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5775 /* make it fill the remainder of the controls width */
5776 HDITEMW hdit;
5777 RECT rcHeader;
5778 INT item_index;
5780 /* get the width of every item except the current one */
5781 hdit.mask = HDI_WIDTH;
5782 hdi.cxy = 0;
5784 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5785 if (Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit)))
5786 hdi.cxy += hdit.cxy;
5788 /* retrieve the layout of the header */
5789 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5790 TRACE("start cxy=%d rcHeader=%s\n", hdi.cxy, debugrect(&rcHeader));
5792 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
5794 else
5795 hdi.cxy = lpColumn->cx;
5798 if (lpColumn->mask & LVCF_TEXT)
5800 hdi.mask |= HDI_TEXT | HDI_FORMAT;
5801 hdi.fmt |= HDF_STRING;
5802 hdi.pszText = lpColumn->pszText;
5803 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
5806 if (lpColumn->mask & LVCF_IMAGE)
5808 hdi.mask |= HDI_IMAGE;
5809 hdi.iImage = lpColumn->iImage;
5812 if (lpColumn->mask & LVCF_ORDER)
5814 hdi.mask |= HDI_ORDER;
5815 hdi.iOrder = lpColumn->iOrder;
5818 /* insert item in header control */
5819 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5820 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5821 (WPARAM)nColumn, (LPARAM)&hdi);
5822 if (nNewColumn == -1) return -1;
5823 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &rcCol)) return -1;
5825 /* now we have to actually adjust the data */
5826 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
5828 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
5829 HDPA hdpaSubItems;
5830 INT nItem, i;
5832 /* preallocate memory, so we can fail gracefully */
5833 if (nNewColumn == 0)
5835 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->nItemCount);
5836 if (!lpNewItems) return -1;
5837 for (i = 0; i < infoPtr->nItemCount; i++)
5838 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
5839 if (i != infoPtr->nItemCount)
5841 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5842 COMCTL32_Free(lpNewItems);
5843 return -1;
5847 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5849 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5850 if (!hdpaSubItems) continue;
5851 for (i = 1; i < hdpaSubItems->nItemCount; i++)
5853 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
5854 if (!lpSubItem) break;
5855 if (lpSubItem->iSubItem >= nNewColumn)
5856 lpSubItem->iSubItem++;
5859 /* if we found our subitem, zapp it */
5860 if (nNewColumn == 0)
5862 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
5863 lpSubItem = lpNewItems[nItem];
5864 lpSubItem->hdr = lpMainItem->hdr;
5865 lpSubItem->iSubItem = 1;
5866 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
5867 lpMainItem->iSubItem = 0;
5868 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
5872 COMCTL32_Free(lpNewItems);
5875 /* we don't have to worry abiut display issues in non-report mode */
5876 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return nNewColumn;
5878 /* if we have a focus, must first erase the focus rect */
5879 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, infoPtr->nFocusedItem, FALSE);
5881 /* Need to reset the item width when inserting a new column */
5882 infoPtr->nItemWidth += rcCol.right - rcCol.left;
5884 LISTVIEW_UpdateScroll(infoPtr);
5886 /* scroll to cover the deleted column, and invalidate for redraw */
5887 rcOld = infoPtr->rcList;
5888 rcOld.left = rcCol.left;
5889 ScrollWindowEx(infoPtr->hwndSelf, rcCol.right - rcCol.left, 0,
5890 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5892 /* we can restore focus now */
5893 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, infoPtr->nFocusedItem, TRUE);
5895 return nNewColumn;
5898 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5899 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5900 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5901 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5902 their own sort proc. when sending LVM_SORTITEMS.
5904 /* Platform SDK:
5905 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5907 LVS_SORTXXX must be specified,
5908 LVS_OWNERDRAW is not set,
5909 <item>.pszText is not LPSTR_TEXTCALLBACK.
5911 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5912 are sorted based on item text..."
5914 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5916 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5917 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5918 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5920 /* if we're sorting descending, negate the return value */
5921 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5924 /***
5925 * nESCRIPTION:
5926 * Inserts a new item in the listview control.
5928 * PARAMETER(S):
5929 * [I] infoPtr : valid pointer to the listview structure
5930 * [I] lpLVItem : item information
5931 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5933 * RETURN:
5934 * SUCCESS : new item index
5935 * FAILURE : -1
5937 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5939 LONG lStyle = infoPtr->dwStyle;
5940 UINT uView = lStyle & LVS_TYPEMASK;
5941 INT nItem = -1;
5942 HDPA hdpaSubItems;
5943 NMLISTVIEW nmlv;
5944 LISTVIEW_ITEM *lpItem;
5945 BOOL is_sorted;
5947 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5949 if (lStyle & LVS_OWNERDATA)
5951 nItem = infoPtr->nItemCount;
5952 infoPtr->nItemCount++;
5953 return nItem;
5956 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5957 if (!lpLVItem || lpLVItem->iSubItem) return -1;
5959 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5961 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5962 return -1;
5964 /* insert item in listview control data structure */
5965 if ( (hdpaSubItems = DPA_Create(8)) )
5966 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5967 if (nItem == -1) goto fail;
5969 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5970 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5972 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
5973 is_sorted ? infoPtr->nItemCount + 1 : lpLVItem->iItem,
5974 hdpaSubItems );
5975 if (nItem == -1) goto fail;
5976 infoPtr->nItemCount++;
5978 if (!LISTVIEW_SetItemT(infoPtr, lpLVItem, isW))
5979 goto undo;
5981 /* if we're sorted, sort the list, and update the index */
5982 if (is_sorted)
5984 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5985 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5986 if (nItem == -1)
5988 ERR("We can't find the item we just inserted, possible memory corruption.");
5989 /* we can't remove it from the list if we can't find it, so just fail */
5990 /* we don't deallocate memory here, as it will probably cause more problems */
5991 return -1;
5995 /* make room for the position, if we are in the right mode */
5996 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5998 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5999 goto undo;
6000 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6002 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6003 goto undo;
6007 /* Add the subitem list to the items array. Do this last in case we go to
6008 * fail during the above.
6010 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6012 lpItem->valid = TRUE;
6014 /* send LVN_INSERTITEM notification */
6015 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6016 nmlv.iItem = nItem;
6017 nmlv.lParam = lpItem->lParam;
6018 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6020 /* align items (set position of each item) */
6021 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6023 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
6024 else LISTVIEW_AlignTop(infoPtr);
6027 LISTVIEW_UpdateScroll(infoPtr);
6029 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6031 TRACE(" <- %d\n", nItem);
6032 return nItem;
6034 undo:
6035 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6036 infoPtr->nItemCount--;
6037 fail:
6038 DPA_DeletePtr(hdpaSubItems, 0);
6039 DPA_Destroy (hdpaSubItems);
6040 COMCTL32_Free (lpItem);
6041 return -1;
6044 /***
6045 * DESCRIPTION:
6046 * Redraws a range of items.
6048 * PARAMETER(S):
6049 * [I] infoPtr : valid pointer to the listview structure
6050 * [I] INT : first item
6051 * [I] INT : last item
6053 * RETURN:
6054 * SUCCESS : TRUE
6055 * FAILURE : FALSE
6057 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6059 INT i;
6061 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6062 max(nFirst, nLast) >= infoPtr->nItemCount)
6063 return FALSE;
6065 for (i = nFirst; i <= nLast; i++)
6066 LISTVIEW_InvalidateItem(infoPtr, i);
6068 return TRUE;
6071 /***
6072 * DESCRIPTION:
6073 * Scroll the content of a listview.
6075 * PARAMETER(S):
6076 * [I] infoPtr : valid pointer to the listview structure
6077 * [I] INT : horizontal scroll amount in pixels
6078 * [I] INT : vertical scroll amount in pixels
6080 * RETURN:
6081 * SUCCESS : TRUE
6082 * FAILURE : FALSE
6084 * COMMENTS:
6085 * If the control is in report mode (LVS_REPORT) the control can
6086 * be scrolled only in line increments. "dy" will be rounded to the
6087 * nearest number of pixels that are a whole line. Ex: if line height
6088 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6089 * is passed the the scroll will be 0. (per MSDN 7/2002)
6091 * For: (per experimentaion with native control and CSpy ListView)
6092 * LVS_ICON dy=1 = 1 pixel (vertical only)
6093 * dx ignored
6094 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6095 * dx ignored
6096 * LVS_LIST dx=1 = 1 column (horizontal only)
6097 * but will only scroll 1 column per message
6098 * no matter what the value.
6099 * dy must be 0 or FALSE returned.
6100 * LVS_REPORT dx=1 = 1 pixel
6101 * dy= see above
6104 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6106 switch(LISTVIEW_GetType(infoPtr)) {
6107 case LVS_REPORT:
6108 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6109 dy /= infoPtr->nItemHeight;
6110 break;
6111 case LVS_LIST:
6112 if (dy != 0) return FALSE;
6113 break;
6114 default: /* icon */
6115 dx = 0;
6116 break;
6119 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6120 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6122 return TRUE;
6125 /***
6126 * DESCRIPTION:
6127 * Sets the background color.
6129 * PARAMETER(S):
6130 * [I] infoPtr : valid pointer to the listview structure
6131 * [I] COLORREF : background color
6133 * RETURN:
6134 * SUCCESS : TRUE
6135 * FAILURE : FALSE
6137 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6139 TRACE("(clrBk=%lx)\n", clrBk);
6141 if(infoPtr->clrBk != clrBk) {
6142 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6143 infoPtr->clrBk = clrBk;
6144 if (clrBk == CLR_NONE)
6145 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6146 else
6147 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6148 LISTVIEW_InvalidateList(infoPtr);
6151 return TRUE;
6154 /* LISTVIEW_SetBkImage */
6156 /***
6157 * DESCRIPTION:
6158 * Sets the attributes of a header item.
6160 * PARAMETER(S):
6161 * [I] infoPtr : valid pointer to the listview structure
6162 * [I] INT : column index
6163 * [I] LPLVCOLUMNW : column attributes
6164 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6165 * otherwise it is in fact a LPLVCOLUMNA
6167 * RETURN:
6168 * SUCCESS : TRUE
6169 * FAILURE : FALSE
6171 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6172 LPLVCOLUMNW lpColumn, BOOL isW)
6174 BOOL bResult = FALSE;
6175 HDITEMW hdi, hdiget;
6177 if ((lpColumn != NULL) && (nColumn >= 0) &&
6178 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6180 /* initialize memory */
6181 ZeroMemory(&hdi, sizeof(hdi));
6183 if (lpColumn->mask & LVCF_FMT)
6185 /* format member is valid */
6186 hdi.mask |= HDI_FORMAT;
6188 /* get current format first */
6189 hdiget.mask = HDI_FORMAT;
6190 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6191 /* preserve HDF_STRING if present */
6192 hdi.fmt = hdiget.fmt & HDF_STRING;
6194 /* set text alignment (leftmost column must be left-aligned) */
6195 if (nColumn == 0)
6197 hdi.fmt |= HDF_LEFT;
6199 else
6201 if (lpColumn->fmt & LVCFMT_LEFT)
6202 hdi.fmt |= HDF_LEFT;
6203 else if (lpColumn->fmt & LVCFMT_RIGHT)
6204 hdi.fmt |= HDF_RIGHT;
6205 else if (lpColumn->fmt & LVCFMT_CENTER)
6206 hdi.fmt |= HDF_CENTER;
6209 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6210 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6212 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6213 hdi.fmt |= HDF_IMAGE;
6215 if (lpColumn->fmt & LVCFMT_IMAGE)
6217 hdi.fmt |= HDF_IMAGE;
6218 hdi.iImage = I_IMAGECALLBACK;
6222 if (lpColumn->mask & LVCF_WIDTH)
6224 hdi.mask |= HDI_WIDTH;
6225 hdi.cxy = lpColumn->cx;
6228 if (lpColumn->mask & LVCF_TEXT)
6230 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6231 hdi.pszText = lpColumn->pszText;
6232 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6233 hdi.fmt |= HDF_STRING;
6236 if (lpColumn->mask & LVCF_IMAGE)
6238 hdi.mask |= HDI_IMAGE;
6239 hdi.iImage = lpColumn->iImage;
6242 if (lpColumn->mask & LVCF_ORDER)
6244 hdi.mask |= HDI_ORDER;
6245 hdi.iOrder = lpColumn->iOrder;
6248 /* set header item attributes */
6249 if (isW)
6250 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6251 else
6252 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6255 return bResult;
6258 /***
6259 * DESCRIPTION:
6260 * Sets the column order array
6262 * PARAMETERS:
6263 * [I] infoPtr : valid pointer to the listview structure
6264 * [I] INT : number of elements in column order array
6265 * [I] INT : pointer to column order array
6267 * RETURN:
6268 * SUCCESS : TRUE
6269 * FAILURE : FALSE
6271 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6273 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6275 if (!lpiArray)
6276 return FALSE;
6278 return TRUE;
6282 /***
6283 * DESCRIPTION:
6284 * Sets the width of a column
6286 * PARAMETERS:
6287 * [I] infoPtr : valid pointer to the listview structure
6288 * [I] INT : column index
6289 * [I] INT : column width
6291 * RETURN:
6292 * SUCCESS : TRUE
6293 * FAILURE : FALSE
6295 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6297 HDITEMW hdi;
6298 LRESULT lret;
6299 LONG lStyle = infoPtr->dwStyle;
6300 UINT uView = lStyle & LVS_TYPEMASK;
6301 HDC hdc;
6302 HFONT header_font;
6303 HFONT old_font;
6304 SIZE size;
6305 WCHAR text_buffer[DISP_TEXT_SIZE];
6306 INT header_item_count;
6307 INT item_index;
6308 INT nLabelWidth;
6309 RECT rcHeader;
6310 LVITEMW lvItem;
6311 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6313 if (!infoPtr->hwndHeader) /* make sure we have a header */
6314 return (FALSE);
6316 /* set column width only if in report or list mode */
6317 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6318 return (FALSE);
6320 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6322 /* take care of invalid cx values */
6323 if((uView == LVS_REPORT) && (cx < -2))
6324 cx = LVSCW_AUTOSIZE;
6325 else if (uView == LVS_LIST && (cx < 1))
6326 return FALSE;
6328 /* resize all columns if in LVS_LIST mode */
6329 if(uView == LVS_LIST) {
6330 infoPtr->nItemWidth = cx;
6331 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6332 return TRUE;
6335 /* autosize based on listview items width */
6336 if(cx == LVSCW_AUTOSIZE)
6338 /* set the width of the column to the width of the widest item */
6339 if (iCol == 0 || uView == LVS_LIST)
6341 cx = 0;
6342 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6344 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6345 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6347 if (infoPtr->himlSmall)
6348 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6350 else
6352 lvItem.iSubItem = iCol;
6353 lvItem.mask = LVIF_TEXT;
6354 lvItem.pszText = szDispText;
6355 lvItem.cchTextMax = DISP_TEXT_SIZE;
6356 cx = 0;
6357 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6359 lvItem.iItem = item_index;
6360 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6361 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6362 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6365 cx += TRAILING_PADDING;
6366 } /* autosize based on listview header width */
6367 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6369 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6371 /* if iCol is the last column make it fill the remainder of the controls width */
6372 if(iCol == (header_item_count - 1)) {
6373 /* get the width of every item except the current one */
6374 hdi.mask = HDI_WIDTH;
6375 cx = 0;
6377 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6378 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6379 cx+=hdi.cxy;
6382 /* retrieve the layout of the header */
6383 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6385 cx = (rcHeader.right - rcHeader.left) - cx;
6387 else
6389 /* Despite what the MS docs say, if this is not the last
6390 column, then MS resizes the column to the width of the
6391 largest text string in the column, including headers
6392 and items. This is different from LVSCW_AUTOSIZE in that
6393 LVSCW_AUTOSIZE ignores the header string length.
6396 /* retrieve header font */
6397 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6399 /* retrieve header text */
6400 hdi.mask = HDI_TEXT;
6401 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6402 hdi.pszText = text_buffer;
6404 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6406 /* determine the width of the text in the header */
6407 hdc = GetDC(infoPtr->hwndSelf);
6408 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6410 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6412 SelectObject(hdc, old_font); /* restore the old font */
6413 ReleaseDC(infoPtr->hwndSelf, hdc);
6415 lvItem.iSubItem = iCol;
6416 lvItem.mask = LVIF_TEXT;
6417 lvItem.pszText = szDispText;
6418 lvItem.cchTextMax = DISP_TEXT_SIZE;
6419 cx = size.cx;
6420 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6422 lvItem.iItem = item_index;
6423 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6424 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6425 nLabelWidth += TRAILING_PADDING;
6426 /* While it is possible for subitems to have icons, even MS messes
6427 up the positioning, so I suspect no applications actually use
6428 them. */
6429 if (item_index == 0 && infoPtr->himlSmall)
6430 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6431 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6436 /* call header to update the column change */
6437 hdi.mask = HDI_WIDTH;
6439 hdi.cxy = cx;
6440 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6442 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6444 return lret;
6447 /***
6448 * DESCRIPTION:
6449 * Sets the extended listview style.
6451 * PARAMETERS:
6452 * [I] infoPtr : valid pointer to the listview structure
6453 * [I] DWORD : mask
6454 * [I] DWORD : style
6456 * RETURN:
6457 * SUCCESS : previous style
6458 * FAILURE : 0
6460 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6462 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6464 /* set new style */
6465 if (dwMask)
6466 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6467 else
6468 infoPtr->dwLvExStyle = dwStyle;
6470 return dwOldStyle;
6473 /***
6474 * DESCRIPTION:
6475 * Sets the new hot cursor used during hot tracking and hover selection.
6477 * PARAMETER(S):
6478 * [I] infoPtr : valid pointer to the listview structure
6479 * [I} hCurosr : the new hot cursor handle
6481 * RETURN:
6482 * Returns the previous hot cursor
6484 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6486 HCURSOR oldCursor = infoPtr->hHotCursor;
6487 infoPtr->hHotCursor = hCursor;
6488 return oldCursor;
6492 /***
6493 * DESCRIPTION:
6494 * Sets the hot item index.
6496 * PARAMETERS:
6497 * [I] infoPtr : valid pointer to the listview structure
6498 * [I] INT : index
6500 * RETURN:
6501 * SUCCESS : previous hot item index
6502 * FAILURE : -1 (no hot item)
6504 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6506 INT iOldIndex = infoPtr->nHotItem;
6507 infoPtr->nHotItem = iIndex;
6508 return iOldIndex;
6512 /***
6513 * DESCRIPTION:
6514 * Sets the amount of time the cursor must hover over an item before it is selected.
6516 * PARAMETER(S):
6517 * [I] infoPtr : valid pointer to the listview structure
6518 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6520 * RETURN:
6521 * Returns the previous hover time
6523 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6525 DWORD oldHoverTime = infoPtr->dwHoverTime;
6526 infoPtr->dwHoverTime = dwHoverTime;
6527 return oldHoverTime;
6530 /***
6531 * DESCRIPTION:
6532 * Sets spacing for icons of LVS_ICON style.
6534 * PARAMETER(S):
6535 * [I] infoPtr : valid pointer to the listview structure
6536 * [I] DWORD : MAKELONG(cx, cy)
6538 * RETURN:
6539 * MAKELONG(oldcx, oldcy)
6541 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6543 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6544 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6545 LONG lStyle = infoPtr->dwStyle;
6546 UINT uView = lStyle & LVS_TYPEMASK;
6548 TRACE("requested=(%d,%d)\n", cx, cy);
6550 /* this is supported only for LVS_ICON style */
6551 if (uView != LVS_ICON) return oldspacing;
6553 /* set to defaults, if instructed to */
6554 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6555 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6557 /* if 0 then compute width
6558 * FIXME: Should scan each item and determine max width of
6559 * icon or label, then make that the width */
6560 if (cx == 0)
6561 cx = infoPtr->iconSpacing.cx;
6563 /* if 0 then compute height */
6564 if (cy == 0)
6565 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6566 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6569 infoPtr->iconSpacing.cx = cx;
6570 infoPtr->iconSpacing.cy = cy;
6572 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6573 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6574 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6575 infoPtr->ntmHeight);
6577 /* these depend on the iconSpacing */
6578 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6579 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6581 return oldspacing;
6584 inline void update_icon_size(HIMAGELIST himl, SIZE *size)
6586 INT cx, cy;
6588 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6590 size->cx = cx;
6591 size->cy = cy;
6593 else
6594 size->cx = size->cy = 0;
6597 /***
6598 * DESCRIPTION:
6599 * Sets image lists.
6601 * PARAMETER(S):
6602 * [I] infoPtr : valid pointer to the listview structure
6603 * [I] INT : image list type
6604 * [I] HIMAGELIST : image list handle
6606 * RETURN:
6607 * SUCCESS : old image list
6608 * FAILURE : NULL
6610 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6612 UINT uView = LISTVIEW_GetType(infoPtr);
6613 INT oldHeight = infoPtr->nItemHeight;
6614 HIMAGELIST himlOld = 0;
6616 switch (nType)
6618 case LVSIL_NORMAL:
6619 himlOld = infoPtr->himlNormal;
6620 infoPtr->himlNormal = himl;
6621 if (uView == LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6622 LISTVIEW_SetIconSpacing(infoPtr, 0);
6623 break;
6625 case LVSIL_SMALL:
6626 himlOld = infoPtr->himlSmall;
6627 infoPtr->himlSmall = himl;
6628 if (uView != LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6629 break;
6631 case LVSIL_STATE:
6632 himlOld = infoPtr->himlState;
6633 infoPtr->himlState = himl;
6634 update_icon_size(himl, &infoPtr->iconStateSize);
6635 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6636 break;
6638 default:
6639 ERR("Unknown icon type=%d\n", nType);
6640 return NULL;
6643 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6644 if (infoPtr->nItemHeight != oldHeight)
6645 LISTVIEW_UpdateScroll(infoPtr);
6647 return himlOld;
6650 /***
6651 * DESCRIPTION:
6652 * Preallocates memory (does *not* set the actual count of items !)
6654 * PARAMETER(S):
6655 * [I] infoPtr : valid pointer to the listview structure
6656 * [I] INT : item count (projected number of items to allocate)
6657 * [I] DWORD : update flags
6659 * RETURN:
6660 * SUCCESS : TRUE
6661 * FAILURE : FALSE
6663 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6665 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6667 if (infoPtr->dwStyle & LVS_OWNERDATA)
6669 int precount,topvisible;
6671 TRACE("LVS_OWNERDATA is set!\n");
6672 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6673 FIXME("flags %s %s not implemented\n",
6674 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6675 : "",
6676 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6678 LISTVIEW_RemoveAllSelections(infoPtr);
6680 precount = infoPtr->nItemCount;
6681 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6682 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6684 infoPtr->nItemCount = nItems;
6685 infoPtr->nItemWidth = max(LISTVIEW_CalculateMaxWidth(infoPtr),
6686 DEFAULT_COLUMN_WIDTH);
6688 LISTVIEW_UpdateSize(infoPtr);
6689 LISTVIEW_UpdateScroll(infoPtr);
6691 if (min(precount,infoPtr->nItemCount) < topvisible)
6692 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6694 else
6696 /* According to MSDN for non-LVS_OWNERDATA this is just
6697 * a performance issue. The control allocates its internal
6698 * data structures for the number of items specified. It
6699 * cuts down on the number of memory allocations. Therefore
6700 * we will just issue a WARN here
6702 WARN("for non-ownerdata performance option not implemented.\n");
6705 return TRUE;
6708 /***
6709 * DESCRIPTION:
6710 * Sets the position of an item.
6712 * PARAMETER(S):
6713 * [I] infoPtr : valid pointer to the listview structure
6714 * [I] nItem : item index
6715 * [I] pt : coordinate
6717 * RETURN:
6718 * SUCCESS : TRUE
6719 * FAILURE : FALSE
6721 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6723 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6724 POINT old;
6726 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6728 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6729 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6731 /* This point value seems to be an undocumented feature.
6732 * The best guess is that it means either at the origin,
6733 * or at true beginning of the list. I will assume the origin. */
6734 if ((pt.x == -1) && (pt.y == -1))
6735 LISTVIEW_GetOrigin(infoPtr, &pt);
6736 else if (uView == LVS_ICON)
6738 pt.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
6739 pt.y -= ICON_TOP_PADDING;
6742 /* save the old position */
6743 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
6744 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
6746 /* Is the position changing? */
6747 if (pt.x == old.x && pt.y == old.y) return TRUE;
6749 /* FIXME: shouldn't we invalidate, as the item moved? */
6751 /* Allocating a POINTER for every item is too resource intensive,
6752 * so we'll keep the (x,y) in different arrays */
6753 if (DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)pt.x) &&
6754 DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)pt.y) )
6755 return TRUE;
6757 ERR("We should never fail here (nItem=%d, pt=%s), please report.\n",
6758 nItem, debugpoint(&pt));
6759 return FALSE;
6762 /***
6763 * DESCRIPTION:
6764 * Sets the state of one or many items.
6766 * PARAMETER(S):
6767 * [I] infoPtr : valid pointer to the listview structure
6768 * [I]INT : item index
6769 * [I] LPLVITEM : item or subitem info
6771 * RETURN:
6772 * SUCCESS : TRUE
6773 * FAILURE : FALSE
6775 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6777 BOOL bResult = TRUE;
6778 LVITEMW lvItem;
6780 lvItem.iItem = nItem;
6781 lvItem.iSubItem = 0;
6782 lvItem.mask = LVIF_STATE;
6783 lvItem.state = lpLVItem->state;
6784 lvItem.stateMask = lpLVItem->stateMask;
6785 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6787 if (nItem == -1)
6789 /* apply to all items */
6790 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6791 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6793 else
6794 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6796 return bResult;
6799 /***
6800 * DESCRIPTION:
6801 * Sets the text of an item or subitem.
6803 * PARAMETER(S):
6804 * [I] hwnd : window handle
6805 * [I] nItem : item index
6806 * [I] lpLVItem : item or subitem info
6807 * [I] isW : TRUE if input is Unicode
6809 * RETURN:
6810 * SUCCESS : TRUE
6811 * FAILURE : FALSE
6813 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6815 LVITEMW lvItem;
6817 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6819 lvItem.iItem = nItem;
6820 lvItem.iSubItem = lpLVItem->iSubItem;
6821 lvItem.mask = LVIF_TEXT;
6822 lvItem.pszText = lpLVItem->pszText;
6823 lvItem.cchTextMax = lpLVItem->cchTextMax;
6825 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6827 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6830 /***
6831 * DESCRIPTION:
6832 * Set item index that marks the start of a multiple selection.
6834 * PARAMETER(S):
6835 * [I] infoPtr : valid pointer to the listview structure
6836 * [I] INT : index
6838 * RETURN:
6839 * Index number or -1 if there is no selection mark.
6841 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6843 INT nOldIndex = infoPtr->nSelectionMark;
6845 TRACE("(nIndex=%d)\n", nIndex);
6847 infoPtr->nSelectionMark = nIndex;
6849 return nOldIndex;
6852 /***
6853 * DESCRIPTION:
6854 * Sets the text background color.
6856 * PARAMETER(S):
6857 * [I] infoPtr : valid pointer to the listview structure
6858 * [I] COLORREF : text background color
6860 * RETURN:
6861 * SUCCESS : TRUE
6862 * FAILURE : FALSE
6864 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6866 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6868 if (infoPtr->clrTextBk != clrTextBk)
6870 infoPtr->clrTextBk = clrTextBk;
6871 LISTVIEW_InvalidateList(infoPtr);
6874 return TRUE;
6877 /***
6878 * DESCRIPTION:
6879 * Sets the text foreground color.
6881 * PARAMETER(S):
6882 * [I] infoPtr : valid pointer to the listview structure
6883 * [I] COLORREF : text color
6885 * RETURN:
6886 * SUCCESS : TRUE
6887 * FAILURE : FALSE
6889 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6891 TRACE("(clrText=%lx)\n", clrText);
6893 if (infoPtr->clrText != clrText)
6895 infoPtr->clrText = clrText;
6896 LISTVIEW_InvalidateList(infoPtr);
6899 return TRUE;
6902 /* LISTVIEW_SetToolTips */
6903 /* LISTVIEW_SetUnicodeFormat */
6904 /* LISTVIEW_SetWorkAreas */
6906 /***
6907 * DESCRIPTION:
6908 * Callback internally used by LISTVIEW_SortItems()
6910 * PARAMETER(S):
6911 * [I] LPVOID : first LISTVIEW_ITEM to compare
6912 * [I] LPVOID : second LISTVIEW_ITEM to compare
6913 * [I] LPARAM : HWND of control
6915 * RETURN:
6916 * if first comes before second : negative
6917 * if first comes after second : positive
6918 * if first and second are equivalent : zero
6920 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6922 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6923 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6924 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6926 /* Forward the call to the client defined callback */
6927 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6930 /***
6931 * DESCRIPTION:
6932 * Sorts the listview items.
6934 * PARAMETER(S):
6935 * [I] infoPtr : valid pointer to the listview structure
6936 * [I] WPARAM : application-defined value
6937 * [I] LPARAM : pointer to comparision callback
6939 * RETURN:
6940 * SUCCESS : TRUE
6941 * FAILURE : FALSE
6943 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6945 UINT lStyle = infoPtr->dwStyle;
6946 HDPA hdpaSubItems;
6947 LISTVIEW_ITEM *lpItem;
6948 LPVOID selectionMarkItem;
6949 int i;
6951 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6953 if (lStyle & LVS_OWNERDATA) return FALSE;
6955 if (!infoPtr->hdpaItems) return FALSE;
6957 /* if there are 0 or 1 items, there is no need to sort */
6958 if (infoPtr->nItemCount < 2) return TRUE;
6960 if (infoPtr->nFocusedItem >= 0)
6962 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6963 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6964 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6967 infoPtr->pfnCompare = pfnCompare;
6968 infoPtr->lParamSort = lParamSort;
6969 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6971 /* Adjust selections and indices so that they are the way they should
6972 * be after the sort (otherwise, the list items move around, but
6973 * whatever is at the item's previous original position will be
6974 * selected instead)
6976 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6977 for (i=0; i < infoPtr->nItemCount; i++)
6979 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6980 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6982 if (lpItem->state & LVIS_SELECTED)
6983 LISTVIEW_AddSelectionRange(infoPtr, i, i);
6984 else
6985 LISTVIEW_RemoveSelectionRange(infoPtr, i, i);
6986 if (lpItem->state & LVIS_FOCUSED)
6988 infoPtr->nFocusedItem = i;
6989 lpItem->state &= ~LVIS_FOCUSED;
6992 if (selectionMarkItem != NULL)
6993 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6994 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6996 /* align the items */
6997 LISTVIEW_AlignTop(infoPtr);
6999 /* refresh the display */
7000 LISTVIEW_InvalidateList(infoPtr); /* FIXME: display should not change for [SMALL]ICON view */
7002 return TRUE;
7005 /***
7006 * DESCRIPTION:
7007 * Updates an items or rearranges the listview control.
7009 * PARAMETER(S):
7010 * [I] infoPtr : valid pointer to the listview structure
7011 * [I] INT : item index
7013 * RETURN:
7014 * SUCCESS : TRUE
7015 * FAILURE : FALSE
7017 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7019 LONG lStyle = infoPtr->dwStyle;
7020 UINT uView = lStyle & LVS_TYPEMASK;
7022 TRACE("(nItem=%d)\n", nItem);
7024 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7026 /* rearrange with default alignment style */
7027 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
7028 LISTVIEW_Arrange(infoPtr, 0);
7029 else
7030 LISTVIEW_InvalidateItem(infoPtr, nItem);
7032 return TRUE;
7036 /***
7037 * DESCRIPTION:
7038 * Creates the listview control.
7040 * PARAMETER(S):
7041 * [I] hwnd : window handle
7042 * [I] lpcs : the create parameters
7044 * RETURN:
7045 * Success: 0
7046 * Failure: -1
7048 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7050 LISTVIEW_INFO *infoPtr;
7051 UINT uView = lpcs->style & LVS_TYPEMASK;
7052 LOGFONTW logFont;
7054 TRACE("(lpcs=%p)\n", lpcs);
7056 /* initialize info pointer */
7057 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7058 if (!infoPtr) return -1;
7060 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7062 infoPtr->hwndSelf = hwnd;
7063 infoPtr->dwStyle = lpcs->style;
7064 /* determine the type of structures to use */
7065 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7066 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7068 /* initialize color information */
7069 infoPtr->clrBk = CLR_NONE;
7070 infoPtr->clrText = comctl32_color.clrWindowText;
7071 infoPtr->clrTextBk = CLR_DEFAULT;
7072 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7074 /* set default values */
7075 infoPtr->nFocusedItem = -1;
7076 infoPtr->nSelectionMark = -1;
7077 infoPtr->nHotItem = -1;
7078 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7079 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7080 infoPtr->nEditLabelItem = -1;
7082 /* get default font (icon title) */
7083 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7084 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7085 infoPtr->hFont = infoPtr->hDefaultFont;
7086 LISTVIEW_SaveTextMetrics(infoPtr);
7088 /* create header */
7089 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7090 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7091 0, 0, 0, 0, hwnd, (HMENU)0,
7092 lpcs->hInstance, NULL);
7094 /* set header unicode format */
7095 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
7097 /* set header font */
7098 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7099 (LPARAM)TRUE);
7101 if (uView == LVS_ICON)
7103 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7104 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7106 else if (uView == LVS_REPORT)
7108 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7110 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7112 else
7114 /* set HDS_HIDDEN flag to hide the header bar */
7115 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7116 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7120 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7121 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7123 else
7125 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7126 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7129 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
7130 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
7132 /* display unsupported listview window styles */
7133 LISTVIEW_UnsupportedStyles(lpcs->style);
7135 /* allocate memory for the data structure */
7136 infoPtr->hdpaItems = DPA_Create(10);
7137 infoPtr->hdpaPosX = DPA_Create(10);
7138 infoPtr->hdpaPosY = DPA_Create(10);
7140 /* allocate memory for the selection ranges */
7141 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7143 /* initialize size of items */
7144 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7145 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
7147 /* initialize the hover time to -1(indicating the default system hover time) */
7148 infoPtr->dwHoverTime = -1;
7150 return 0;
7153 /***
7154 * DESCRIPTION:
7155 * Erases the background of the listview control.
7157 * PARAMETER(S):
7158 * [I] infoPtr : valid pointer to the listview structure
7159 * [I] hdc : device context handle
7161 * RETURN:
7162 * SUCCESS : TRUE
7163 * FAILURE : FALSE
7165 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7167 RECT rc;
7169 TRACE("(hdc=%x)\n", hdc);
7171 if (!GetClipBox(hdc, &rc)) return FALSE;
7173 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7177 /***
7178 * DESCRIPTION:
7179 * Helper function for LISTVIEW_[HV]Scroll *only*.
7180 * Performs vertical/horizontal scrolling by a give amount.
7182 * PARAMETER(S):
7183 * [I] infoPtr : valid pointer to the listview structure
7184 * [I] dx : amount of horizontal scroll
7185 * [I] dy : amount of vertical scroll
7187 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7189 /* now we can scroll the list */
7190 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7191 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7192 /* if we have focus, adjust rect */
7193 OffsetRect(&infoPtr->rcFocus, dx, dy);
7194 UpdateWindow(infoPtr->hwndSelf);
7197 /***
7198 * DESCRIPTION:
7199 * Performs vertical scrolling.
7201 * PARAMETER(S):
7202 * [I] infoPtr : valid pointer to the listview structure
7203 * [I] nScrollCode : scroll code
7204 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7205 * [I] hScrollWnd : scrollbar control window handle
7207 * RETURN:
7208 * Zero
7210 * NOTES:
7211 * SB_LINEUP/SB_LINEDOWN:
7212 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7213 * for LVS_REPORT is 1 line
7214 * for LVS_LIST cannot occur
7217 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7218 INT nScrollDiff, HWND hScrollWnd)
7220 UINT uView = LISTVIEW_GetType(infoPtr);
7221 INT nOldScrollPos, nNewScrollPos;
7222 SCROLLINFO scrollInfo;
7223 BOOL is_an_icon;
7225 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7227 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7229 scrollInfo.cbSize = sizeof(SCROLLINFO);
7230 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7232 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7234 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7236 nOldScrollPos = scrollInfo.nPos;
7237 switch (nScrollCode)
7239 case SB_INTERNAL:
7240 break;
7242 case SB_LINEUP:
7243 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7244 break;
7246 case SB_LINEDOWN:
7247 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7248 break;
7250 case SB_PAGEUP:
7251 nScrollDiff = -scrollInfo.nPage;
7252 break;
7254 case SB_PAGEDOWN:
7255 nScrollDiff = scrollInfo.nPage;
7256 break;
7258 case SB_THUMBPOSITION:
7259 case SB_THUMBTRACK:
7260 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7261 break;
7263 default:
7264 nScrollDiff = 0;
7267 /* quit right away if pos isn't changing */
7268 if (nScrollDiff == 0) return 0;
7270 /* calculate new position, and handle overflows */
7271 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7272 if (nScrollDiff > 0) {
7273 if (nNewScrollPos < nOldScrollPos ||
7274 nNewScrollPos > scrollInfo.nMax)
7275 nNewScrollPos = scrollInfo.nMax;
7276 } else {
7277 if (nNewScrollPos > nOldScrollPos ||
7278 nNewScrollPos < scrollInfo.nMin)
7279 nNewScrollPos = scrollInfo.nMin;
7282 /* set the new position, and reread in case it changed */
7283 scrollInfo.fMask = SIF_POS;
7284 scrollInfo.nPos = nNewScrollPos;
7285 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7287 /* carry on only if it really changed */
7288 if (nNewScrollPos == nOldScrollPos) return 0;
7290 /* now adjust to client coordinates */
7291 nScrollDiff = nOldScrollPos - nNewScrollPos;
7292 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7294 /* and scroll the window */
7295 scroll_list(infoPtr, 0, nScrollDiff);
7297 return 0;
7300 /***
7301 * DESCRIPTION:
7302 * Performs horizontal scrolling.
7304 * PARAMETER(S):
7305 * [I] infoPtr : valid pointer to the listview structure
7306 * [I] nScrollCode : scroll code
7307 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7308 * [I] hScrollWnd : scrollbar control window handle
7310 * RETURN:
7311 * Zero
7313 * NOTES:
7314 * SB_LINELEFT/SB_LINERIGHT:
7315 * for LVS_ICON, LVS_SMALLICON 1 pixel
7316 * for LVS_REPORT is 1 pixel
7317 * for LVS_LIST is 1 column --> which is a 1 because the
7318 * scroll is based on columns not pixels
7321 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7322 INT nScrollDiff, HWND hScrollWnd)
7324 UINT uView = LISTVIEW_GetType(infoPtr);
7325 INT nOldScrollPos, nNewScrollPos;
7326 SCROLLINFO scrollInfo;
7328 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7330 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7332 scrollInfo.cbSize = sizeof(SCROLLINFO);
7333 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7335 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7337 nOldScrollPos = scrollInfo.nPos;
7339 switch (nScrollCode)
7341 case SB_INTERNAL:
7342 break;
7344 case SB_LINELEFT:
7345 nScrollDiff = -1;
7346 break;
7348 case SB_LINERIGHT:
7349 nScrollDiff = 1;
7350 break;
7352 case SB_PAGELEFT:
7353 nScrollDiff = -scrollInfo.nPage;
7354 break;
7356 case SB_PAGERIGHT:
7357 nScrollDiff = scrollInfo.nPage;
7358 break;
7360 case SB_THUMBPOSITION:
7361 case SB_THUMBTRACK:
7362 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7363 break;
7365 default:
7366 nScrollDiff = 0;
7369 /* quit right away if pos isn't changing */
7370 if (nScrollDiff == 0) return 0;
7372 /* calculate new position, and handle overflows */
7373 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7374 if (nScrollDiff > 0) {
7375 if (nNewScrollPos < nOldScrollPos ||
7376 nNewScrollPos > scrollInfo.nMax)
7377 nNewScrollPos = scrollInfo.nMax;
7378 } else {
7379 if (nNewScrollPos > nOldScrollPos ||
7380 nNewScrollPos < scrollInfo.nMin)
7381 nNewScrollPos = scrollInfo.nMin;
7384 /* set the new position, and reread in case it changed */
7385 scrollInfo.fMask = SIF_POS;
7386 scrollInfo.nPos = nNewScrollPos;
7387 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7389 /* carry on only if it really changed */
7390 if (nNewScrollPos == nOldScrollPos) return 0;
7392 if(uView == LVS_REPORT)
7393 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7395 /* now adjust to client coordinates */
7396 nScrollDiff = nOldScrollPos - nNewScrollPos;
7397 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7399 /* and scroll the window */
7400 scroll_list(infoPtr, nScrollDiff, 0);
7402 return 0;
7405 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7407 UINT uView = LISTVIEW_GetType(infoPtr);
7408 INT gcWheelDelta = 0;
7409 UINT pulScrollLines = 3;
7410 SCROLLINFO scrollInfo;
7412 TRACE("(wheelDelta=%d)\n", wheelDelta);
7414 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7415 gcWheelDelta -= wheelDelta;
7417 scrollInfo.cbSize = sizeof(SCROLLINFO);
7418 scrollInfo.fMask = SIF_POS;
7420 switch(uView)
7422 case LVS_ICON:
7423 case LVS_SMALLICON:
7425 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7426 * should be fixed in the future.
7428 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7429 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7430 scrollInfo.nPos + (gcWheelDelta < 0) ?
7431 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7432 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7433 break;
7435 case LVS_REPORT:
7436 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7438 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7440 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7441 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7442 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7445 break;
7447 case LVS_LIST:
7448 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7449 break;
7451 return 0;
7454 /***
7455 * DESCRIPTION:
7456 * ???
7458 * PARAMETER(S):
7459 * [I] infoPtr : valid pointer to the listview structure
7460 * [I] INT : virtual key
7461 * [I] LONG : key data
7463 * RETURN:
7464 * Zero
7466 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7468 UINT uView = LISTVIEW_GetType(infoPtr);
7469 INT nItem = -1;
7470 NMLVKEYDOWN nmKeyDown;
7472 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7474 /* send LVN_KEYDOWN notification */
7475 nmKeyDown.wVKey = nVirtualKey;
7476 nmKeyDown.flags = 0;
7477 notify(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7479 switch (nVirtualKey)
7481 case VK_RETURN:
7482 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7484 notify_return(infoPtr);
7485 notify_itemactivate(infoPtr);
7487 break;
7489 case VK_HOME:
7490 if (infoPtr->nItemCount > 0)
7491 nItem = 0;
7492 break;
7494 case VK_END:
7495 if (infoPtr->nItemCount > 0)
7496 nItem = infoPtr->nItemCount - 1;
7497 break;
7499 case VK_LEFT:
7500 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7501 break;
7503 case VK_UP:
7504 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7505 break;
7507 case VK_RIGHT:
7508 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7509 break;
7511 case VK_DOWN:
7512 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7513 break;
7515 case VK_PRIOR:
7516 if (uView == LVS_REPORT)
7517 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7518 else
7519 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7520 * LISTVIEW_GetCountPerRow(infoPtr);
7521 if(nItem < 0) nItem = 0;
7522 break;
7524 case VK_NEXT:
7525 if (uView == LVS_REPORT)
7526 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7527 else
7528 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7529 * LISTVIEW_GetCountPerRow(infoPtr);
7530 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7531 break;
7534 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7535 LISTVIEW_KeySelection(infoPtr, nItem);
7537 return 0;
7540 /***
7541 * DESCRIPTION:
7542 * Kills the focus.
7544 * PARAMETER(S):
7545 * [I] infoPtr : valid pointer to the listview structure
7547 * RETURN:
7548 * Zero
7550 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7552 TRACE("()\n");
7554 /* if we did not have the focus, there's nothing to do */
7555 if (!infoPtr->bFocus) return 0;
7557 /* send NM_KILLFOCUS notification */
7558 notify_killfocus(infoPtr);
7560 /* if we have a focus rectagle, get rid of it */
7561 LISTVIEW_ShowFocusRect(infoPtr, infoPtr->nFocusedItem, FALSE);
7563 /* set window focus flag */
7564 infoPtr->bFocus = FALSE;
7566 /* invalidate the selected items before reseting focus flag */
7567 LISTVIEW_InvalidateSelectedItems(infoPtr);
7569 return 0;
7572 /***
7573 * DESCRIPTION:
7574 * Processes double click messages (left mouse button).
7576 * PARAMETER(S):
7577 * [I] infoPtr : valid pointer to the listview structure
7578 * [I] wKey : key flag
7579 * [I] pts : mouse coordinate
7581 * RETURN:
7582 * Zero
7584 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7586 LVHITTESTINFO htInfo;
7587 NMLISTVIEW nmlv;
7589 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7591 htInfo.pt.x = pts.x;
7592 htInfo.pt.y = pts.y;
7594 /* send NM_DBLCLK notification */
7595 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7596 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE);
7597 nmlv.iItem = htInfo.iItem;
7598 nmlv.iSubItem = htInfo.iSubItem;
7599 nmlv.ptAction = htInfo.pt;
7600 notify_listview(infoPtr, NM_DBLCLK, &nmlv);
7602 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7603 if(nmlv.iItem != -1)
7604 notify_itemactivate(infoPtr);
7606 return 0;
7609 /***
7610 * DESCRIPTION:
7611 * Processes mouse down messages (left mouse button).
7613 * PARAMETER(S):
7614 * [I] infoPtr : valid pointer to the listview structure
7615 * [I] wKey : key flag
7616 * [I] pts : mouse coordinate
7618 * RETURN:
7619 * Zero
7621 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7623 LONG lStyle = infoPtr->dwStyle;
7624 static BOOL bGroupSelect = TRUE;
7625 POINT pt = { pts.x, pts.y };
7626 INT nItem;
7628 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7630 /* FIXME: NM_CLICK */
7632 /* send NM_RELEASEDCAPTURE notification */
7633 notify_releasedcapture(infoPtr);
7635 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7637 /* set left button down flag */
7638 infoPtr->bLButtonDown = TRUE;
7640 nItem = LISTVIEW_GetItemAtPt(infoPtr, pt);
7641 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7642 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7644 if (lStyle & LVS_SINGLESEL)
7646 if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7647 && infoPtr->nEditLabelItem == -1)
7648 infoPtr->nEditLabelItem = nItem;
7649 else
7650 LISTVIEW_SetSelection(infoPtr, nItem);
7652 else
7654 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7656 if (bGroupSelect)
7657 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7658 else
7660 LVITEMW item;
7662 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7663 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7665 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7666 infoPtr->nSelectionMark = nItem;
7669 else if (wKey & MK_CONTROL)
7671 LVITEMW item;
7673 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7675 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7676 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7677 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7678 infoPtr->nSelectionMark = nItem;
7680 else if (wKey & MK_SHIFT)
7682 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7684 else
7686 BOOL was_selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
7688 /* set selection (clears other pre-existing selections) */
7689 LISTVIEW_SetSelection(infoPtr, nItem);
7691 if (was_selected && infoPtr->nEditLabelItem == -1)
7692 infoPtr->nEditLabelItem = nItem;
7696 else
7698 /* remove all selections */
7699 LISTVIEW_RemoveAllSelections(infoPtr);
7702 return 0;
7705 /***
7706 * DESCRIPTION:
7707 * Processes mouse up messages (left mouse button).
7709 * PARAMETER(S):
7710 * [I] infoPtr : valid pointer to the listview structure
7711 * [I] wKey : key flag
7712 * [I] pts : mouse coordinate
7714 * RETURN:
7715 * Zero
7717 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7719 LVHITTESTINFO lvHitTestInfo;
7720 NMLISTVIEW nmlv;
7722 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7724 if (!infoPtr->bLButtonDown) return 0;
7726 lvHitTestInfo.pt.x = pts.x;
7727 lvHitTestInfo.pt.y = pts.y;
7729 /* send NM_CLICK notification */
7730 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7731 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE);
7732 nmlv.iItem = lvHitTestInfo.iItem;
7733 nmlv.iSubItem = lvHitTestInfo.iSubItem;
7734 nmlv.ptAction = lvHitTestInfo.pt;
7735 notify_listview(infoPtr, NM_CLICK, &nmlv);
7737 /* set left button flag */
7738 infoPtr->bLButtonDown = FALSE;
7740 if(infoPtr->nEditLabelItem != -1)
7742 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem &&
7743 (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7744 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7745 infoPtr->nEditLabelItem = -1;
7748 return 0;
7751 /***
7752 * DESCRIPTION:
7753 * Destroys the listview control (called after WM_DESTROY).
7755 * PARAMETER(S):
7756 * [I] infoPtr : valid pointer to the listview structure
7758 * RETURN:
7759 * Zero
7761 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7763 LONG lStyle = infoPtr->dwStyle;
7765 TRACE("()\n");
7767 /* delete all items */
7768 LISTVIEW_DeleteAllItems(infoPtr);
7770 /* destroy data structure */
7771 DPA_Destroy(infoPtr->hdpaItems);
7772 DPA_Destroy(infoPtr->hdpaSelectionRanges);
7774 /* destroy image lists */
7775 if (!(lStyle & LVS_SHAREIMAGELISTS))
7777 /* FIXME: If the caller does a ImageList_Destroy and then we
7778 * do this code the area will be freed twice. Currently
7779 * this generates an "err:heap:HEAP_ValidateInUseArena
7780 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
7781 * has PREV_FREE flag" sometimes.
7783 * We will leak the memory till we figure out how to fix
7785 if (infoPtr->himlNormal)
7786 ImageList_Destroy(infoPtr->himlNormal);
7787 if (infoPtr->himlSmall)
7788 ImageList_Destroy(infoPtr->himlSmall);
7789 if (infoPtr->himlState)
7790 ImageList_Destroy(infoPtr->himlState);
7793 /* destroy font, bkgnd brush */
7794 infoPtr->hFont = 0;
7795 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7796 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7798 /* free listview info pointer*/
7799 COMCTL32_Free(infoPtr);
7801 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7802 return 0;
7805 /***
7806 * DESCRIPTION:
7807 * Handles notifications from children.
7809 * PARAMETER(S):
7810 * [I] infoPtr : valid pointer to the listview structure
7811 * [I] INT : control identifier
7812 * [I] LPNMHDR : notification information
7814 * RETURN:
7815 * Zero
7817 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7819 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7821 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7823 /* handle notification from header control */
7824 if (lpnmh->code == HDN_ENDTRACKW)
7826 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7827 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7829 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7831 /* Handle sorting by Header Column */
7832 NMLISTVIEW nmlv;
7834 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7835 nmlv.iItem = -1;
7836 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
7837 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7839 else if(lpnmh->code == NM_RELEASEDCAPTURE)
7841 /* Idealy this should be done in HDN_ENDTRACKA
7842 * but since SetItemBounds in Header.c is called after
7843 * the notification is sent, it is neccessary to handle the
7844 * update of the scroll bar here (Header.c works fine as it is,
7845 * no need to disturb it)
7847 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7848 LISTVIEW_UpdateScroll(infoPtr);
7849 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7854 return 0;
7857 /***
7858 * DESCRIPTION:
7859 * Determines the type of structure to use.
7861 * PARAMETER(S):
7862 * [I] infoPtr : valid pointer to the listview structureof the sender
7863 * [I] HWND : listview window handle
7864 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
7866 * RETURN:
7867 * Zero
7869 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7871 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7873 if (nCommand == NF_REQUERY)
7874 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7875 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7876 return 0;
7879 /***
7880 * DESCRIPTION:
7881 * Paints/Repaints the listview control.
7883 * PARAMETER(S):
7884 * [I] infoPtr : valid pointer to the listview structure
7885 * [I] HDC : device context handle
7887 * RETURN:
7888 * Zero
7890 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7892 TRACE("(hdc=%x)\n", hdc);
7894 if (hdc)
7895 LISTVIEW_Refresh(infoPtr, hdc);
7896 else
7898 PAINTSTRUCT ps;
7900 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7901 if (!hdc) return 1;
7902 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7903 LISTVIEW_Refresh(infoPtr, hdc);
7904 EndPaint(infoPtr->hwndSelf, &ps);
7907 return 0;
7910 /***
7911 * DESCRIPTION:
7912 * Processes double click messages (right mouse button).
7914 * PARAMETER(S):
7915 * [I] infoPtr : valid pointer to the listview structure
7916 * [I] wKey : key flag
7917 * [I] pts : mouse coordinate
7919 * RETURN:
7920 * Zero
7922 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7924 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7926 /* send NM_RELEASEDCAPTURE notification */
7927 notify_releasedcapture(infoPtr);
7929 /* send NM_RDBLCLK notification */
7930 notify_rdblclk(infoPtr);
7932 return 0;
7935 /***
7936 * DESCRIPTION:
7937 * Processes mouse down messages (right mouse button).
7939 * PARAMETER(S):
7940 * [I] infoPtr : valid pointer to the listview structure
7941 * [I] wKey : key flag
7942 * [I] pts : mouse coordinate
7944 * RETURN:
7945 * Zero
7947 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7949 LVHITTESTINFO lvHitTestInfo;
7950 NMLISTVIEW nmlv;
7951 INT nItem;
7953 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7955 /* FIXME: NM_CLICK */
7957 /* send NM_RELEASEDCAPTURE notification */
7958 notify_releasedcapture(infoPtr);
7960 /* make sure the listview control window has the focus */
7961 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7963 /* set right button down flag */
7964 infoPtr->bRButtonDown = TRUE;
7966 /* determine the index of the selected item */
7967 lvHitTestInfo.pt.x = pts.x;
7968 lvHitTestInfo.pt.y = pts.y;
7969 nItem = LISTVIEW_GetItemAtPt(infoPtr, lvHitTestInfo.pt);
7971 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7973 LISTVIEW_SetItemFocus(infoPtr,nItem);
7974 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7975 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7976 LISTVIEW_SetSelection(infoPtr, nItem);
7978 else
7980 LISTVIEW_RemoveAllSelections(infoPtr);
7984 /* Send NM_RClICK notification */
7985 ZeroMemory(&nmlv, sizeof(nmlv));
7986 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE);
7987 nmlv.iItem = lvHitTestInfo.iItem;
7988 nmlv.iSubItem = lvHitTestInfo.iSubItem;
7989 nmlv.ptAction = lvHitTestInfo.pt;
7990 notify_listview(infoPtr, NM_RCLICK, &nmlv);
7992 return 0;
7995 /***
7996 * DESCRIPTION:
7997 * Processes mouse up messages (right mouse button).
7999 * PARAMETER(S):
8000 * [I] infoPtr : valid pointer to the listview structure
8001 * [I] wKey : key flag
8002 * [I] pts : mouse coordinate
8004 * RETURN:
8005 * Zero
8007 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8009 POINT pt = { pts.x, pts.y };
8011 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8013 if (!infoPtr->bRButtonDown) return 0;
8015 /* set button flag */
8016 infoPtr->bRButtonDown = FALSE;
8018 /* Change to screen coordinate for WM_CONTEXTMENU */
8019 ClientToScreen(infoPtr->hwndSelf, &pt);
8021 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8022 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8023 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8025 return 0;
8029 /***
8030 * DESCRIPTION:
8031 * Sets the cursor.
8033 * PARAMETER(S):
8034 * [I] infoPtr : valid pointer to the listview structure
8035 * [I] hwnd : window handle of window containing the cursor
8036 * [I] nHittest : hit-test code
8037 * [I] wMouseMsg : ideintifier of the mouse message
8039 * RETURN:
8040 * TRUE if cursor is set
8041 * FALSE otherwise
8043 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8045 POINT pt;
8047 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8049 if(!infoPtr->hHotCursor) return FALSE;
8051 GetCursorPos(&pt);
8052 if (LISTVIEW_GetItemAtPt(infoPtr, pt) < 0) return FALSE;
8054 SetCursor(infoPtr->hHotCursor);
8056 return TRUE;
8059 /***
8060 * DESCRIPTION:
8061 * Sets the focus.
8063 * PARAMETER(S):
8064 * [I] infoPtr : valid pointer to the listview structure
8065 * [I] infoPtr : handle of previously focused window
8067 * RETURN:
8068 * Zero
8070 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8072 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
8074 /* if we have the focus already, there's nothing to do */
8075 if (infoPtr->bFocus) return 0;
8077 /* send NM_SETFOCUS notification */
8078 notify_setfocus(infoPtr);
8080 /* set window focus flag */
8081 infoPtr->bFocus = TRUE;
8083 /* put the focus rect back on */
8084 LISTVIEW_ShowFocusRect(infoPtr, infoPtr->nFocusedItem, TRUE);
8086 /* redraw all visible selected items */
8087 LISTVIEW_InvalidateSelectedItems(infoPtr);
8089 return 0;
8092 /***
8093 * DESCRIPTION:
8094 * Sets the font.
8096 * PARAMETER(S):
8097 * [I] infoPtr : valid pointer to the listview structure
8098 * [I] HFONT : font handle
8099 * [I] WORD : redraw flag
8101 * RETURN:
8102 * Zero
8104 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8106 HFONT oldFont = infoPtr->hFont;
8108 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
8110 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8111 if (infoPtr->hFont == oldFont) return 0;
8113 LISTVIEW_SaveTextMetrics(infoPtr);
8115 if (LISTVIEW_GetType(infoPtr) == LVS_REPORT)
8116 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8118 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8120 return 0;
8123 /***
8124 * DESCRIPTION:
8125 * Message handling for WM_SETREDRAW.
8126 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8128 * PARAMETER(S):
8129 * [I] infoPtr : valid pointer to the listview structure
8130 * [I] bRedraw: state of redraw flag
8132 * RETURN:
8133 * DefWinProc return value
8135 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8137 /* FIXME: this is bogus */
8138 LRESULT lResult = DefWindowProcW(infoPtr->hwndSelf, WM_SETREDRAW, bRedraw, 0);
8139 if(bRedraw)
8140 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
8141 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8142 return lResult;
8145 /***
8146 * DESCRIPTION:
8147 * Resizes the listview control. This function processes WM_SIZE
8148 * messages. At this time, the width and height are not used.
8150 * PARAMETER(S):
8151 * [I] infoPtr : valid pointer to the listview structure
8152 * [I] WORD : new width
8153 * [I] WORD : new height
8155 * RETURN:
8156 * Zero
8158 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8160 LONG lStyle = infoPtr->dwStyle;
8161 UINT uView = lStyle & LVS_TYPEMASK;
8163 TRACE("(width=%d, height=%d)\n", Width, Height);
8165 if (LISTVIEW_UpdateSize(infoPtr))
8167 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8169 if (lStyle & LVS_ALIGNLEFT)
8170 LISTVIEW_AlignLeft(infoPtr);
8171 else
8172 LISTVIEW_AlignTop(infoPtr);
8175 LISTVIEW_UpdateScroll(infoPtr);
8177 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8180 return 0;
8183 /***
8184 * DESCRIPTION:
8185 * Sets the size information.
8187 * PARAMETER(S):
8188 * [I] infoPtr : valid pointer to the listview structure
8190 * RETURN:
8191 * Zero if no size change
8192 * 1 of size changed
8194 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8196 LONG lStyle = infoPtr->dwStyle;
8197 UINT uView = lStyle & LVS_TYPEMASK;
8198 RECT rcList;
8199 RECT rcOld;
8201 GetClientRect(infoPtr->hwndSelf, &rcList);
8202 CopyRect(&rcOld,&(infoPtr->rcList));
8203 infoPtr->rcList.left = 0;
8204 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8205 infoPtr->rcList.top = 0;
8206 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8208 if (uView == LVS_LIST)
8210 /* Apparently the "LIST" style is supposed to have the same
8211 * number of items in a column even if there is no scroll bar.
8212 * Since if a scroll bar already exists then the bottom is already
8213 * reduced, only reduce if the scroll bar does not currently exist.
8214 * The "2" is there to mimic the native control. I think it may be
8215 * related to either padding or edges. (GLA 7/2002)
8217 if (!(lStyle & WS_HSCROLL))
8219 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8220 if (infoPtr->rcList.bottom > nHScrollHeight)
8221 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8223 else
8225 if (infoPtr->rcList.bottom > 2)
8226 infoPtr->rcList.bottom -= 2;
8229 else if (uView == LVS_REPORT)
8231 HDLAYOUT hl;
8232 WINDOWPOS wp;
8234 hl.prc = &rcList;
8235 hl.pwpos = &wp;
8236 Header_Layout(infoPtr->hwndHeader, &hl);
8238 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8240 if (!(LVS_NOCOLUMNHEADER & lStyle))
8241 infoPtr->rcList.top = max(wp.cy, 0);
8243 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8246 /***
8247 * DESCRIPTION:
8248 * Processes WM_STYLECHANGED messages.
8250 * PARAMETER(S):
8251 * [I] infoPtr : valid pointer to the listview structure
8252 * [I] WPARAM : window style type (normal or extended)
8253 * [I] LPSTYLESTRUCT : window style information
8255 * RETURN:
8256 * Zero
8258 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8259 LPSTYLESTRUCT lpss)
8261 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8262 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8263 RECT rcList = infoPtr->rcList;
8265 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8266 wStyleType, lpss->styleOld, lpss->styleNew);
8268 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8270 if (wStyleType == GWL_STYLE)
8272 infoPtr->dwStyle = lpss->styleNew;
8274 if (uOldView == LVS_REPORT)
8275 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8277 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8278 ((lpss->styleNew & WS_HSCROLL) == 0))
8279 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8281 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8282 ((lpss->styleNew & WS_VSCROLL) == 0))
8283 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8285 /* If switching modes, then start with no scroll bars and then
8286 * decide.
8288 if (uNewView != uOldView)
8289 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8291 if (uNewView == LVS_ICON)
8293 INT oldcx, oldcy;
8295 /* First readjust the iconSize and if necessary the iconSpacing */
8296 oldcx = infoPtr->iconSize.cx;
8297 oldcy = infoPtr->iconSize.cy;
8298 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8299 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8300 if (infoPtr->himlNormal != NULL)
8302 INT cx, cy;
8303 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
8304 infoPtr->iconSize.cx = cx;
8305 infoPtr->iconSize.cy = cy;
8307 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
8309 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8310 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8311 LISTVIEW_SetIconSpacing(infoPtr,0);
8314 /* Now update the full item width and height */
8315 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8316 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8317 if (lpss->styleNew & LVS_ALIGNLEFT)
8318 LISTVIEW_AlignLeft(infoPtr);
8319 else
8320 LISTVIEW_AlignTop(infoPtr);
8322 else if (uNewView == LVS_REPORT)
8324 HDLAYOUT hl;
8325 WINDOWPOS wp;
8327 hl.prc = &rcList;
8328 hl.pwpos = &wp;
8329 Header_Layout(infoPtr->hwndHeader, &hl);
8330 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8331 wp.flags);
8332 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8333 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8335 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8336 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8337 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8338 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8340 else if (uNewView == LVS_LIST)
8342 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8343 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8344 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8345 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8347 else
8349 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8350 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8351 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8352 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8353 if (lpss->styleNew & LVS_ALIGNLEFT)
8354 LISTVIEW_AlignLeft(infoPtr);
8355 else
8356 LISTVIEW_AlignTop(infoPtr);
8359 /* update the size of the client area */
8360 LISTVIEW_UpdateSize(infoPtr);
8362 /* add scrollbars if needed */
8363 LISTVIEW_UpdateScroll(infoPtr);
8365 /* invalidate client area + erase background */
8366 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8368 /* print the list of unsupported window styles */
8369 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8372 /* If they change the view and we have an active edit control
8373 we will need to kill the control since the redraw will
8374 misplace the edit control.
8376 if (infoPtr->bEditing &&
8377 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8378 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8380 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8383 return 0;
8386 /***
8387 * DESCRIPTION:
8388 * Window procedure of the listview control.
8391 static LRESULT WINAPI
8392 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8394 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8396 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8398 if (!infoPtr && (uMsg != WM_CREATE))
8399 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8401 if (infoPtr)
8403 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8406 switch (uMsg)
8408 case LVM_APPROXIMATEVIEWRECT:
8409 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8410 LOWORD(lParam), HIWORD(lParam));
8411 case LVM_ARRANGE:
8412 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8414 /* case LVN_CANCELEDITLABEL */
8416 /* case LVM_CREATEDRAGIMAGE: */
8418 case LVM_DELETEALLITEMS:
8419 return LISTVIEW_DeleteAllItems(infoPtr);
8421 case LVM_DELETECOLUMN:
8422 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8424 case LVM_DELETEITEM:
8425 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8427 case LVM_EDITLABELW:
8428 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8430 case LVM_EDITLABELA:
8431 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8433 /* case LVN_ENABLEGROUPVIEW: */
8435 case LVM_ENSUREVISIBLE:
8436 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8438 case LVM_FINDITEMW:
8439 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8441 case LVM_FINDITEMA:
8442 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8444 case LVM_GETBKCOLOR:
8445 return infoPtr->clrBk;
8447 /* case LVM_GETBKIMAGE: */
8449 case LVM_GETCALLBACKMASK:
8450 return infoPtr->uCallbackMask;
8452 case LVM_GETCOLUMNA:
8453 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8455 case LVM_GETCOLUMNW:
8456 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8458 case LVM_GETCOLUMNORDERARRAY:
8459 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8461 case LVM_GETCOLUMNWIDTH:
8462 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8464 case LVM_GETCOUNTPERPAGE:
8465 return LISTVIEW_GetCountPerPage(infoPtr);
8467 case LVM_GETEDITCONTROL:
8468 return (LRESULT)infoPtr->hwndEdit;
8470 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8471 return infoPtr->dwLvExStyle;
8473 case LVM_GETHEADER:
8474 return (LRESULT)infoPtr->hwndHeader;
8476 case LVM_GETHOTCURSOR:
8477 return infoPtr->hHotCursor;
8479 case LVM_GETHOTITEM:
8480 return infoPtr->nHotItem;
8482 case LVM_GETHOVERTIME:
8483 return infoPtr->dwHoverTime;
8485 case LVM_GETIMAGELIST:
8486 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8488 /* case LVN_GETINSERTMARK: */
8490 /* case LVN_GETINSERTMARKCOLOR: */
8492 /* case LVN_GETINSERTMARKRECT: */
8494 case LVM_GETISEARCHSTRINGA:
8495 case LVM_GETISEARCHSTRINGW:
8496 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8497 return FALSE;
8499 case LVM_GETITEMA:
8500 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8502 case LVM_GETITEMW:
8503 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8505 case LVM_GETITEMCOUNT:
8506 return infoPtr->nItemCount;
8508 case LVM_GETITEMPOSITION:
8509 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8511 case LVM_GETITEMRECT:
8512 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8514 case LVM_GETITEMSPACING:
8515 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8517 case LVM_GETITEMSTATE:
8518 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8520 case LVM_GETITEMTEXTA:
8521 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8523 case LVM_GETITEMTEXTW:
8524 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8526 case LVM_GETNEXTITEM:
8527 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8529 case LVM_GETNUMBEROFWORKAREAS:
8530 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8531 return 1;
8533 case LVM_GETORIGIN:
8534 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8536 /* case LVN_GETOUTLINECOLOR: */
8538 /* case LVM_GETSELECTEDCOLUMN: */
8540 case LVM_GETSELECTEDCOUNT:
8541 return LISTVIEW_GetSelectedCount(infoPtr);
8543 case LVM_GETSELECTIONMARK:
8544 return infoPtr->nSelectionMark;
8546 case LVM_GETSTRINGWIDTHA:
8547 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8549 case LVM_GETSTRINGWIDTHW:
8550 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8552 case LVM_GETSUBITEMRECT:
8553 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8555 case LVM_GETTEXTBKCOLOR:
8556 return infoPtr->clrTextBk;
8558 case LVM_GETTEXTCOLOR:
8559 return infoPtr->clrText;
8561 /* case LVN_GETTILEINFO: */
8563 /* case LVN_GETTILEVIEWINFO: */
8565 case LVM_GETTOOLTIPS:
8566 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8567 return FALSE;
8569 case LVM_GETTOPINDEX:
8570 return LISTVIEW_GetTopIndex(infoPtr);
8572 /*case LVM_GETUNICODEFORMAT:
8573 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8574 return FALSE;*/
8576 case LVM_GETVIEWRECT:
8577 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8579 case LVM_GETWORKAREAS:
8580 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8581 return FALSE;
8583 /* case LVN_HASGROUP: */
8585 case LVM_HITTEST:
8586 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE);
8588 case LVM_INSERTCOLUMNA:
8589 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8591 case LVM_INSERTCOLUMNW:
8592 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8594 /* case LVN_INSERTGROUP: */
8596 /* case LVN_INSERTGROUPSORTED: */
8598 case LVM_INSERTITEMA:
8599 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8601 case LVM_INSERTITEMW:
8602 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8604 /* case LVN_INSERTMARKHITTEST: */
8606 /* case LVN_ISGROUPVIEWENABLED: */
8608 /* case LVN_MAPIDTOINDEX: */
8610 /* case LVN_INEDXTOID: */
8612 /* case LVN_MOVEGROUP: */
8614 /* case LVN_MOVEITEMTOGROUP: */
8616 case LVM_REDRAWITEMS:
8617 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8619 /* case LVN_REMOVEALLGROUPS: */
8621 /* case LVN_REMOVEGROUP: */
8623 case LVM_SCROLL:
8624 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8626 case LVM_SETBKCOLOR:
8627 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8629 /* case LVM_SETBKIMAGE: */
8631 case LVM_SETCALLBACKMASK:
8632 infoPtr->uCallbackMask = (UINT)wParam;
8633 return TRUE;
8635 case LVM_SETCOLUMNA:
8636 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8638 case LVM_SETCOLUMNW:
8639 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8641 case LVM_SETCOLUMNORDERARRAY:
8642 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8644 case LVM_SETCOLUMNWIDTH:
8645 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8647 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8648 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8650 /* case LVN_SETGROUPINFO: */
8652 /* case LVN_SETGROUPMETRICS: */
8654 case LVM_SETHOTCURSOR:
8655 return LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8657 case LVM_SETHOTITEM:
8658 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8660 case LVM_SETHOVERTIME:
8661 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8663 case LVM_SETICONSPACING:
8664 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8666 case LVM_SETIMAGELIST:
8667 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8669 /* case LVN_SETINFOTIP: */
8671 /* case LVN_SETINSERTMARK: */
8673 /* case LVN_SETINSERTMARKCOLOR: */
8675 case LVM_SETITEMA:
8676 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8678 case LVM_SETITEMW:
8679 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8681 case LVM_SETITEMCOUNT:
8682 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8684 case LVM_SETITEMPOSITION:
8686 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8687 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8690 case LVM_SETITEMPOSITION32:
8691 if (lParam == 0) return FALSE;
8692 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8694 case LVM_SETITEMSTATE:
8695 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8697 case LVM_SETITEMTEXTA:
8698 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8700 case LVM_SETITEMTEXTW:
8701 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8703 /* case LVN_SETOUTLINECOLOR: */
8705 /* case LVN_SETSELECTEDCOLUMN: */
8707 case LVM_SETSELECTIONMARK:
8708 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8710 case LVM_SETTEXTBKCOLOR:
8711 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8713 case LVM_SETTEXTCOLOR:
8714 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8716 /* case LVN_SETTILEINFO: */
8718 /* case LVN_SETTILEVIEWINFO: */
8720 /* case LVN_SETTILEWIDTH: */
8722 /* case LVM_SETTOOLTIPS: */
8724 /* case LVM_SETUNICODEFORMAT: */
8726 /* case LVN_SETVIEW: */
8728 /* case LVM_SETWORKAREAS: */
8730 /* case LVN_SORTGROUPS: */
8732 case LVM_SORTITEMS:
8733 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8735 case LVM_SUBITEMHITTEST:
8736 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE);
8738 case LVM_UPDATE:
8739 return LISTVIEW_Update(infoPtr, (INT)wParam);
8741 case WM_CHAR:
8742 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8744 case WM_COMMAND:
8745 return LISTVIEW_Command(infoPtr, wParam, lParam);
8747 case WM_CREATE:
8748 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8750 case WM_ERASEBKGND:
8751 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8753 case WM_GETDLGCODE:
8754 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8756 case WM_GETFONT:
8757 return infoPtr->hFont;
8759 case WM_HSCROLL:
8760 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8762 case WM_KEYDOWN:
8763 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8765 case WM_KILLFOCUS:
8766 return LISTVIEW_KillFocus(infoPtr);
8768 case WM_LBUTTONDBLCLK:
8769 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8771 case WM_LBUTTONDOWN:
8772 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8774 case WM_LBUTTONUP:
8775 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8777 case WM_MOUSEMOVE:
8778 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8780 case WM_MOUSEHOVER:
8781 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8783 case WM_NCDESTROY:
8784 return LISTVIEW_NCDestroy(infoPtr);
8786 case WM_NOTIFY:
8787 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8789 case WM_NOTIFYFORMAT:
8790 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8792 case WM_PAINT:
8793 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8795 case WM_RBUTTONDBLCLK:
8796 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8798 case WM_RBUTTONDOWN:
8799 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8801 case WM_RBUTTONUP:
8802 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8804 case WM_SETCURSOR:
8805 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8806 return TRUE;
8807 goto fwd_msg;
8809 case WM_SETFOCUS:
8810 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8812 case WM_SETFONT:
8813 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8815 case WM_SETREDRAW:
8816 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8818 case WM_SIZE:
8819 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8821 case WM_STYLECHANGED:
8822 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8824 case WM_SYSCOLORCHANGE:
8825 COMCTL32_RefreshSysColors();
8826 return 0;
8828 /* case WM_TIMER: */
8830 case WM_VSCROLL:
8831 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8833 case WM_MOUSEWHEEL:
8834 if (wParam & (MK_SHIFT | MK_CONTROL))
8835 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8836 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8838 case WM_WINDOWPOSCHANGED:
8839 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8840 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8841 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8842 LISTVIEW_UpdateSize(infoPtr);
8843 LISTVIEW_UpdateScroll(infoPtr);
8845 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8847 /* case WM_WININICHANGE: */
8849 default:
8850 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8851 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8853 fwd_msg:
8854 /* call default window procedure */
8855 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8858 return 0;
8861 /***
8862 * DESCRIPTION:
8863 * Registers the window class.
8865 * PARAMETER(S):
8866 * None
8868 * RETURN:
8869 * None
8871 void LISTVIEW_Register(void)
8873 WNDCLASSW wndClass;
8875 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8876 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8877 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8878 wndClass.cbClsExtra = 0;
8879 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8880 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8881 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8882 wndClass.lpszClassName = WC_LISTVIEWW;
8883 RegisterClassW(&wndClass);
8886 /***
8887 * DESCRIPTION:
8888 * Unregisters the window class.
8890 * PARAMETER(S):
8891 * None
8893 * RETURN:
8894 * None
8896 void LISTVIEW_Unregister(void)
8898 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8901 /***
8902 * DESCRIPTION:
8903 * Handle any WM_COMMAND messages
8905 * PARAMETER(S):
8907 * RETURN:
8909 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8911 switch (HIWORD(wParam))
8913 case EN_UPDATE:
8916 * Adjust the edit window size
8918 WCHAR buffer[1024];
8919 HDC hdc = GetDC(infoPtr->hwndEdit);
8920 HFONT hFont, hOldFont = 0;
8921 RECT rect;
8922 SIZE sz;
8923 int len;
8925 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8926 GetWindowRect(infoPtr->hwndEdit, &rect);
8928 /* Select font to get the right dimension of the string */
8929 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8930 if(hFont != 0)
8932 hOldFont = SelectObject(hdc, hFont);
8935 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8937 TEXTMETRICW textMetric;
8939 /* Add Extra spacing for the next character */
8940 GetTextMetricsW(hdc, &textMetric);
8941 sz.cx += (textMetric.tmMaxCharWidth * 2);
8943 SetWindowPos (
8944 infoPtr->hwndEdit,
8945 HWND_TOP,
8948 sz.cx,
8949 rect.bottom - rect.top,
8950 SWP_DRAWFRAME|SWP_NOMOVE);
8952 if(hFont != 0)
8953 SelectObject(hdc, hOldFont);
8955 ReleaseDC(infoPtr->hwndSelf, hdc);
8957 break;
8960 default:
8961 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8964 return 0;
8968 /***
8969 * DESCRIPTION:
8970 * Subclassed edit control windproc function
8972 * PARAMETER(S):
8974 * RETURN:
8976 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
8977 WPARAM wParam, LPARAM lParam, BOOL isW)
8979 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8980 static BOOL bIgnoreKillFocus = FALSE;
8981 BOOL cancel = FALSE;
8983 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8984 hwnd, uMsg, wParam, lParam, isW);
8986 switch (uMsg)
8988 case WM_GETDLGCODE:
8989 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8991 case WM_KILLFOCUS:
8992 if(bIgnoreKillFocus) return TRUE;
8993 break;
8995 case WM_DESTROY:
8997 WNDPROC editProc = infoPtr->EditWndProc;
8998 infoPtr->EditWndProc = 0;
8999 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9000 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9003 case WM_KEYDOWN:
9004 if (VK_ESCAPE == (INT)wParam)
9006 cancel = TRUE;
9007 break;
9009 else if (VK_RETURN == (INT)wParam)
9010 break;
9012 default:
9013 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9016 if (infoPtr->bEditing)
9018 LPWSTR buffer = NULL;
9020 if (!cancel)
9022 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9024 if (len)
9026 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9028 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9029 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9033 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9034 /* eg. Using a messagebox */
9035 bIgnoreKillFocus = TRUE;
9036 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9038 if (buffer) COMCTL32_Free(buffer);
9040 bIgnoreKillFocus = FALSE;
9043 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9044 return TRUE;
9047 /***
9048 * DESCRIPTION:
9049 * Subclassed edit control windproc function
9051 * PARAMETER(S):
9053 * RETURN:
9055 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9057 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9060 /***
9061 * DESCRIPTION:
9062 * Subclassed edit control windproc function
9064 * PARAMETER(S):
9066 * RETURN:
9068 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9070 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9073 /***
9074 * DESCRIPTION:
9075 * Creates a subclassed edit cotrol
9077 * PARAMETER(S):
9079 * RETURN:
9081 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9082 INT x, INT y, INT width, INT height, BOOL isW)
9084 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9085 HWND hedit;
9086 SIZE sz;
9087 HDC hdc;
9088 HDC hOldFont=0;
9089 TEXTMETRICW textMetric;
9090 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9092 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9094 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9095 hdc = GetDC(infoPtr->hwndSelf);
9097 /* Select the font to get appropriate metric dimensions */
9098 if(infoPtr->hFont != 0)
9099 hOldFont = SelectObject(hdc, infoPtr->hFont);
9101 /*Get String Lenght in pixels */
9102 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9104 /*Add Extra spacing for the next character */
9105 GetTextMetricsW(hdc, &textMetric);
9106 sz.cx += (textMetric.tmMaxCharWidth * 2);
9108 if(infoPtr->hFont != 0)
9109 SelectObject(hdc, hOldFont);
9111 ReleaseDC(infoPtr->hwndSelf, hdc);
9112 if (isW)
9113 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9114 else
9115 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9117 if (!hedit) return 0;
9119 infoPtr->EditWndProc = (WNDPROC)
9120 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9121 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9123 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
9125 return hedit;